From 05395398a8a0a5e3a0d9ae2df3558f593b9f445f Mon Sep 17 00:00:00 2001 From: Ojo Paul Date: Tue, 15 Feb 2022 18:16:42 +0100 Subject: [PATCH 01/42] - Allow authors to update their own information #24 --- src/assets/css/multiple-authors.css | 14 +++ .../multiple-authors/multiple-authors.php | 117 ++++++++++++++++-- 2 files changed, 123 insertions(+), 8 deletions(-) diff --git a/src/assets/css/multiple-authors.css b/src/assets/css/multiple-authors.css index 267941dc..bae513b8 100644 --- a/src/assets/css/multiple-authors.css +++ b/src/assets/css/multiple-authors.css @@ -318,3 +318,17 @@ body.block-editor-page select[id^="post-author-selector-"] { display: block; font-style: italic; } + +/* Set tab active class if user is on their author profile edit page */ +body.own-profile-edit li[class*=' toplevel_page_term?taxonomy=author&tag_ID'] { + border: 0 none; +} +body.own-profile-edit li[class*=' toplevel_page_term?taxonomy=author&tag_ID'] a.menu-top { + background: #2271b1; + color: #fff; +} + +body.own-profile-edit .term-authors-user_id-wrap, +body.own-profile-edit .edit-tag-actions span#delete-link { + display: none !important; +} \ No newline at end of file diff --git a/src/modules/multiple-authors/multiple-authors.php b/src/modules/multiple-authors/multiple-authors.php index 04cfe23e..4900c5a7 100644 --- a/src/modules/multiple-authors/multiple-authors.php +++ b/src/modules/multiple-authors/multiple-authors.php @@ -185,6 +185,9 @@ public function init() if (!is_admin()) { add_filter('body_class', [$this, 'filter_body_class']); add_filter('comment_class', [$this, 'filterCommentClass'], 10, 5); + }else{ + //author profile edit body class + add_filter('admin_body_class', [$this, 'filter_admin_body_class']); } // Fix upload permissions for multiple authors. @@ -254,6 +257,9 @@ public function init() add_action('profile_update', [$this, 'userProfileUpdate'], 10, 2); add_filter('pre_comment_approved', [$this, 'preCommentApproved'], 10, 2); + + // Allow author to edit own author profile. + add_filter('map_meta_cap', [$this, 'filter_term_map_meta_cap'], 10, 4); } /** @@ -270,6 +276,25 @@ public function action_admin_menu_page() 'dashicons-groups', 26 ); + + $current_author = $this->get_author_by_id(get_current_user_id()); + if ( + !current_user_can(apply_filters('pp_multiple_authors_manage_authors_cap', 'ppma_manage_authors')) && + $current_author && + is_object($current_author) && + isset($current_author->term_id) + ) { + add_menu_page( + esc_html__('Your Author Profile', 'publishpress-authors'), + esc_html__('Author Profile', 'publishpress-authors'), + apply_filters('pp_multiple_authors_edit_own_profile_cap', 'ppma_edit_own_profile'), + 'term.php?taxonomy=author&tag_ID='.$current_author->term_id, + __return_empty_string(), + 'dashicons-groups', + 26 + ); + } + } /** @@ -2068,16 +2093,27 @@ public function admin_enqueue_scripts() */ public function migrate_legacy_settings() { - if (get_option('publishpress_multiple_authors_settings_migrated_3_0_0')) { - return false; - } + if (!get_option('publishpress_multiple_authors_settings_migrated_3_0_0')) { + $legacyOptions = get_option('publishpress_multiple_authors_options'); + if (!empty($legacyOptions)) { + update_option('multiple_authors_multiple_authors_options', $legacyOptions); + } - $legacyOptions = get_option('publishpress_multiple_authors_options'); - if (!empty($legacyOptions)) { - update_option('multiple_authors_multiple_authors_options', $legacyOptions); + update_option('publishpress_multiple_authors_settings_migrated_3_0_0', 1); + } + + if (!get_option('publishpress_multiple_authors_settings_migrated_3_15_0')) { + if(function_exists('get_role')){ + $capability_roles = ['administrator', 'editor', 'author']; + foreach($capability_roles as $capability_role){ + $role = get_role($capability_role); + if(is_object($role) && !is_wp_error($role)){ + $role->add_cap('ppma_edit_own_profile'); + } + } + update_option('publishpress_multiple_authors_settings_migrated_3_15_0', 1); + } } - - update_option('publishpress_multiple_authors_settings_migrated_3_0_0', 1); } /** @@ -2759,5 +2795,70 @@ public function preCommentApproved($approved, $commentData) return $approved; } + + /** + * Allow author to edit own author profile. + * + * @param $caps + * @param $cap + * @param $user_id + * @param $args + * + * @return mixed + */ + public function filter_term_map_meta_cap($caps, $cap, $user_id, $args) + { + if (in_array($cap, ['edit_term', 'ppma_manage_authors']) && in_array('ppma_manage_authors', $caps, true)) { + + $term_id = 0; + if (isset($args[0])) { + $term_id = (int)$args[0]; + }else if (isset($_POST['action']) && $_POST['action'] === 'editedtag' && isset($_POST['tag_ID']) && (int)$_POST['tag_ID'] > 0) { + //this is needed for when saving the profile as it run through edit-tags.php which user doesn't have permission + $term_id = (int)$_POST['tag_ID']; + } + $current_author = $this->get_author_by_id(get_current_user_id()); + + //allow user to edit own profile. + if ( + $term_id > 0 && + $current_author && + is_object($current_author) && + isset($current_author->term_id) && + (int)$current_author->term_id === $term_id + ) { + foreach ($caps as &$item) { + if ($item === 'ppma_manage_authors') { + $item = 'ppma_edit_own_profile'; + } + } + $caps = apply_filters('pp_authors_filter_term_map_meta_cap', $caps, $cap, $user_id, $term_id); + } + } + + return $caps; + } + + /** + * Author profile edit body class + * + * @param string $class + * + * @return string + */ + public function filter_admin_body_class($classes) { + + if(!function_exists('get_current_screen')){ + return $classes; + } + + $screen = get_current_screen(); + + if($screen && is_object($screen) && isset($screen->id) && $screen->id === 'edit-author'){ + $classes .= (current_user_can(apply_filters('pp_multiple_authors_manage_authors_cap', 'ppma_manage_authors'))) ? ' authorised-profile-edit ' : ' own-profile-edit '; + } + + return $classes; + } } } From ca94d937ccb8025117454e3a16c09cbcf9ed5919 Mon Sep 17 00:00:00 2001 From: Ojo Paul Date: Tue, 15 Feb 2022 18:21:15 +0100 Subject: [PATCH 02/42] - Use same title for Author profile menu --- src/modules/multiple-authors/multiple-authors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/multiple-authors/multiple-authors.php b/src/modules/multiple-authors/multiple-authors.php index 4900c5a7..96098b43 100644 --- a/src/modules/multiple-authors/multiple-authors.php +++ b/src/modules/multiple-authors/multiple-authors.php @@ -285,7 +285,7 @@ public function action_admin_menu_page() isset($current_author->term_id) ) { add_menu_page( - esc_html__('Your Author Profile', 'publishpress-authors'), + esc_html__('Author Profile', 'publishpress-authors'), esc_html__('Author Profile', 'publishpress-authors'), apply_filters('pp_multiple_authors_edit_own_profile_cap', 'ppma_edit_own_profile'), 'term.php?taxonomy=author&tag_ID='.$current_author->term_id, From 55fa05220178e835773da30d9939676bde126bec Mon Sep 17 00:00:00 2001 From: Ojo Paul Date: Wed, 16 Feb 2022 13:24:45 +0100 Subject: [PATCH 03/42] - Only allow one author mapped to each user #64 --- src/assets/js/multiple-authors.js | 40 ++++++++++++++++++++++++++++++ src/core/Classes/Admin_Ajax.php | 32 ++++++++++++++++++++++++ src/core/Classes/Author_Editor.php | 25 +++++++++++++++++++ src/core/Plugin.php | 11 ++++++++ 4 files changed, 108 insertions(+) diff --git a/src/assets/js/multiple-authors.js b/src/assets/js/multiple-authors.js index 93d60295..bec5d26d 100644 --- a/src/assets/js/multiple-authors.js +++ b/src/assets/js/multiple-authors.js @@ -486,6 +486,46 @@ jQuery(document).ready(function ($) { return false; }); }); + + //process a request to validate author mapped user. + $('body.taxonomy-author form#edittag').submit(function (event) { + + var $mappedUser = $('select[name="authors-user_id"]').val(); + var $termId = $('input[name="tag_ID"]').val(); + var $form = $(this); + + $('.author-response-notice').remove(); + + if (!$mappedUser || $mappedUser == "") { + return; + } + + event.preventDefault(); + + //prepare ajax data + var data = { + action: "mapped_author_validation", + author_id: $mappedUser, + term_id: $termId, + nonce: MultipleAuthorsStrings.mapped_author_nonce, + }; + + if ($('.author-loading-spinner').length === 0) { + $('.edit-tag-actions input[type="submit"]').after('
'); + } + + $('.author-loading-spinner').addClass('is-active'); + + $.post(ajaxurl, data, function (response) { + if (response.status === 'error') { + $('.edit-tag-actions').after('

' + response.content + '

'); + $('.author-loading-spinner').removeClass('is-active'); + } else { + $form.unbind('submit').submit(); + } + }); + + }); }); if (typeof console === "undefined") { diff --git a/src/core/Classes/Admin_Ajax.php b/src/core/Classes/Admin_Ajax.php index d08179ae..ed4487ea 100644 --- a/src/core/Classes/Admin_Ajax.php +++ b/src/core/Classes/Admin_Ajax.php @@ -209,4 +209,36 @@ public static function handle_author_get_user_data() echo wp_json_encode($response); exit; } + + /** + * Handle a request to validate mapped author. + */ + public static function handle_mapped_author_validation() + { + + $response['status'] = 'success'; + $response['content'] = esc_html__('Request status.', 'publishpress-authors'); + + //do not process request if nonce validation failed + if (empty($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mapped_author_nonce')) { + $response['status'] = 'error'; + $response['content'] = esc_html__('Security error. Kindly reload this page and try again', 'publishpress-authors'); + }else{ + $author_id = !empty($_POST['author_id']) ? (int)($_POST['author_id']) : 0; + $term_id = !empty($_POST['term_id']) ? (int)($_POST['term_id']) : 0; + + if ($author_id > 0) { + $author = Author::get_by_user_id($author_id); + if ($author && is_object($author) && isset($author->term_id)) { + if ((int)$author->term_id !== (int)$term_id) { + $response['status'] = 'error'; + $response['content'] = esc_html__('This user is already mapped to another author.', 'publishpress-authors'); + } + } + } + } + + wp_send_json($response); + exit; + } } diff --git a/src/core/Classes/Author_Editor.php b/src/core/Classes/Author_Editor.php index 44ddb05a..42fcc019 100644 --- a/src/core/Classes/Author_Editor.php +++ b/src/core/Classes/Author_Editor.php @@ -12,6 +12,7 @@ use MultipleAuthors\Classes\Legacy\Util; use MultipleAuthors\Classes\Objects\Author; use MultipleAuthors\Factory; +use WP_Error; /** * Class Author_Editor @@ -625,4 +626,28 @@ public static function admin_notices() echo ''; } } + + /** + * Author term mapped limit validation + * + * @param string|WP_Error $term The term name to add, or a WP_Error object if there's an error. + * @param string $taxonomy Taxonomy slug. + * + * @return array|WP_Error + */ + public static function filter_pre_insert_term($term, $taxonomy) + { + if ($taxonomy === 'author' && isset($_POST['authors-new']) && $author_id = (int)$_POST['authors-new'] > 0) { + /** + * Check if term with this user exist + */ + $author = Author::get_by_user_id($author_id); + + if ($author && is_object($author) && isset($author->term_id) && (int)$author->term_id > 0){ + return new WP_Error('duplicate_mapped_user', __('This user is already mapped to another author.', 'publishpress-authors')); + } + } + + return $term; + } } diff --git a/src/core/Plugin.php b/src/core/Plugin.php index be96f2a5..28fad89e 100644 --- a/src/core/Plugin.php +++ b/src/core/Plugin.php @@ -250,6 +250,16 @@ public function __construct() 'admin_notices', ['MultipleAuthors\\Classes\\Author_Editor', 'admin_notices'] ); + add_filter( + 'pre_insert_term', + ['MultipleAuthors\\Classes\\Author_Editor', 'filter_pre_insert_term'], + 10, + 2 + ); + add_action( + 'wp_ajax_mapped_author_validation', + ['MultipleAuthors\\Classes\\Admin_Ajax', 'handle_mapped_author_validation'] + ); add_filter('admin_footer_text', [$this, 'update_footer_admin']); } @@ -1391,6 +1401,7 @@ public function enqueue_scripts($hook_suffix) 'Sorry, the request returned an error.', 'publishpress-authors' ), + 'mapped_author_nonce' => wp_create_nonce("mapped_author_nonce"), ]; wp_localize_script( From a5ae2ba3fbe4b88cd568738aceb08c3ae22db8cf Mon Sep 17 00:00:00 2001 From: Ojo Paul Date: Wed, 16 Feb 2022 13:29:04 +0100 Subject: [PATCH 04/42] - use Author::get_by_user_id directly to get author term --- src/modules/multiple-authors/multiple-authors.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/multiple-authors/multiple-authors.php b/src/modules/multiple-authors/multiple-authors.php index 96098b43..faf7bea8 100644 --- a/src/modules/multiple-authors/multiple-authors.php +++ b/src/modules/multiple-authors/multiple-authors.php @@ -277,7 +277,7 @@ public function action_admin_menu_page() 26 ); - $current_author = $this->get_author_by_id(get_current_user_id()); + $current_author = Author::get_by_user_id(get_current_user_id()); if ( !current_user_can(apply_filters('pp_multiple_authors_manage_authors_cap', 'ppma_manage_authors')) && $current_author && @@ -2817,7 +2817,7 @@ public function filter_term_map_meta_cap($caps, $cap, $user_id, $args) //this is needed for when saving the profile as it run through edit-tags.php which user doesn't have permission $term_id = (int)$_POST['tag_ID']; } - $current_author = $this->get_author_by_id(get_current_user_id()); + $current_author = Author::get_by_user_id(get_current_user_id()); //allow user to edit own profile. if ( From 65de5edcdb1d93f4a39b4e718d75d3a55ea85a3f Mon Sep 17 00:00:00 2001 From: Ojo Paul Date: Thu, 17 Feb 2022 10:12:00 +0100 Subject: [PATCH 05/42] - Either / or choice for avatars #564 --- src/assets/js/multiple-authors.js | 34 +++++++++++ src/core/Classes/Author_Editor.php | 92 ++++++++++++++++++++++++++++-- src/core/Plugin.php | 4 ++ 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/src/assets/js/multiple-authors.js b/src/assets/js/multiple-authors.js index 93d60295..3d0a3e4a 100644 --- a/src/assets/js/multiple-authors.js +++ b/src/assets/js/multiple-authors.js @@ -486,6 +486,40 @@ jQuery(document).ready(function ($) { return false; }); }); + + /** + * Add tab class to author editor's tr without tab + * + * This will add general tab class to 'Name' and Author URL + * or any tab that's rendered by default or third party + * without tab attribute + */ + if ($('body').hasClass('taxonomy-author')) { + $('form#edittag tr.form-field:not(.ppma-tab-content)') + .addClass('ppma-tab-content ppma-general-tab') + .attr('data-tab', 'general'); + } + + /** + * Author editor tab switch + */ + $(document).on('click', '.ppma-editor-tabs a', function (event) { + + event.preventDefault(); + + var clicked_tab = $(this).attr('data-tab'); + + //remove active class from all tabs + $('.ppma-editor-tabs a').removeClass('nav-tab-active'); + //add active class to current tab + $(this).addClass('nav-tab-active'); + + //hide all tabs contents + $('.ppma-tab-content').hide(); + //show this current tab contents + $('.ppma-' + clicked_tab + '-tab').show(); + + }); }); if (typeof console === "undefined") { diff --git a/src/core/Classes/Author_Editor.php b/src/core/Classes/Author_Editor.php index 44ddb05a..3c54976b 100644 --- a/src/core/Classes/Author_Editor.php +++ b/src/core/Classes/Author_Editor.php @@ -22,6 +22,9 @@ */ class Author_Editor { + + const AUTHOR_EDITOR_DEFAULT_TAB = 'general'; + /** * Customize the term table to look more like the users table. * @@ -226,6 +229,41 @@ public static function filter_author_row_actions($actions, $author_term) return $actions; } + /** + * Render fields tabs for the author profile editor + * + * @param WP_Term $term Author term being edited. + */ + public static function action_author_edit_form_fields_tab($term) + { + $author = Author::get_by_term_id($term->term_id); + + /** + * Filter the fields tabs on the Author's profile. + * + * @param array $tabs + * @param Author $author + * + * @return array + */ + $fields_tabs = apply_filters('multiple_authors_author_fields_tabs', self::get_fields_tabs($author), $author); + + echo '