From 8da39928b58d9b9365e81932f1980a3ab107b600 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 03:14:30 +0000 Subject: [PATCH 01/23] Bump ip in /tests/codeception/_data/themes/custom_twentytwenty Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9. - [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9) --- updated-dependencies: - dependency-name: ip dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../themes/custom_twentytwenty/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/codeception/_data/themes/custom_twentytwenty/package-lock.json b/tests/codeception/_data/themes/custom_twentytwenty/package-lock.json index b8079534..0f083e22 100644 --- a/tests/codeception/_data/themes/custom_twentytwenty/package-lock.json +++ b/tests/codeception/_data/themes/custom_twentytwenty/package-lock.json @@ -8531,9 +8531,9 @@ "dev": true }, "ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", + "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", "dev": true }, "ipaddr.js": { @@ -13676,9 +13676,9 @@ }, "dependencies": { "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", "dev": true } } From 79b151462c15c0e62e42aecaa3e84f09fa9173e6 Mon Sep 17 00:00:00 2001 From: Ojo Paul Date: Wed, 28 Feb 2024 10:14:56 +0100 Subject: [PATCH 02/23] - Add User Account option for Guest Authors #1287 --- src/assets/css/multiple-authors.css | 43 +++++ src/assets/js/multiple-authors.js | 30 ++++ src/core/Classes/Author_Editor.php | 151 ++++++++++++++---- .../multiple-authors/multiple-authors.php | 44 +++++ 4 files changed, 241 insertions(+), 27 deletions(-) diff --git a/src/assets/css/multiple-authors.css b/src/assets/css/multiple-authors.css index 14416165..2198b8c3 100644 --- a/src/assets/css/multiple-authors.css +++ b/src/assets/css/multiple-authors.css @@ -442,6 +442,49 @@ form#edittag tr.form-field #slug { margin-left: 30px; } +.form-wrap label.ppma-account-type { + margin: 0 0 2px; +} + +.ppma-button-group { + padding-left: 1px; + display: inline-flex; + flex-direction: row; + flex-wrap: nowrap; + width: 100%; +} + +.ppma-button-group label { + display: inline-block; + border: #7e8993 solid 1px; + position: relative; + z-index: 1; + padding: 5px 10px; + background: #fff; +} + +.ppma-button-group label { + margin: 0 0 0 -1px; + flex: 1; + text-align: center; + white-space: nowrap; +} + +.ppma-button-group label.selected { + border-color: #007cba; + background: #008dd4; + color: #fff; + z-index: 2; +} + +.ppma-button-group label:first-child { + border-radius: 3px 0 0 3px; +} + +.ppma-button-group input { + display: none !important; +} + body.edit-php.post-type-ppmacf_field .row-actions .inline, body.edit-php.post-type-ppma_boxes .row-actions .inline, body.edit-php.post-type-ppmacf_field .tablenav.top .alignleft.actions:not(.bulkactions), diff --git a/src/assets/js/multiple-authors.js b/src/assets/js/multiple-authors.js index a40aa58b..b5b343d4 100644 --- a/src/assets/js/multiple-authors.js +++ b/src/assets/js/multiple-authors.js @@ -758,6 +758,36 @@ jQuery(document).ready(function ($) { } + /** + * Author button group click + */ + $(document).on("click", ".ppma-button-group label", function () { + var current_button = $(this); + var target_value = current_button.find('input').val(); + var button_group = current_button.closest('.ppma-button-group'); + + //remove active class + button_group.find('label.selected').removeClass('selected'); + //hide descriptions + button_group.closest('.ppma-group-wrap').find('.ppma-button-description').hide(); + //add active class to current select + current_button.addClass('selected'); + //show selected tab descriptions + button_group.closest('.ppma-group-wrap').find('.ppma-button-description.' + target_value).show(); + + // hide/show fields + if (target_value !== 'existing_user') { + $('.form-field.term-user_id-wrap').hide(); + } else { + $('.form-field.term-user_id-wrap').show(); + } + if (target_value !== 'new_user') { + $('.form-field.term-author_email-wrap').hide(); + } else { + $('.form-field.term-author_email-wrap').show(); + } + }); + /** * Author editor tab switch */ diff --git a/src/core/Classes/Author_Editor.php b/src/core/Classes/Author_Editor.php index a3a0527e..4b3073e7 100644 --- a/src/core/Classes/Author_Editor.php +++ b/src/core/Classes/Author_Editor.php @@ -527,6 +527,26 @@ class="authors-select2-user-select" name="" style=" selected="selected">display_name); ?> + +
+
+ $group_option) : + $group_option_style = $group === $selected_button ? '' : 'display: none;'; + $group_description .= '

'. $group_option['description'] .'

'; + ?> + + +
+ +
@@ -534,7 +554,7 @@ class="authors-select2-user-select" name="" style=" id="" value=""/> - +

@@ -654,27 +674,66 @@ public static function action_user_register($user_id) public static function action_new_form_tag() { + // Close the form tag. + echo '>'; + $legacyPlugin = Factory::getLegacyPlugin(); $enable_guest_author_user = $legacyPlugin->modules->multiple_authors->options->enable_guest_author_user === 'yes'; - // Close the form tag. - echo '>'; + $author_type_options = []; + $author_type_options['existing_user'] = [ + 'label' => esc_html__('Existing User', 'publishpress-authors'), + 'description' => esc_html__('Create an author profile for an existing user.', 'publishpress-authors'), + ]; + $author_type_options['new_user'] = [ + 'label' => esc_html__('New User', 'publishpress-authors'), + 'description' => '' . esc_html__('Guest Author With a User Account:', 'publishpress-authors') . ' ' . esc_html__('Create an author profile and a linked user account. This account will not be able to login to the WordPress dashboard. This option is best for compatibility with other themes and plugins.', 'publishpress-authors'), + ]; + if ($enable_guest_author_user) { + $author_type_options['guest_author'] = [ + 'label' => esc_html__('Guest Author', 'publishpress-authors'), + 'description' => '' . esc_html__('Guest Author With No User Account:', 'publishpress-authors') . ' ' . esc_html__('Create an author profile with no linked user account.', 'publishpress-authors'), + ]; + } ?> -
- - 'ajax_user_select', - 'value' => '', - 'key' => 'new', - 'description' => ($enable_guest_author_user) ? esc_html__( - 'You don\'t have to choose a Mapped User. Leave this choice blank and you can create a Guest Author with no WordPress account.', - 'publishpress-authors' - ) : '', - ] - ); +
+
+ + 'button_group', + 'value' => '', + 'key' => 'author_type', + 'options' => $author_type_options, + ] + ); + echo '
'; + ?> + '; + ?> +
+ + 'ajax_user_select', + 'value' => '', + 'key' => 'new', + ] + ); + echo '
'; // It is missing the end of the tag by purpose, because there is a hardcoded > after the action is called. echo ' $slug, + 'display_name' => sanitize_text_field($_POST['tag-name']), + 'user_email' => sanitize_text_field($_POST['authors-author_email']), + 'user_pass' => wp_generate_password(), + 'role' => 'ppma_guest_author', + ); + $user_id = wp_insert_user($user_data); + + if (is_wp_error($user_id)) { + return new WP_Error( + 'publishpress_authors_new_user_error', + $user_id->get_error_message() + ); + } else { + $author_id = $user_id; + $_POST['authors-new'] = $author_id; + } + } + } + + if (!$enable_guest_author_user && $author_id === 0) { + return new WP_Error( + 'publishpress_authors_mapped_user_required', + esc_html__( + 'Mapped user is required.', + 'publishpress-authors' + ) + ); + } } return $term; diff --git a/src/modules/multiple-authors/multiple-authors.php b/src/modules/multiple-authors/multiple-authors.php index d39c6f99..309caabf 100644 --- a/src/modules/multiple-authors/multiple-authors.php +++ b/src/modules/multiple-authors/multiple-authors.php @@ -299,6 +299,15 @@ public function init() //add authors page post limit add_filter('pre_get_posts', [$this, 'authors_taxonomy_post_limit']); + + //prevent guest author login + add_filter('wp_authenticate_user', [$this, 'prevent_guest_author_login'], 1); + + //prevent outgoing email for guest author + add_filter('send_password_change_email', [$this, 'prevent_guest_author_emails'], 10, 2); + add_filter('wp_send_new_user_notification_to_admin', [$this, 'prevent_guest_author_emails'], 10, 2); + add_filter('wp_send_new_user_notification_to_user', [$this, 'prevent_guest_author_emails'], 10, 2); + add_filter('send_email_change_email', [$this, 'prevent_guest_author_emails'], 10, 2); } /** @@ -4667,5 +4676,40 @@ public function authors_template_redirect($taxonomy_template) { } } } + + /** + * Prevent guest author login + * + * @param $user (null|WP_User|WP_Error) WP_User if the user is authenticated. WP_Error or null otherwise. + * + * @return WP_User object if credentials authenticate the user. WP_Error or null otherwise + */ + public function prevent_guest_author_login($user) { + + if (is_wp_error($user)) { + return $user; + } + + if (isset($user->roles) && is_array($user->roles) && in_array('ppma_guest_author', $user->roles)) { + return new WP_Error('ppma_guest_author_login_denied', __('Guest Author cannot login on the site.', 'publishpress-authors')); + } + + return $user; + } + + /** + * Prevent outgoing email for guest author + * + * @param bool $notify + * @param object $user + * + * @return array $args + */ + public function prevent_guest_author_emails($notify, $user) { + if ($user && is_object($user) && isset($user->roles) && in_array('ppma_guest_author', (array) $user->roles)) { + return false; + } + return $notify; + } } } From 3fc1e67b861c9bd25ec16bbb43cc04fad893f476 Mon Sep 17 00:00:00 2001 From: Ojo Paul Date: Thu, 29 Feb 2024 09:59:14 +0100 Subject: [PATCH 03/23] Add Schema option for Author Categories #1642 --- publishpress-authors.php | 2 +- src/functions/template-tags.php | 51 +++++++++-- .../assets/js/author-categories.js | 1 + .../assets/js/inline-edit.js | 2 + .../author-categories/author-categories.php | 84 ++++++++++++++++++- .../classes/AuthorCategoriesSchema.php | 39 +++++++++ .../classes/AuthorCategoriesTable.php | 17 +++- .../rank-math-seo-integration.php | 26 ++++++ .../src/SchemaFacade.php | 24 ++++++ 9 files changed, 234 insertions(+), 12 deletions(-) diff --git a/publishpress-authors.php b/publishpress-authors.php index 8dbaa09a..9b20b74e 100644 --- a/publishpress-authors.php +++ b/publishpress-authors.php @@ -5,7 +5,7 @@ * Description: PublishPress Authors allows you to add multiple authors and guest authors to WordPress posts * Author: PublishPress * Author URI: https://publishpress.com - * Version: 4.3.2 + * Version: 4.3.2.1 * Text Domain: publishpress-authors * Domain Path: /languages * Requires at least: 5.5 diff --git a/src/functions/template-tags.php b/src/functions/template-tags.php index 7634467f..e904bedd 100644 --- a/src/functions/template-tags.php +++ b/src/functions/template-tags.php @@ -1348,11 +1348,14 @@ function get_ppma_author_categories($args = []) { 'orderby' => 'category_order', 'order' => 'ASC', 'count_only' => false, + 'meta_query' => [] ]; $args = wp_parse_args($args, $default_args); - $table_name = $wpdb->prefix . 'ppma_author_categories'; + $table_name = $wpdb->prefix . 'ppma_author_categories'; + $meta_table_name = $wpdb->prefix . 'ppma_author_categories_meta'; + $paged = intval($args['paged']); $limit = intval($args['limit']); @@ -1384,21 +1387,34 @@ function get_ppma_author_categories($args = []) { $cache_key = 'author_categories_results_' . md5(serialize($args)); $category_results = wp_cache_get($cache_key, 'author_categories_results_cache'); - + $single_result = false; if ($category_results === false) { $category_results = []; if ($field_search) { $query = $wpdb->prepare( - "SELECT * FROM {$table_name} WHERE {$field_search} = %s ORDER BY {$orderby} {$order} LIMIT 1", + "SELECT {$table_name}.*, {$meta_table_name}.meta_key, {$meta_table_name}.meta_value + FROM {$table_name} + LEFT JOIN {$meta_table_name} ON {$table_name}.id = {$meta_table_name}.category_id + WHERE {$table_name}.{$field_search} = %s + ORDER BY {$orderby} {$order} + LIMIT 1", $field_value ); $category_results = $wpdb->get_row($query, \ARRAY_A); + $single_result = true; } else { $offset = ($paged - 1) * $limit; - $query = "SELECT * FROM {$table_name} WHERE 1=1"; - + if ($count_only) { + $query = "SELECT * FROM {$table_name} WHERE 1=1"; + } else { + $query = "SELECT {$table_name}.*, {$meta_table_name}.meta_key, {$meta_table_name}.meta_value + FROM {$table_name} + LEFT JOIN {$meta_table_name} ON {$table_name}.id = {$meta_table_name}.category_id + WHERE 1=1"; + } + if (!empty($search)) { $query .= $wpdb->prepare( " AND (slug LIKE '%%%s%%' OR category_name LIKE '%%%s%%' OR plural_name LIKE '%%%s%%')", @@ -1414,23 +1430,44 @@ function get_ppma_author_categories($args = []) { $category_status ); } - + if ($count_only) { $query = str_replace("SELECT *", "SELECT COUNT(*)", $query); return $wpdb->get_var($query); } - + $query .= $wpdb->prepare( " ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d", $limit, $offset ); + $category_results = $wpdb->get_results($query, \ARRAY_A); wp_cache_set($cache_key, $category_results, 'author_categories_results_cache', 3600); } } + if (is_array($category_results)) { + $merged_results = $single_result ? [$category_results] : $category_results; + $category_results = array_reduce($merged_results, function ($accumulator, $item) { + $id = $item['id']; + + if (!isset($accumulator[$id])) { + $accumulator[$id] = $item; + } + + if (isset($item['meta_key']) && isset($item['meta_value'])) { + $accumulator[$id][$item['meta_key']] = $item['meta_value']; + } + + unset($accumulator[$id]['meta_key'], $accumulator[$id]['meta_value']); + + return $accumulator; + }, []); + $category_results = $single_result ? array_values($category_results)[0] : array_values($category_results); + } + return $category_results; } diff --git a/src/modules/author-categories/assets/js/author-categories.js b/src/modules/author-categories/assets/js/author-categories.js index f653b75e..14ed4b05 100644 --- a/src/modules/author-categories/assets/js/author-categories.js +++ b/src/modules/author-categories/assets/js/author-categories.js @@ -19,6 +19,7 @@ action: "save_ppma_author_category", category_name: form.find('#category-name').val(), plural_name: form.find('#category-plural-name').val(), + schema_property: form.find('#category-schema-property').val(), enabled_category: form.find('#category-enabled-category').is(':checked') ? 1 : 0, nonce: authorCategories.nonce, }; diff --git a/src/modules/author-categories/assets/js/inline-edit.js b/src/modules/author-categories/assets/js/inline-edit.js index ac9e2b8b..99b9383c 100644 --- a/src/modules/author-categories/assets/js/inline-edit.js +++ b/src/modules/author-categories/assets/js/inline-edit.js @@ -128,6 +128,7 @@ window.wp = window.wp || {}; var category_id = element.attr('data-category_id'); var category_name = element.attr('data-category_name'); var plural_name = element.attr('data-plural_name'); + var schema_property = element.attr('data-schema_property'); var category_status = Number(element.attr('data-category_status')); var enabled_category = category_status > 0 ? true : false; @@ -139,6 +140,7 @@ window.wp = window.wp || {}; $(':input[name="singular_name"]', editRow).val(category_name); $(':input[name="plural_name"]', editRow).val(plural_name); + $(':input[name="schema_property"]', editRow).val(schema_property); $(':input[name="enabled_category"]', editRow).prop('checked', enabled_category); diff --git a/src/modules/author-categories/author-categories.php b/src/modules/author-categories/author-categories.php index 9a4717c5..53720136 100644 --- a/src/modules/author-categories/author-categories.php +++ b/src/modules/author-categories/author-categories.php @@ -238,6 +238,7 @@ public function handleNewCategory() { $category_name = isset($_POST['category_name']) ? sanitize_text_field($_POST['category_name']) : ''; $plural_name = isset($_POST['plural_name']) ? sanitize_text_field($_POST['plural_name']) : ''; + $schema_property = isset($_POST['schema_property']) ? sanitize_text_field($_POST['schema_property']) : ''; $enabled_category = isset($_POST['enabled_category']) ? intval($_POST['enabled_category']) : 0; $slug = sanitize_title($category_name); @@ -260,6 +261,7 @@ public function handleNewCategory() { $category_args = [ 'category_name' => $category_name, 'plural_name' => $plural_name, + 'meta_data' => ['schema_property' => $schema_property], 'slug' => $slug, 'category_order' => 0, 'category_status' => $enabled_category, @@ -319,6 +321,7 @@ public function handleEditCategory() { $category_name = isset($_POST['singular_name']) ? sanitize_text_field($_POST['singular_name']) : ''; $plural_name = isset($_POST['plural_name']) ? sanitize_text_field($_POST['plural_name']) : ''; + $schema_property = isset($_POST['schema_property']) ? sanitize_text_field($_POST['schema_property']) : ''; $category_status = isset($_POST['enabled_category']) ? intval($_POST['enabled_category']) : 0; $category_id = isset($_POST['category_id']) ? intval($_POST['category_id']) : 0; $slug = sanitize_title($category_name); @@ -338,6 +341,7 @@ public function handleEditCategory() { $category_args = [ 'category_name' => $category_name, 'plural_name' => $plural_name, + 'meta_data' => ['schema_property' => $schema_property], 'slug' => $slug, 'category_status' => $category_status ]; @@ -411,6 +415,12 @@ private function addAuthorCategory($insert_args) { $table_name = AuthorCategoriesSchema::tableName(); + $meta_data = []; + if (isset($insert_args['meta_data'])) { + $meta_data = $insert_args['meta_data']; + unset($insert_args['meta_data']); + } + $wpdb->insert( $table_name, $insert_args @@ -419,6 +429,9 @@ private function addAuthorCategory($insert_args) { $category_id = $wpdb->insert_id; if ((int) $category_id > 0) { + foreach ($meta_data as $meta_data_key => $meta_data_value) { + self::updateAuthorCategoryMeta($category_id, $meta_data_key, $meta_data_value); + } return get_ppma_author_categories(['id' => $category_id]); } else { return false; @@ -442,6 +455,12 @@ private function editAuthorCategory($edit_args, $id) { $table_name = AuthorCategoriesSchema::tableName(); + $meta_data = []; + if (isset($edit_args['meta_data'])) { + $meta_data = $edit_args['meta_data']; + unset($edit_args['meta_data']); + } + $wpdb->update( $table_name, $edit_args, @@ -450,9 +469,58 @@ private function editAuthorCategory($edit_args, $id) { ] ); + foreach ($meta_data as $meta_data_key => $meta_data_value) { + self::updateAuthorCategoryMeta($id, $meta_data_key, $meta_data_value); + } + return get_ppma_author_categories(['id' => $id]); } + public function updateAuthorCategoryMeta($category_id, $meta_key, $meta_value) { + global $wpdb; + + $table_name = AuthorCategoriesSchema::metaTableName(); + + if (empty($meta_value)) { + $result = $wpdb->query( + $wpdb->prepare( + "DELETE FROM {$table_name} WHERE meta_key = %s AND category_id = %d", + $meta_key, $category_id + ) + ); + } else { + $meta_ids = $wpdb->get_col($wpdb->prepare("SELECT meta_id FROM $table_name WHERE meta_key = %s AND category_id = %d", $meta_key, $category_id)); + + if (empty($meta_ids)) { + $result = $wpdb->insert( + $table_name, + [ + 'category_id' => $category_id, + 'meta_key' => $meta_key, + 'meta_value' => maybe_serialize($meta_value) + ] + ); + } else { + $_meta_value = $meta_value; + $meta_value = maybe_serialize( $meta_value ); + + $data = compact('meta_value'); + $where = [ + 'category_id' => $category_id, + 'meta_key' => $meta_key, + ]; + + $result = $wpdb->update( + $table_name, + $data, + $where + ); + } + } + + return $result ? true : false; + } + /** * Author categories callback * @@ -492,16 +560,21 @@ public function manageAuthorCategories() {

- +

- +

-
+
+ + +

+
+
+ + +