Plugin Directory

Changeset 3343286


Ignore:
Timestamp:
08/12/2025 05:37:45 AM (7 months ago)
Author:
monarchwp23
Message:

tagging version 2.0

Location:
default-post-author
Files:
4 edited
1 copied

Legend:

Unmodified
Added
Removed
  • default-post-author/tags/2.0/default-post-author.php

    r3230262 r3343286  
    11<?php
    2 
    32/**
    43 * Plugin Name: Default Post Author
    5  * Plugin URI: https://wordpress.org/plugins/default-post-author/
     4 * Plugin URI:  https://wordpress.org/plugins/default-post-author/
    65 * Description: The easiest way to set default post author in your WordPress Site.
    7  * Version: 1.1.5
     6 * Version:     2.0
    87 * Requires at least: 6.1
    9  * Requires PHP: 7.4
    10  * Author: MonarchWP
    11  * Author URI: https://www.monarchwp.com
    12  * License: GPL v2 or later
     8 * Requires PHP: 8.0
     9 * Author:      MonarchWP
     10 * Author URI:  https://www.monarchwp.com
     11 * License:     GPL v2 or later
    1312 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1413 * Text Domain: default-post-author
    15  * Domain Path: /languages
    16  */
    17 
    18 if (!defined('ABSPATH')) {
    19     die; // Exit if accessed directly
    20 }
    21 
    22 /**
    23  * Load plugin textdomain.
    24  */
    25 function dpap_load_textdomain() {
    26     load_plugin_textdomain('default-post-author', false, dirname(plugin_basename(__FILE__)) . '/languages');
    27 }
    28 add_action('plugins_loaded', 'dpap_load_textdomain');
    29 
    30 
    31 // Add the field to the general settings page
    32 function dpap_add_author_setting_field() {
    33     add_settings_field(
    34         'default_post_author', // ID of the settings field
    35         __('Default Post Author', 'default-post-author'), // Title of the field
    36         'dpap_default_post_author_field_html', // Function to display the field
    37         'general' // Page to add the field to (General Settings page)
     14 */
     15
     16if ( ! defined( 'ABSPATH' ) ) {
     17    exit; // Exit if accessed directly.
     18}
     19
     20/**
     21 * Option name constant.
     22 */
     23if ( ! defined( 'DPAP_OPTION_NAME' ) ) {
     24    define( 'DPAP_OPTION_NAME', 'default_post_author' );
     25}
     26
     27/**
     28 * Register setting for storing default author ID.
     29 */
     30function dpap_register_setting() {
     31    register_setting(
     32        'dpap_options_group',
     33        DPAP_OPTION_NAME,
     34        array(
     35            'type'              => 'integer',
     36            'sanitize_callback' => 'absint',
     37            'default'           => 1,
     38        )
    3839    );
    39 
    40     // Register the setting
    41     register_setting('general', 'default_post_author', array(
    42         'type' => 'integer',
    43         'description' => 'Default author ID for new posts',
    44         'sanitize_callback' => 'absint', // Ensure the value is an absolute integer
    45         'default' => 1 // Default value if not set
    46     ));
    47 }
    48 add_action('admin_init', 'dpap_add_author_setting_field');
    49 
    50 // HTML for the settings field
    51 function dpap_default_post_author_field_html() {
    52     $value = get_option('default_post_author', 1); // Get the current value, default to 1
    53 
    54     // Fetch users with 'Author', 'Editor', or 'Administrator' roles
    55     $user_query = new WP_User_Query(array(
    56         'role__in' => array('Author', 'Editor', 'Administrator'), // Array of roles
    57         'orderby' => 'display_name',
    58         'order' => 'ASC'
    59     ));
     40}
     41add_action( 'admin_init', 'dpap_register_setting' );
     42
     43/**
     44 * Add settings page under Settings menu.
     45 */
     46function dpap_add_admin_menu() {
     47    add_options_page(
     48        __( 'Default Post Author', 'default-post-author' ),
     49        __( 'Default Post Author', 'default-post-author' ),
     50        'manage_options',
     51        'default-post-author',
     52        'dpap_render_options_page'
     53    );
     54}
     55add_action( 'admin_menu', 'dpap_add_admin_menu' );
     56
     57/**
     58 * Render the options page HTML. The form posts to admin-post.php handlers.
     59 */
     60function dpap_render_options_page() {
     61    if ( ! current_user_can( 'manage_options' ) ) {
     62        return;
     63    }
     64
     65    $current_value = get_option( DPAP_OPTION_NAME, 1 );
     66
     67    // Fetch users with roles author, editor, administrator.
     68    $user_query = new WP_User_Query( array(
     69        'role__in' => array( 'author', 'editor', 'administrator' ),
     70        'orderby'   => 'display_name',
     71        'order'     => 'ASC',
     72    ) );
    6073
    6174    $users = $user_query->get_results();
    62 
    63     // Create a dropdown list of users
    64     echo '<select id="default_post_author" name="default_post_author">';
    65     foreach ($users as $user) {
    66         echo '<option value="' . esc_attr($user->ID) . '"' . selected($user->ID, $value, false) . '>' . esc_html($user->display_name) . '</option>';
    67     }
    68     echo '</select>';
    69 }
    70 
    71 // Set the default author for new posts
     75    ?>
     76    <div class="wrap">
     77        <h1><?php esc_html_e( 'Default Post Author Settings', 'default-post-author' ); ?></h1>
     78
     79        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
     80            <?php // Settings fields for WP Settings API (keeps option group consistent).
     81            settings_fields( 'dpap_options_group' ); ?>
     82
     83            <table class="form-table" role="presentation">
     84                <tr>
     85                    <th scope="row"><label for="default_post_author"><?php esc_html_e( 'Default Post Author', 'default-post-author' ); ?></label></th>
     86                    <td>
     87                        <select id="default_post_author" name="default_post_author">
     88                            <?php if ( empty( $users ) ) : ?>
     89                                <option value="0"><?php esc_html_e( 'No eligible users found', 'default-post-author' ); ?></option>
     90                            <?php else : ?>
     91                                <?php foreach ( $users as $user ) : ?>
     92                                    <option value="<?php echo esc_attr( $user->ID ); ?>" <?php selected( $user->ID, $current_value ); ?>>
     93                                        <?php echo esc_html( $user->display_name ); ?>
     94                                    </option>
     95                                <?php endforeach; ?>
     96                            <?php endif; ?>
     97                        </select>
     98                        <p class="description"><?php esc_html_e( 'Select the default author for new posts.', 'default-post-author' ); ?></p>
     99                    </td>
     100                </tr>
     101            </table>
     102
     103            <?php // Nonce and action for saving settings.
     104            wp_nonce_field( 'dpap_save_settings_action', 'dpap_save_settings_nonce' ); ?>
     105            <input type="hidden" name="action" value="dpap_save_settings">
     106            <?php submit_button( __( 'Save Changes', 'default-post-author' ), 'primary', 'submit' ); ?>
     107        </form>
     108
     109        <hr />
     110
     111        <h2><?php esc_html_e( 'Update Existing Posts Authors', 'default-post-author' ); ?></h2>
     112        <p><?php esc_html_e( "Click the button below to change all existing posts' authors to the selected default author. This operation uses batch updates to avoid timeouts and will respect WordPress hooks for each post update.", 'default-post-author' ); ?></p>
     113
     114        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
     115            <?php wp_nonce_field( 'dpap_update_old_posts_action', 'dpap_update_old_posts_nonce' ); ?>
     116            <input type="hidden" name="action" value="dpap_update_old_posts">
     117            <input type="hidden" name="default_post_author" value="<?php echo esc_attr( $current_value ); ?>">
     118            <?php submit_button( __( 'Update All Existing Posts to Default Author', 'default-post-author' ), 'secondary', 'dpap_update_old_authors' ); ?>
     119        </form>
     120    </div>
     121    <?php
     122}
     123
     124/**
     125 * Handle save settings via admin-post action.
     126 */
     127function dpap_handle_save_settings() {
     128    if ( ! current_user_can( 'manage_options' ) ) {
     129        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'default-post-author' ) );
     130    }
     131
     132    $nonce = isset( $_POST['dpap_save_settings_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['dpap_save_settings_nonce'] ) ) : '';
     133
     134    if ( ! wp_verify_nonce( $nonce, 'dpap_save_settings_action' ) ) {
     135        wp_die( esc_html__( 'Security check failed.', 'default-post-author' ) );
     136    }
     137
     138    if ( isset( $_POST['default_post_author'] ) ) {
     139        $new_author = absint( wp_unslash( $_POST['default_post_author'] ) );
     140        update_option( DPAP_OPTION_NAME, $new_author );
     141    }
     142
     143    wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'options-general.php?page=default-post-author' ) );
     144    exit;
     145}
     146add_action( 'admin_post_dpap_save_settings', 'dpap_handle_save_settings' );
     147
     148/**
     149 * Handle update of existing posts to the selected default author.
     150 * Processes posts in batches to avoid memory/timeouts and uses wp_update_post so hooks run.
     151 */
     152function dpap_handle_update_old_posts() {
     153    if ( ! current_user_can( 'manage_options' ) ) {
     154        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'default-post-author' ) );
     155    }
     156
     157    $nonce = isset( $_POST['dpap_update_old_posts_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['dpap_update_old_posts_nonce'] ) ) : '';
     158
     159    if ( ! wp_verify_nonce( $nonce, 'dpap_update_old_posts_action' ) ) {
     160        wp_die( esc_html__( 'Security check failed.', 'default-post-author' ) );
     161    }
     162
     163    $default_author_id = get_option( DPAP_OPTION_NAME, 1 );
     164
     165    if ( isset( $_POST['default_post_author'] ) ) {
     166        $default_author_id = absint( wp_unslash( $_POST['default_post_author'] ) );
     167        update_option( DPAP_OPTION_NAME, $default_author_id );
     168    }
     169
     170    $user = get_userdata( $default_author_id );
     171    if ( ! $user || ! array_intersect( array( 'author', 'editor', 'administrator' ), $user->roles ) ) {
     172        wp_die( esc_html__( 'Selected default author is invalid.', 'default-post-author' ) );
     173    }
     174
     175    // Batch process posts to avoid timeout/memory issues.
     176    $args = array(
     177        'post_type'      => 'post',
     178        'post_status'    => 'any',
     179        'posts_per_page' => 100,
     180        'fields'         => 'ids',
     181        'orderby'        => 'ID',
     182        'order'          => 'ASC',
     183        'paged'          => 1,
     184    );
     185
     186    $total_updated = 0;
     187
     188    while ( true ) {
     189        $query = new WP_Query( $args );
     190        if ( ! $query->have_posts() ) {
     191            break;
     192        }
     193
     194        foreach ( $query->posts as $post_id ) {
     195            // Skip auto-drafts.
     196            $post_status = get_post_status( $post_id );
     197            if ( 'auto-draft' === $post_status ) {
     198                continue;
     199            }
     200
     201            // Only update if different.
     202            $current_author = (int) get_post_field( 'post_author', $post_id );
     203            if ( $current_author === $default_author_id ) {
     204                continue;
     205            }
     206
     207            // Use wp_update_post so hooks & cache invalidation run as expected.
     208            $updated_post_id = wp_update_post( array(
     209                'ID'          => $post_id,
     210                'post_author' => $default_author_id,
     211            ), true );
     212
     213            if ( is_wp_error( $updated_post_id ) ) {
     214                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     215                    // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     216                    error_log( sprintf( 'dpap: failed to update post %d: %s', $post_id, $updated_post_id->get_error_message() ) );
     217                }
     218                continue;
     219            }
     220
     221            $total_updated++;
     222        }
     223
     224        // Move to next page.
     225        $args['paged']++;
     226
     227        // Avoid infinite loops - break if no more found.
     228        if ( 0 === count( $query->posts ) ) {
     229            break;
     230        }
     231
     232        // Clean up.
     233        wp_reset_postdata();
     234    }
     235
     236    /* translators: %d: number of posts updated */
     237    $message = sprintf( esc_html( _n( '%d post author updated.', '%d post authors updated.', $total_updated, 'default-post-author' ) ), $total_updated );
     238
     239    // Store message as transient to display after redirect.
     240    set_transient( 'dpap_update_result', $message, 30 );
     241
     242    wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'options-general.php?page=default-post-author' ) );
     243    exit;
     244}
     245add_action( 'admin_post_dpap_update_old_posts', 'dpap_handle_update_old_posts' );
     246
     247/**
     248 * Show admin notices for the batch update result (transient-based).
     249 */
     250function dpap_admin_notices() {
     251    if ( ! current_user_can( 'manage_options' ) ) {
     252        return;
     253    }
     254
     255    $message = get_transient( 'dpap_update_result' );
     256    if ( $message ) {
     257        echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $message ) . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     258        delete_transient( 'dpap_update_result' );
     259    }
     260}
     261add_action( 'admin_notices', 'dpap_admin_notices' );
     262
     263/**
     264 * Set default author for new posts during creation only.
     265 * Uses wp_insert_post_data filter.
     266 *
     267 * @param array $data    Array of slashed post data.
     268 * @param array $postarr Original post data.
     269 * @return array Modified post data.
     270 */
    72271function dpap_force_post_author( $data, $postarr ) {
    73     // Retrieve the default author ID from WordPress options, default to 1 if not set
    74     $default_author_id = get_option('default_post_author', 1);
    75 
    76     // Set the author for new posts
    77     if (empty($postarr['ID'])) {
     272    $default_author_id = (int) get_option( DPAP_OPTION_NAME, 1 );
     273
     274    if ( empty( $postarr['ID'] ) ) {
    78275        $data['post_author'] = $default_author_id;
    79276    }
     277
    80278    return $data;
    81279}
    82280add_filter( 'wp_insert_post_data', 'dpap_force_post_author', 10, 2 );
    83281
    84 // Set the revision author to the same author as the post
    85 function dpap_set_revision_author($post_has_changed, $last_revision, $post) {
    86     global $wpdb;
    87 
    88     // Update the post_author of the revision to match the original post
    89     $result = $wpdb->update(
    90         $wpdb->posts,
    91         array('post_author' => $post->post_author),
    92         array('ID' => $last_revision->ID),
    93         array('%d'),
    94         array('%d')
    95     );
    96 
    97     // Basic error handling
    98     if (false === $result) {
    99         error_log('Failed to update revision author for post ID ' . $post->ID);
    100     }
    101 
    102     return $post_has_changed;
    103 }
    104 add_filter('wp_save_post_revision_check_for_changes', 'dpap_set_revision_author', 10, 3);
     282/**
     283 * Ensure revision author matches original post author.
     284 * Hook into wp_save_post_revision which provides revision ID and original post.
     285 * Use wp_update_post to avoid direct DB queries.
     286 *
     287 * @param int     $revision_id Revision post ID.
     288 * @param WP_Post $post       The original post object.
     289 */
     290function dpap_set_revision_author( $revision_id, $post ) {
     291    $revision_id = (int) $revision_id;
     292    $post_author = (int) $post->post_author;
     293
     294    $result = wp_update_post( array(
     295        'ID'          => $revision_id,
     296        'post_author' => $post_author,
     297    ), true );
     298
     299    if ( is_wp_error( $result ) ) {
     300        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     301            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     302            error_log( 'dpap: Failed to update revision author for post ID ' . $post->ID . ': ' . $result->get_error_message() );
     303        }
     304    }
     305}
     306add_action( 'wp_save_post_revision', 'dpap_set_revision_author', 10, 2 );
     307
     308/**
     309 * Uninstall cleanup: remove option when plugin is uninstalled.
     310 */
     311function dpap_uninstall() {
     312    delete_option( DPAP_OPTION_NAME );
     313}
     314register_uninstall_hook( __FILE__, 'dpap_uninstall' );
     315
     316/* End of file */
     317
  • default-post-author/tags/2.0/readme.txt

    r3230273 r3343286  
    33Tags: post author, default post author, wp post, posts
    44Requires at least: 6.1
    5 Tested up to: 6.7
    6 Requires PHP: 7.4
    7 Stable tag: 1.1.5
     5Tested up to: 6.8
     6Requires PHP: 8.0
     7Stable tag: 2.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    7373== Upgrade Notice ==
    7474
     75= 2.0 - (August 12, 2025) =
     76- Security Update
     77- Performance Update
     78- Ability to Update All Existing Posts to Default Author
     79
    7580= 1.1.5 - (January 28, 2025) =
    7681- Security Update
     
    110115== Changelog ==
    111116
     117= 2.0 - (August 28, 2025) =
     118- Security Update
     119- Performance Update
     120- Ability to Update All Existing Posts to Default Author
     121
    112122= 1.1.5 - (January 28, 2025) =
    113123- Security Update
  • default-post-author/trunk/default-post-author.php

    r3230262 r3343286  
    11<?php
    2 
    32/**
    43 * Plugin Name: Default Post Author
    5  * Plugin URI: https://wordpress.org/plugins/default-post-author/
     4 * Plugin URI:  https://wordpress.org/plugins/default-post-author/
    65 * Description: The easiest way to set default post author in your WordPress Site.
    7  * Version: 1.1.5
     6 * Version:     2.0
    87 * Requires at least: 6.1
    9  * Requires PHP: 7.4
    10  * Author: MonarchWP
    11  * Author URI: https://www.monarchwp.com
    12  * License: GPL v2 or later
     8 * Requires PHP: 8.0
     9 * Author:      MonarchWP
     10 * Author URI:  https://www.monarchwp.com
     11 * License:     GPL v2 or later
    1312 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1413 * Text Domain: default-post-author
    15  * Domain Path: /languages
    16  */
    17 
    18 if (!defined('ABSPATH')) {
    19     die; // Exit if accessed directly
    20 }
    21 
    22 /**
    23  * Load plugin textdomain.
    24  */
    25 function dpap_load_textdomain() {
    26     load_plugin_textdomain('default-post-author', false, dirname(plugin_basename(__FILE__)) . '/languages');
    27 }
    28 add_action('plugins_loaded', 'dpap_load_textdomain');
    29 
    30 
    31 // Add the field to the general settings page
    32 function dpap_add_author_setting_field() {
    33     add_settings_field(
    34         'default_post_author', // ID of the settings field
    35         __('Default Post Author', 'default-post-author'), // Title of the field
    36         'dpap_default_post_author_field_html', // Function to display the field
    37         'general' // Page to add the field to (General Settings page)
     14 */
     15
     16if ( ! defined( 'ABSPATH' ) ) {
     17    exit; // Exit if accessed directly.
     18}
     19
     20/**
     21 * Option name constant.
     22 */
     23if ( ! defined( 'DPAP_OPTION_NAME' ) ) {
     24    define( 'DPAP_OPTION_NAME', 'default_post_author' );
     25}
     26
     27/**
     28 * Register setting for storing default author ID.
     29 */
     30function dpap_register_setting() {
     31    register_setting(
     32        'dpap_options_group',
     33        DPAP_OPTION_NAME,
     34        array(
     35            'type'              => 'integer',
     36            'sanitize_callback' => 'absint',
     37            'default'           => 1,
     38        )
    3839    );
    39 
    40     // Register the setting
    41     register_setting('general', 'default_post_author', array(
    42         'type' => 'integer',
    43         'description' => 'Default author ID for new posts',
    44         'sanitize_callback' => 'absint', // Ensure the value is an absolute integer
    45         'default' => 1 // Default value if not set
    46     ));
    47 }
    48 add_action('admin_init', 'dpap_add_author_setting_field');
    49 
    50 // HTML for the settings field
    51 function dpap_default_post_author_field_html() {
    52     $value = get_option('default_post_author', 1); // Get the current value, default to 1
    53 
    54     // Fetch users with 'Author', 'Editor', or 'Administrator' roles
    55     $user_query = new WP_User_Query(array(
    56         'role__in' => array('Author', 'Editor', 'Administrator'), // Array of roles
    57         'orderby' => 'display_name',
    58         'order' => 'ASC'
    59     ));
     40}
     41add_action( 'admin_init', 'dpap_register_setting' );
     42
     43/**
     44 * Add settings page under Settings menu.
     45 */
     46function dpap_add_admin_menu() {
     47    add_options_page(
     48        __( 'Default Post Author', 'default-post-author' ),
     49        __( 'Default Post Author', 'default-post-author' ),
     50        'manage_options',
     51        'default-post-author',
     52        'dpap_render_options_page'
     53    );
     54}
     55add_action( 'admin_menu', 'dpap_add_admin_menu' );
     56
     57/**
     58 * Render the options page HTML. The form posts to admin-post.php handlers.
     59 */
     60function dpap_render_options_page() {
     61    if ( ! current_user_can( 'manage_options' ) ) {
     62        return;
     63    }
     64
     65    $current_value = get_option( DPAP_OPTION_NAME, 1 );
     66
     67    // Fetch users with roles author, editor, administrator.
     68    $user_query = new WP_User_Query( array(
     69        'role__in' => array( 'author', 'editor', 'administrator' ),
     70        'orderby'   => 'display_name',
     71        'order'     => 'ASC',
     72    ) );
    6073
    6174    $users = $user_query->get_results();
    62 
    63     // Create a dropdown list of users
    64     echo '<select id="default_post_author" name="default_post_author">';
    65     foreach ($users as $user) {
    66         echo '<option value="' . esc_attr($user->ID) . '"' . selected($user->ID, $value, false) . '>' . esc_html($user->display_name) . '</option>';
    67     }
    68     echo '</select>';
    69 }
    70 
    71 // Set the default author for new posts
     75    ?>
     76    <div class="wrap">
     77        <h1><?php esc_html_e( 'Default Post Author Settings', 'default-post-author' ); ?></h1>
     78
     79        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
     80            <?php // Settings fields for WP Settings API (keeps option group consistent).
     81            settings_fields( 'dpap_options_group' ); ?>
     82
     83            <table class="form-table" role="presentation">
     84                <tr>
     85                    <th scope="row"><label for="default_post_author"><?php esc_html_e( 'Default Post Author', 'default-post-author' ); ?></label></th>
     86                    <td>
     87                        <select id="default_post_author" name="default_post_author">
     88                            <?php if ( empty( $users ) ) : ?>
     89                                <option value="0"><?php esc_html_e( 'No eligible users found', 'default-post-author' ); ?></option>
     90                            <?php else : ?>
     91                                <?php foreach ( $users as $user ) : ?>
     92                                    <option value="<?php echo esc_attr( $user->ID ); ?>" <?php selected( $user->ID, $current_value ); ?>>
     93                                        <?php echo esc_html( $user->display_name ); ?>
     94                                    </option>
     95                                <?php endforeach; ?>
     96                            <?php endif; ?>
     97                        </select>
     98                        <p class="description"><?php esc_html_e( 'Select the default author for new posts.', 'default-post-author' ); ?></p>
     99                    </td>
     100                </tr>
     101            </table>
     102
     103            <?php // Nonce and action for saving settings.
     104            wp_nonce_field( 'dpap_save_settings_action', 'dpap_save_settings_nonce' ); ?>
     105            <input type="hidden" name="action" value="dpap_save_settings">
     106            <?php submit_button( __( 'Save Changes', 'default-post-author' ), 'primary', 'submit' ); ?>
     107        </form>
     108
     109        <hr />
     110
     111        <h2><?php esc_html_e( 'Update Existing Posts Authors', 'default-post-author' ); ?></h2>
     112        <p><?php esc_html_e( "Click the button below to change all existing posts' authors to the selected default author. This operation uses batch updates to avoid timeouts and will respect WordPress hooks for each post update.", 'default-post-author' ); ?></p>
     113
     114        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
     115            <?php wp_nonce_field( 'dpap_update_old_posts_action', 'dpap_update_old_posts_nonce' ); ?>
     116            <input type="hidden" name="action" value="dpap_update_old_posts">
     117            <input type="hidden" name="default_post_author" value="<?php echo esc_attr( $current_value ); ?>">
     118            <?php submit_button( __( 'Update All Existing Posts to Default Author', 'default-post-author' ), 'secondary', 'dpap_update_old_authors' ); ?>
     119        </form>
     120    </div>
     121    <?php
     122}
     123
     124/**
     125 * Handle save settings via admin-post action.
     126 */
     127function dpap_handle_save_settings() {
     128    if ( ! current_user_can( 'manage_options' ) ) {
     129        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'default-post-author' ) );
     130    }
     131
     132    $nonce = isset( $_POST['dpap_save_settings_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['dpap_save_settings_nonce'] ) ) : '';
     133
     134    if ( ! wp_verify_nonce( $nonce, 'dpap_save_settings_action' ) ) {
     135        wp_die( esc_html__( 'Security check failed.', 'default-post-author' ) );
     136    }
     137
     138    if ( isset( $_POST['default_post_author'] ) ) {
     139        $new_author = absint( wp_unslash( $_POST['default_post_author'] ) );
     140        update_option( DPAP_OPTION_NAME, $new_author );
     141    }
     142
     143    wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'options-general.php?page=default-post-author' ) );
     144    exit;
     145}
     146add_action( 'admin_post_dpap_save_settings', 'dpap_handle_save_settings' );
     147
     148/**
     149 * Handle update of existing posts to the selected default author.
     150 * Processes posts in batches to avoid memory/timeouts and uses wp_update_post so hooks run.
     151 */
     152function dpap_handle_update_old_posts() {
     153    if ( ! current_user_can( 'manage_options' ) ) {
     154        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'default-post-author' ) );
     155    }
     156
     157    $nonce = isset( $_POST['dpap_update_old_posts_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['dpap_update_old_posts_nonce'] ) ) : '';
     158
     159    if ( ! wp_verify_nonce( $nonce, 'dpap_update_old_posts_action' ) ) {
     160        wp_die( esc_html__( 'Security check failed.', 'default-post-author' ) );
     161    }
     162
     163    $default_author_id = get_option( DPAP_OPTION_NAME, 1 );
     164
     165    if ( isset( $_POST['default_post_author'] ) ) {
     166        $default_author_id = absint( wp_unslash( $_POST['default_post_author'] ) );
     167        update_option( DPAP_OPTION_NAME, $default_author_id );
     168    }
     169
     170    $user = get_userdata( $default_author_id );
     171    if ( ! $user || ! array_intersect( array( 'author', 'editor', 'administrator' ), $user->roles ) ) {
     172        wp_die( esc_html__( 'Selected default author is invalid.', 'default-post-author' ) );
     173    }
     174
     175    // Batch process posts to avoid timeout/memory issues.
     176    $args = array(
     177        'post_type'      => 'post',
     178        'post_status'    => 'any',
     179        'posts_per_page' => 100,
     180        'fields'         => 'ids',
     181        'orderby'        => 'ID',
     182        'order'          => 'ASC',
     183        'paged'          => 1,
     184    );
     185
     186    $total_updated = 0;
     187
     188    while ( true ) {
     189        $query = new WP_Query( $args );
     190        if ( ! $query->have_posts() ) {
     191            break;
     192        }
     193
     194        foreach ( $query->posts as $post_id ) {
     195            // Skip auto-drafts.
     196            $post_status = get_post_status( $post_id );
     197            if ( 'auto-draft' === $post_status ) {
     198                continue;
     199            }
     200
     201            // Only update if different.
     202            $current_author = (int) get_post_field( 'post_author', $post_id );
     203            if ( $current_author === $default_author_id ) {
     204                continue;
     205            }
     206
     207            // Use wp_update_post so hooks & cache invalidation run as expected.
     208            $updated_post_id = wp_update_post( array(
     209                'ID'          => $post_id,
     210                'post_author' => $default_author_id,
     211            ), true );
     212
     213            if ( is_wp_error( $updated_post_id ) ) {
     214                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     215                    // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     216                    error_log( sprintf( 'dpap: failed to update post %d: %s', $post_id, $updated_post_id->get_error_message() ) );
     217                }
     218                continue;
     219            }
     220
     221            $total_updated++;
     222        }
     223
     224        // Move to next page.
     225        $args['paged']++;
     226
     227        // Avoid infinite loops - break if no more found.
     228        if ( 0 === count( $query->posts ) ) {
     229            break;
     230        }
     231
     232        // Clean up.
     233        wp_reset_postdata();
     234    }
     235
     236    /* translators: %d: number of posts updated */
     237    $message = sprintf( esc_html( _n( '%d post author updated.', '%d post authors updated.', $total_updated, 'default-post-author' ) ), $total_updated );
     238
     239    // Store message as transient to display after redirect.
     240    set_transient( 'dpap_update_result', $message, 30 );
     241
     242    wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'options-general.php?page=default-post-author' ) );
     243    exit;
     244}
     245add_action( 'admin_post_dpap_update_old_posts', 'dpap_handle_update_old_posts' );
     246
     247/**
     248 * Show admin notices for the batch update result (transient-based).
     249 */
     250function dpap_admin_notices() {
     251    if ( ! current_user_can( 'manage_options' ) ) {
     252        return;
     253    }
     254
     255    $message = get_transient( 'dpap_update_result' );
     256    if ( $message ) {
     257        echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $message ) . '</p></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     258        delete_transient( 'dpap_update_result' );
     259    }
     260}
     261add_action( 'admin_notices', 'dpap_admin_notices' );
     262
     263/**
     264 * Set default author for new posts during creation only.
     265 * Uses wp_insert_post_data filter.
     266 *
     267 * @param array $data    Array of slashed post data.
     268 * @param array $postarr Original post data.
     269 * @return array Modified post data.
     270 */
    72271function dpap_force_post_author( $data, $postarr ) {
    73     // Retrieve the default author ID from WordPress options, default to 1 if not set
    74     $default_author_id = get_option('default_post_author', 1);
    75 
    76     // Set the author for new posts
    77     if (empty($postarr['ID'])) {
     272    $default_author_id = (int) get_option( DPAP_OPTION_NAME, 1 );
     273
     274    if ( empty( $postarr['ID'] ) ) {
    78275        $data['post_author'] = $default_author_id;
    79276    }
     277
    80278    return $data;
    81279}
    82280add_filter( 'wp_insert_post_data', 'dpap_force_post_author', 10, 2 );
    83281
    84 // Set the revision author to the same author as the post
    85 function dpap_set_revision_author($post_has_changed, $last_revision, $post) {
    86     global $wpdb;
    87 
    88     // Update the post_author of the revision to match the original post
    89     $result = $wpdb->update(
    90         $wpdb->posts,
    91         array('post_author' => $post->post_author),
    92         array('ID' => $last_revision->ID),
    93         array('%d'),
    94         array('%d')
    95     );
    96 
    97     // Basic error handling
    98     if (false === $result) {
    99         error_log('Failed to update revision author for post ID ' . $post->ID);
    100     }
    101 
    102     return $post_has_changed;
    103 }
    104 add_filter('wp_save_post_revision_check_for_changes', 'dpap_set_revision_author', 10, 3);
     282/**
     283 * Ensure revision author matches original post author.
     284 * Hook into wp_save_post_revision which provides revision ID and original post.
     285 * Use wp_update_post to avoid direct DB queries.
     286 *
     287 * @param int     $revision_id Revision post ID.
     288 * @param WP_Post $post       The original post object.
     289 */
     290function dpap_set_revision_author( $revision_id, $post ) {
     291    $revision_id = (int) $revision_id;
     292    $post_author = (int) $post->post_author;
     293
     294    $result = wp_update_post( array(
     295        'ID'          => $revision_id,
     296        'post_author' => $post_author,
     297    ), true );
     298
     299    if ( is_wp_error( $result ) ) {
     300        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     301            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     302            error_log( 'dpap: Failed to update revision author for post ID ' . $post->ID . ': ' . $result->get_error_message() );
     303        }
     304    }
     305}
     306add_action( 'wp_save_post_revision', 'dpap_set_revision_author', 10, 2 );
     307
     308/**
     309 * Uninstall cleanup: remove option when plugin is uninstalled.
     310 */
     311function dpap_uninstall() {
     312    delete_option( DPAP_OPTION_NAME );
     313}
     314register_uninstall_hook( __FILE__, 'dpap_uninstall' );
     315
     316/* End of file */
     317
  • default-post-author/trunk/readme.txt

    r3230273 r3343286  
    33Tags: post author, default post author, wp post, posts
    44Requires at least: 6.1
    5 Tested up to: 6.7
    6 Requires PHP: 7.4
    7 Stable tag: 1.1.5
     5Tested up to: 6.8
     6Requires PHP: 8.0
     7Stable tag: 2.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    7373== Upgrade Notice ==
    7474
     75= 2.0 - (August 12, 2025) =
     76- Security Update
     77- Performance Update
     78- Ability to Update All Existing Posts to Default Author
     79
    7580= 1.1.5 - (January 28, 2025) =
    7681- Security Update
     
    110115== Changelog ==
    111116
     117= 2.0 - (August 28, 2025) =
     118- Security Update
     119- Performance Update
     120- Ability to Update All Existing Posts to Default Author
     121
    112122= 1.1.5 - (January 28, 2025) =
    113123- Security Update
Note: See TracChangeset for help on using the changeset viewer.