From 60e8d47d2bec690367c9628a81bd3c03a0e9da76 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 12:01:59 +0200 Subject: [PATCH 01/60] WIP --- classes/class-base.php | 4 + classes/class-suggested-tasks.php | 15 + .../front-end/class-front-end-onboarding.php | 630 ++++++++++++++++++ 3 files changed, 649 insertions(+) create mode 100644 classes/front-end/class-front-end-onboarding.php diff --git a/classes/class-base.php b/classes/class-base.php index f952929cc..a4cb42711 100644 --- a/classes/class-base.php +++ b/classes/class-base.php @@ -55,6 +55,7 @@ * @method \Progress_Planner\Admin\Widgets\Challenge get_admin__widgets__challenge() * @method \Progress_Planner\Admin\Widgets\Activity_Scores get_admin__widgets__activity_scores() * @method \Progress_Planner\Utils\Date get_utils__date() + * @method \Progress_Planner\Front_End\Front_End_Onboarding get_front_end_onboarding() */ class Base { @@ -170,6 +171,9 @@ public function init() { // Init the enqueue class. $this->get_admin__enqueue()->init(); + + // TODO: Decide when this needs to be initialized. + $this->get_front_end__front_end_onboarding(); } /** diff --git a/classes/class-suggested-tasks.php b/classes/class-suggested-tasks.php index 5b1f44362..8f997e746 100644 --- a/classes/class-suggested-tasks.php +++ b/classes/class-suggested-tasks.php @@ -197,6 +197,17 @@ public function maybe_complete_task() { return; } + $this->complete_task( $task_id ); + } + + /** + * Complete a task. + * + * @param string $task_id The task ID. + * + * @return bool + */ + public function complete_task( $task_id ) { if ( ! $this->was_task_completed( $task_id ) ) { $task = \progress_planner()->get_suggested_tasks_db()->get_post( $task_id ); @@ -205,8 +216,12 @@ public function maybe_complete_task() { // Insert an activity. $this->insert_activity( $task_id ); + + return true; } } + + return false; } /** diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php new file mode 100644 index 000000000..8814eb580 --- /dev/null +++ b/classes/front-end/class-front-end-onboarding.php @@ -0,0 +1,630 @@ +add_node( + [ + 'id' => 'progress-planner-tour', + 'title' => 'Progress Planner Tour', + 'href' => '#', + 'meta' => [ + 'onclick' => 'prplStartTour(); return false;', + ], + ] + ); + } + + /** + * Save the tour progress. + * + * @return void + */ + public function ajax_save_tour_progress() { + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['state'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'State is required.', 'progress-planner' ) ] ); + } + $progress = \sanitize_text_field( \wp_unslash( $_POST['state'] ) ); + + \error_log( print_r( $progress, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r, WordPress.PHP.DevelopmentFunctions.error_log_error_log + + // Save as user meta? + \update_user_meta( \get_current_user_id(), '_prpl_tour_progress', $progress ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Tour progress saved.', 'progress-planner' ) ] ); + } + + /** + * Complete a task. + * + * @return void + */ + public function ajax_complete_task() { + + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['task_id'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Task ID is required.', 'progress-planner' ) ] ); + } + + $task_id = \sanitize_text_field( \wp_unslash( $_POST['task_id'] ) ); + + // Note: Completing task will set it it to pending, so user will get celebration. + // Do we want that? + $result = \progress_planner()->get_suggested_tasks()->complete_task( $task_id ); + + if ( ! $result ) { + \error_log( 'Task not completed: ' . $task_id ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + \wp_send_json_error( [ 'message' => \esc_html__( 'Task not completed.', 'progress-planner' ) ] ); + } + + \error_log( 'Task completed: ' . $task_id ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + \wp_send_json_success( [ 'message' => \esc_html__( 'Task completed.', 'progress-planner' ) ] ); + } + + /** + * Add the popover. + * + * @return void + */ + public function add_popover() { + ?> +
+ +
+

+ +

+
+
+ +
+ +
+ + + get_suggested_tasks_db()->get_tasks_by( + [ + 'post_status' => 'publish', + 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query + [ + 'taxonomy' => 'prpl_recommendations_provider', + 'field' => 'slug', + 'terms' => 'user', + 'operator' => 'NOT IN', + ], + ], + ] + ); + $tasks = []; + foreach ( $ravis_recommendations as $recommendation ) { + + $tasks[] = [ + 'task_id' => $recommendation->task_id, + 'title' => $recommendation->post_title, + 'url' => $recommendation->url, + 'provider_id' => $recommendation->get_provider_id(), + 'points' => $recommendation->points, + ]; + } + ?> + + + + + + + + + + + + + + + + + Date: Mon, 22 Sep 2025 12:17:02 +0200 Subject: [PATCH 02/60] split stlyes --- assets/css/front-end-onboarding.css | 83 ++++++++++++ .../front-end/class-front-end-onboarding.php | 121 +++--------------- 2 files changed, 98 insertions(+), 106 deletions(-) create mode 100644 assets/css/front-end-onboarding.css diff --git a/assets/css/front-end-onboarding.css b/assets/css/front-end-onboarding.css new file mode 100644 index 000000000..44ed6f928 --- /dev/null +++ b/assets/css/front-end-onboarding.css @@ -0,0 +1,83 @@ +#prpl-popover-front-end-onboarding { + + padding: 24px 24px 14px 24px; + box-sizing: border-box; + + background: #fff; + border: 1px solid #9ca3af; + border-radius: 8px; + font-weight: 400; + max-height: 82vh; + width: 1200px; + max-width: 80vw; + + &::backdrop { + background: rgba(0, 0, 0, 0.5); + } + + .prpl-btn { + display: inline-block; + margin: 1rem 0; + padding: 0.75rem 1.25rem; + color: #fff; + text-decoration: none; + cursor: pointer; + font-size: 16px; + background: #dd3244; + line-height: 1.25; + box-shadow: none; + border: none; + border-radius: 6px; + transition: all 0.25s ease-in-out; + font-weight: 600; + text-align: center; + box-sizing: border-box; + position: relative; + z-index: 1; + + &:disabled { + opacity: 0.5; + pointer-events: none; + } + + &:not([disabled]):hover, + &:not([disabled]):focus { + background: #cf2441; + } + } + + .prpl-complete-task-btn { + border: none; + background: none; + cursor: pointer; + padding: 0; + margin: 0; + font-size: 16px; + color: #1e40af; + } + + .prpl-complete-task-btn-completed { + color: #059669; + } + + .prpl-complete-task-btn-error { + color: #9f0712; + } + + .prpl-complete-task-item { + margin-bottom: 1rem; + display: flex; + align-items: center; + justify-content: space-between; + } + + #prpl-more-tasks-list { + list-style: none; + padding: 0; + margin: 0; + + li:not(:last-child) { + margin-bottom: 10px; + } + } +} diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 8814eb580..564c0f365 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -12,25 +12,20 @@ */ class Front_End_Onboarding { - /** - * The popover ID for front end onboarding. - * - * @var string - */ - const POPOVER_ID = 'front-end-onboarding'; - /** * Constructor. * * @return void */ public function __construct() { + // Add popover markup. \add_action( 'wp_footer', [ $this, 'add_popover' ] ); - \add_action( 'wp_footer', [ $this, 'add_popover_style' ] ); - \add_action( 'wp_footer', [ $this, 'add_popover_inline_script' ] ); + // Add popover scripts. + \add_action( 'wp_enqueue_scripts', [ $this, 'add_popover_scripts' ] ); + // Add admin toolbar item. \add_action( 'admin_bar_menu', [ $this, 'add_admin_toolbar_item' ] ); @@ -39,6 +34,15 @@ public function __construct() { \add_action( 'wp_ajax_progress_planner_tour_save_progress', [ $this, 'ajax_save_tour_progress' ] ); } + /** + * Add popover scripts. + * + * @return void + */ + public function add_popover_scripts() { + \wp_enqueue_style( 'prpl-popover-front-end-onboarding', \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/css/front-end-onboarding.css', [], \progress_planner()->get_plugin_version() ); + } + /** * Add admin toolbar item. * @@ -127,7 +131,7 @@ public function ajax_complete_task() { */ public function add_popover() { ?> -
+

@@ -146,101 +150,6 @@ public function add_popover() { - - From fb45a78ffe5164c9accdfd009d7adbbf25761450 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 12:54:19 +0200 Subject: [PATCH 05/60] no need for tasks on backend, yet --- classes/front-end/class-front-end-onboarding.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 2e8621d49..ebc09e930 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -59,16 +59,6 @@ public function add_popover_scripts() { ], ] ); - $tasks = []; - foreach ( $ravis_recommendations as $recommendation ) { - $tasks[] = [ - 'task_id' => $recommendation->task_id, - 'title' => $recommendation->post_title, - 'url' => $recommendation->url, - 'provider_id' => $recommendation->get_provider_id(), - 'points' => $recommendation->points, - ]; - } \wp_localize_script( 'prpl-popover-front-end-onboarding', @@ -76,7 +66,6 @@ public function add_popover_scripts() { [ 'adminAjaxUrl' => \esc_url_raw( admin_url( 'admin-ajax.php' ) ), 'nonceProgressPlanner' => \esc_js( \wp_create_nonce( 'progress_planner' ) ), - 'tasks' => $tasks, ] ); } From ec8b8dce6d45e2a2ee923886bf0a7985869a5204 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 12:59:52 +0200 Subject: [PATCH 06/60] no need to mount finish step --- assets/js/front-end-onboarding.js | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index 6b281d625..a9eab4209 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -67,7 +67,6 @@ class ProgressPlannerTour { title: 'Setup complete', render: () => document.getElementById( 'tour-step-finish' ).innerHTML, - onMount: ( state ) => this.mountFinishStep( state ), }, ]; } @@ -134,23 +133,6 @@ class ProgressPlannerTour { }; } - /** - * Mount finish step - * @param {Object} state - */ - mountFinishStep( state ) { - const btn = document.querySelector( '#prpl-finish-btn' ); - if ( ! btn ) return () => {}; - - const handler = () => { - state.data.finished = true; - this.closeTour(); - }; - - btn.addEventListener( 'click', handler ); - return () => btn.removeEventListener( 'click', handler ); - } - /** * Complete a task via AJAX * @param {string} taskId @@ -355,9 +337,11 @@ class ProgressPlannerTour { const nextBtn = popover.querySelector( '.prpl-tour-next' ); const prevBtn = popover.querySelector( '.prpl-tour-prev' ); + const finishBtn = popover.querySelector( '#prpl-finish-btn' ); console.log( 'Next button found:', nextBtn ); console.log( 'Prev button found:', prevBtn ); + console.log( 'Finish button found:', finishBtn ); if ( nextBtn ) { nextBtn.addEventListener( 'click', () => { @@ -372,6 +356,13 @@ class ProgressPlannerTour { this.prevStep(); } ); } + + if ( finishBtn ) { + finishBtn.addEventListener( 'click', () => { + console.log( 'Finish button clicked!' ); + this.closeTour(); + } ); + } } else { console.error( 'Popover not found!' ); } From e4b2d071f3f36b754f1d81248fb298acaf826149 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 16:33:08 +0200 Subject: [PATCH 07/60] a bit of localization (titles needs to be localized as well) --- classes/front-end/class-front-end-onboarding.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index ebc09e930..9d33b00e7 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -225,7 +225,7 @@ public function add_popover_inline_script() { @@ -234,7 +234,7 @@ public function add_popover_inline_script() {

- +

@@ -243,13 +243,13 @@ public function add_popover_inline_script() { Date: Mon, 22 Sep 2025 16:41:47 +0200 Subject: [PATCH 08/60] get proxy comment --- assets/js/front-end-onboarding.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index a9eab4209..73137f2c9 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -384,6 +384,8 @@ class ProgressPlannerTour { */ createDeepProxy( target, callback ) { return new Proxy( target, { + // Note: Maybe hook into get here as well, to handle reactivity better. + set: ( obj, prop, value ) => { if ( value && From fbda2d1b6f057a131d38c028f64328d23021b6bb Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 17:05:06 +0200 Subject: [PATCH 09/60] dont reset state --- assets/js/front-end-onboarding.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index 73137f2c9..1cefed6f6 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -103,8 +103,6 @@ class ProgressPlannerTour { * @param {Object} state */ mountMoreTasksStep( state ) { - state.data.moreTasksCompleted = {}; - const handler = ( e ) => { const thisBtn = e.target.closest( 'button' ); this.completeTask( thisBtn.dataset.taskId ) From e2ed1750d2c00ecf70ca15ec006700cf2836985c Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Tue, 23 Sep 2025 09:01:41 +0200 Subject: [PATCH 10/60] better proxy --- assets/js/front-end-onboarding.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index 1cefed6f6..d961b0739 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -381,9 +381,18 @@ class ProgressPlannerTour { * @param {Function} callback */ createDeepProxy( target, callback ) { - return new Proxy( target, { - // Note: Maybe hook into get here as well, to handle reactivity better. + // Recursively wrap existing nested objects first + for ( const key of Object.keys( target ) ) { + if ( + target[ key ] && + typeof target[ key ] === 'object' && + ! Array.isArray( target[ key ] ) + ) { + target[ key ] = this.createDeepProxy( target[ key ], callback ); + } + } + return new Proxy( target, { set: ( obj, prop, value ) => { if ( value && From 19e585682ecccb722700b69be6632f5fcb621239 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Tue, 23 Sep 2025 09:25:41 +0200 Subject: [PATCH 11/60] titles to templates --- assets/js/front-end-onboarding.js | 9 +- .../front-end/class-front-end-onboarding.php | 114 ++++++++++-------- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index d961b0739..60d71ef36 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -28,13 +28,11 @@ class ProgressPlannerTour { return [ { id: 'welcome', - title: 'Welcome', render: () => document.getElementById( 'tour-step-welcome' ).innerHTML, }, { id: 'first-task', - title: 'Complete your first task', render: () => document.getElementById( 'tour-step-first-task' ).innerHTML, onMount: ( state ) => this.mountFirstTaskStep( state ), @@ -42,13 +40,11 @@ class ProgressPlannerTour { }, { id: 'badges', - title: 'Our badges are waiting for you', render: () => document.getElementById( 'tour-step-badges' ).innerHTML, }, { id: 'more-tasks', - title: 'Complete more tasks', render: () => document.getElementById( 'tour-step-more-tasks' ).innerHTML, onMount: ( state ) => this.mountMoreTasksStep( state ), @@ -64,7 +60,6 @@ class ProgressPlannerTour { }, { id: 'finish', - title: 'Setup complete', render: () => document.getElementById( 'tour-step-finish' ).innerHTML, }, @@ -159,8 +154,8 @@ class ProgressPlannerTour { const step = this.tourSteps[ this.state.currentStep ]; const popover = this.getPopover(); - popover.querySelector( '.tour-title' ).innerHTML = step.title; - popover.querySelector( '.tour-content' ).innerHTML = step.render(); + popover.querySelector( '.tour-content-wrapper' ).innerHTML = + step.render(); // Cleanup previous step if ( this.state.cleanup ) { diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 9d33b00e7..dc242bcaa 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -46,20 +46,6 @@ public function add_popover_scripts() { // Enqueue front-end-onboarding.js. \wp_enqueue_script( 'prpl-popover-front-end-onboarding', \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/js/front-end-onboarding.js', [], \progress_planner()->get_plugin_version(), true ); - $ravis_recommendations = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( - [ - 'post_status' => 'publish', - 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query - [ - 'taxonomy' => 'prpl_recommendations_provider', - 'field' => 'slug', - 'terms' => 'user', - 'operator' => 'NOT IN', - ], - ], - ] - ); - \wp_localize_script( 'prpl-popover-front-end-onboarding', 'ProgressPlannerData', @@ -92,7 +78,7 @@ public function add_admin_toolbar_item_callback( $admin_bar ) { 'title' => 'Progress Planner Tour', 'href' => '#', 'meta' => [ - 'onclick' => 'prplStartTour(); return false;', + 'onclick' => 'window.prplTour.startTour(); return false;', ], ] ); @@ -160,14 +146,10 @@ public function add_popover() { ?>
-
-

- -

-
-
+
+

-

- -

+
+
+

+ +

+ +

+ Lorem ipsum dolor sit amet consectetur adipiscing, elit nullam hendrerit porttitor torquent, nec molestie hac parturient vehicula. Fames condimentum netus nisl tempus potenti curabitur iaculis nam velit, etiam sapien mollis dictum vitae eu bibendum per mus, hendrerit dis blandit parturient dictumst cum ridiculus libero. +

+
+
+ Badges +
+
diff --git a/views/front-end-onboarding/finish.php b/views/front-end-onboarding/finish.php index 98a6d711b..e1a949945 100644 --- a/views/front-end-onboarding/finish.php +++ b/views/front-end-onboarding/finish.php @@ -20,5 +20,12 @@

+

+ +

+ Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam, nibh accumsan ultricies ad placerat maecenas. +

+ Id sollicitudin ac auctor odio luctus ornare donec duis maecenas sodales montes nostra mi aliquam ultricies augue, posuere torquent imperdiet lobortis cras gravida nascetur venenatis malesuada potenti et mattis massa parturient. +

diff --git a/views/front-end-onboarding/first-task.php b/views/front-end-onboarding/first-task.php index 03722aeeb..531914952 100644 --- a/views/front-end-onboarding/first-task.php +++ b/views/front-end-onboarding/first-task.php @@ -19,18 +19,37 @@
-

You have pending tasks to complete.

- -
-

- -

-
- - -
+
+
+

You have pending tasks to complete.

+ +

+ Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam, nibh accumsan ultricies ad placerat maecenas. Id sollicitudin ac auctor odio luctus ornare donec duis maecenas sodales montes nostra mi aliquam ultricies augue, posuere torquent imperdiet lobortis cras gravida nascetur venenatis malesuada potenti et mattis massa parturient. +

+
+
+ +
+
+

+ +

+

+ Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

+ +

+ Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

+
+
+ + +
+
+
+ +
-
-
diff --git a/views/front-end-onboarding/more-tasks.php b/views/front-end-onboarding/more-tasks.php index 183fdd426..90f9d7320 100644 --- a/views/front-end-onboarding/more-tasks.php +++ b/views/front-end-onboarding/more-tasks.php @@ -27,7 +27,7 @@ break; } ?>
  • - +
    diff --git a/views/front-end-onboarding/welcome.php b/views/front-end-onboarding/welcome.php index ff58405cc..90c9ee84f 100644 --- a/views/front-end-onboarding/welcome.php +++ b/views/front-end-onboarding/welcome.php @@ -19,6 +19,18 @@
  • -

    +
    +
    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit facilisis interdum rhoncus magnis, curabitur pretium molestie posuere massa tristique commodo pellentesque ullamcorper vulputate risus, netus sem est metus habitasse sodales diam aliquam scelerisque mi. +

    +

    + Egestas turpis class nostra sociis felis nunc rhoncus, semper tristique fermentum mus scelerisque habitasse accumsan imperdiet, nisl in vehicula magna pretium ullamcorper. Dis dictumst semper urna hendrerit a pretium lectus luctus justo proin, elementum cras sodales dictum habitant curae iaculis nibh nisi tortor tempus, malesuada torquent etiam litora facilisis arcu montes bibendum nisl. Ultrices arcu varius id pellentesque metus quis litora odio vestibulum taciti molestie, maecenas fames quam conubia ultricies donec aliquet pharetra tristique. +

    +
    +
    + +
    +
    From aa9fa8dfcf26b3979bdea0cb868c44963756a9eb Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 29 Sep 2025 15:46:43 +0200 Subject: [PATCH 20/60] dashboard button --- assets/js/front-end-onboarding.js | 2 ++ classes/front-end/class-front-end-onboarding.php | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index d96b4d043..03a350359 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -28,6 +28,7 @@ class ProgressPlannerTour { this.prevBtn = this.popover.querySelector( '.prpl-tour-prev' ); this.nextBtn = this.popover.querySelector( '.prpl-tour-next' ); this.finishBtn = this.popover.querySelector( '#prpl-finish-btn' ); + this.dashboardBtn = this.popover.querySelector( '#prpl-dashboard-btn' ); // Setup event listeners after DOM is ready this.setupEventListeners(); @@ -222,6 +223,7 @@ class ProgressPlannerTour { isFirstStep || isLastStep ? 'none' : 'inline-block'; this.nextBtn.style.display = isLastStep ? 'none' : 'inline-block'; this.finishBtn.style.display = isLastStep ? 'inline-block' : 'none'; + this.dashboardBtn.style.display = isLastStep ? 'inline-block' : 'none'; } /** diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index fe1d2b437..457e1564a 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -192,9 +192,10 @@ public function add_popover() { Date: Mon, 29 Sep 2025 15:49:36 +0200 Subject: [PATCH 21/60] add inline redirect --- classes/front-end/class-front-end-onboarding.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 457e1564a..d0598170d 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -195,7 +195,7 @@ public function add_popover() { - + Date: Tue, 30 Sep 2025 19:36:54 +0000 Subject: [PATCH 22/60] Add All in One SEO Pack integration with 8 task providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements AIOSEO equivalents for Yoast SEO recommendations #1-#8: - Archive Author: Disable author archives for single-author sites - Archive Date: Disable date archives when not used in permalinks - Archive Format: Disable format archives for sites with few formatted posts - Media Pages: Redirect media/attachment pages to parent posts - Crawl Settings Emoji Scripts: Enable crawl cleanup optimization - Crawl Settings Feed Authors: Disable author RSS feeds for single-author sites - Crawl Settings Feed Global Comments: Disable global comment RSS feeds - Organization Logo: Set organization or person logo for schema markup All providers follow the same pattern as Yoast integration and include proper condition checking using AIOSEO's options API. The integration automatically detects if All in One SEO is active via the aioseo() function. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Co-authored-by: Sculptor --- .../suggested-tasks/class-tasks-manager.php | 6 +- .../aioseo/class-add-aioseo-providers.php | 54 ++++++++ .../aioseo/class-aioseo-provider.php | 30 +++++ .../aioseo/class-archive-author.php | 118 +++++++++++++++++ .../aioseo/class-archive-date.php | 105 +++++++++++++++ .../aioseo/class-archive-format.php | 120 +++++++++++++++++ .../class-crawl-settings-emoji-scripts.php | 85 ++++++++++++ .../class-crawl-settings-feed-authors.php | 124 ++++++++++++++++++ ...ss-crawl-settings-feed-global-comments.php | 91 +++++++++++++ .../integrations/aioseo/class-media-pages.php | 87 ++++++++++++ .../aioseo/class-organization-logo.php | 115 ++++++++++++++++ 11 files changed, 934 insertions(+), 1 deletion(-) create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-provider.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php diff --git a/classes/suggested-tasks/class-tasks-manager.php b/classes/suggested-tasks/class-tasks-manager.php index 1c6f30d85..afc160e20 100644 --- a/classes/suggested-tasks/class-tasks-manager.php +++ b/classes/suggested-tasks/class-tasks-manager.php @@ -25,6 +25,7 @@ use Progress_Planner\Suggested_Tasks\Providers\Search_Engine_Visibility; use Progress_Planner\Suggested_Tasks\Tasks_Interface; use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Add_Yoast_Providers; +use Progress_Planner\Suggested_Tasks\Providers\Integrations\AIOSEO\Add_AIOSEO_Providers; use Progress_Planner\Suggested_Tasks\Providers\User as User_Tasks; use Progress_Planner\Suggested_Tasks\Providers\Email_Sending; use Progress_Planner\Suggested_Tasks\Providers\Set_Valuable_Post_Types; @@ -98,13 +99,16 @@ public function __construct() { } /** - * Add the Yoast task if the plugin is active. + * Add the plugin integrations if the plugins are active. * * @return void */ public function add_plugin_integration() { // Yoast SEO integration. new Add_Yoast_Providers(); + + // All in One SEO integration. + new Add_AIOSEO_Providers(); } /** diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php new file mode 100644 index 000000000..ed4fc3b01 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php @@ -0,0 +1,54 @@ +providers = [ + new Archive_Author(), + new Archive_Date(), + new Archive_Format(), + new Media_Pages(), + new Crawl_Settings_Emoji_Scripts(), + new Crawl_Settings_Feed_Authors(), + new Crawl_Settings_Feed_Global_Comments(), + new Organization_Logo(), + ]; + + return \array_merge( + $providers, + $this->providers + ); + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-provider.php b/classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-provider.php new file mode 100644 index 000000000..1b357b8f3 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-provider.php @@ -0,0 +1,30 @@ +is_task_relevant() ) { + return false; + } + + // Check if author archives are already disabled in AIOSEO. + $options = \aioseo()->options->searchAppearance->archives; + + // Check if author archives show in search results is set to false. + if ( isset( $options->author->show ) && false === $options->author->show ) { + return false; + } + + return true; + } + + /** + * Check if the task is still relevant. + * For example, we have a task to disable author archives if there is only one author. + * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. + * + * @return bool + */ + public function is_task_relevant() { + // If there is more than one author, we don't need to add the task. + return $this->get_data_collector()->collect() <= self::MINIMUM_AUTHOR_WITH_POSTS; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php new file mode 100644 index 000000000..dc64c2293 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php @@ -0,0 +1,105 @@ +is_task_relevant() ) { + return false; + } + + // Check if date archives are already disabled in AIOSEO. + $options = \aioseo()->options->searchAppearance->archives; + + // Check if date archives show in search results is set to false. + if ( isset( $options->date->show ) && false === $options->date->show ) { + return false; + } + + return true; + } + + /** + * Check if the task is still relevant. + * For example, we have a task to disable author archives if there is only one author. + * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. + * + * @return bool + */ + public function is_task_relevant() { + // If the permalink structure includes %year%, %monthnum%, or %day%, we don't need to add the task. + $permalink_structure = \get_option( 'permalink_structure' ); + return \strpos( $permalink_structure, '%year%' ) === false + && \strpos( $permalink_structure, '%monthnum%' ) === false + && \strpos( $permalink_structure, '%day%' ) === false; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php new file mode 100644 index 000000000..439c1e68f --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php @@ -0,0 +1,120 @@ +is_task_relevant() ) { + return false; + } + + // Note: AIOSEO may not have a specific "format archives" toggle. + // This task will guide users to the Archives settings where they can manage post format taxonomies. + // Check if post format archives are disabled via noindex or show setting. + $options = \aioseo()->options->searchAppearance->archives; + + // Check if post format archive exists and is set to not show. + if ( isset( $options->postFormat->show ) && false === $options->postFormat->show ) { + return false; + } + + return true; + } + + /** + * Check if the task is still relevant. + * For example, we have a task to disable author archives if there is only one author. + * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. + * + * @return bool + */ + public function is_task_relevant() { + // If there are more than X posts with a post format, we don't need to add the task. X is set in the class. + return $this->get_data_collector()->collect() <= static::MINIMUM_POSTS_WITH_FORMAT; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php new file mode 100644 index 000000000..886a3dd8a --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php @@ -0,0 +1,85 @@ +options->searchAppearance->advanced; + + // Check if crawlCleanup is enabled. + if ( isset( $options->crawlCleanup ) && true === $options->crawlCleanup ) { + return false; + } + + return true; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Enable', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php new file mode 100644 index 000000000..55eade186 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php @@ -0,0 +1,124 @@ +is_task_relevant() ) { + return false; + } + + // Check if crawl cleanup is enabled and author feeds are disabled. + $options = \aioseo()->options->searchAppearance->advanced; + + // First check if crawl cleanup is enabled. + if ( ! isset( $options->crawlCleanup ) || false === $options->crawlCleanup ) { + // If crawl cleanup is not enabled, this task is not yet applicable. + return false; + } + + // Check if author feeds are already disabled. + if ( isset( $options->feeds ) && isset( $options->feeds->author ) && false === $options->feeds->author ) { + return false; + } + + return true; + } + + /** + * Check if the task is still relevant. + * For example, we have a task to disable author archives if there is only one author. + * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. + * + * @return bool + */ + public function is_task_relevant() { + // If there is more than one author, we don't need to add the task. + return $this->get_data_collector()->collect() <= self::MINIMUM_AUTHOR_WITH_POSTS; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php new file mode 100644 index 000000000..a68c63368 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php @@ -0,0 +1,91 @@ +options->searchAppearance->advanced; + + // First check if crawl cleanup is enabled. + if ( ! isset( $options->crawlCleanup ) || false === $options->crawlCleanup ) { + // If crawl cleanup is not enabled, this task is not yet applicable. + return false; + } + + // Check if comment feeds are already disabled. + if ( isset( $options->feeds ) && isset( $options->feeds->comments ) && false === $options->feeds->comments ) { + return false; + } + + return true; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php new file mode 100644 index 000000000..bfaf1bd3a --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php @@ -0,0 +1,87 @@ +options->searchAppearance; + + // Check if redirectAttachmentUrls is set to 'attachment' or 'attachmentParent'. + // The setting should be 'disabled', 'attachment', or 'attachmentParent'. + // We want to recommend setting it to 'attachmentParent'. + if ( isset( $options->redirectAttachmentUrls ) && 'disabled' !== $options->redirectAttachmentUrls ) { + return false; + } + + return true; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Configure redirect', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php b/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php new file mode 100644 index 000000000..6f9884477 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php @@ -0,0 +1,115 @@ +options->searchAppearance->global->schema; + $is_person = isset( $options->personOrOrganization ) && 'person' === $options->personOrOrganization; + return $is_person + ? \esc_html__( 'All in One SEO: set your person logo', 'progress-planner' ) + : \esc_html__( 'All in One SEO: set your organization logo', 'progress-planner' ); + } + + /** + * Get external link URL. + * + * @return string + */ + public function get_external_link_url() { + // Check if AIOSEO is active to determine person vs organization. + if ( ! \function_exists( 'aioseo' ) ) { + return \progress_planner()->get_ui__branding()->get_url( 'https://prpl.fyi/aioseo-organization-logo' ); + } + + $options = \aioseo()->options->searchAppearance->global->schema; + $is_person = isset( $options->personOrOrganization ) && 'person' === $options->personOrOrganization; + return $is_person + ? \progress_planner()->get_ui__branding()->get_url( 'https://prpl.fyi/aioseo-person-logo' ) + : \progress_planner()->get_ui__branding()->get_url( 'https://prpl.fyi/aioseo-organization-logo' ); + } + + /** + * Determine if the task should be added. + * + * @return bool + */ + public function should_add_task() { + // Check if AIOSEO is active. + if ( ! \function_exists( 'aioseo' ) ) { + return false; + } + + $options = \aioseo()->options->searchAppearance->global->schema; + + // Check if person or organization. + $is_person = isset( $options->personOrOrganization ) && 'person' === $options->personOrOrganization; + + // Check if logo is already set. + if ( $is_person ) { + // Check person logo. + if ( isset( $options->person->image ) && ! empty( $options->person->image ) ) { + return false; + } + } else { + // Check organization logo. + if ( isset( $options->organizationLogo ) && ! empty( $options->organizationLogo ) ) { + return false; + } + } + + return true; + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Set logo', 'progress-planner' ) . '', + ]; + + return $actions; + } +} From 7ab9307ec02596518d85410500f1ec57c7de8cc7 Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Tue, 30 Sep 2025 19:43:50 +0000 Subject: [PATCH 23/60] Fix AIOSEO author archive completion detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplified the check for author archives being disabled to use empty() which properly catches false, 0, '0', and null values. This ensures the task completes correctly when the user toggles the setting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Co-authored-by: Sculptor --- .../providers/integrations/aioseo/class-archive-author.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php index 7c70dae96..ee3f43be0 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php @@ -77,10 +77,12 @@ public function should_add_task() { } // Check if author archives are already disabled in AIOSEO. + // AIOSEO uses 'show' property - when false, archives are hidden from search results. $options = \aioseo()->options->searchAppearance->archives; - // Check if author archives show in search results is set to false. - if ( isset( $options->author->show ) && false === $options->author->show ) { + // Check if author archives show is disabled. + // Using empty() to catch false, 0, '0', null, etc. + if ( isset( $options->author ) && isset( $options->author->show ) && empty( $options->author->show ) ) { return false; } From 45cea44f5573c925c5c6f8ad4b73bdd0418c8e0a Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Tue, 30 Sep 2025 19:48:56 +0000 Subject: [PATCH 24/60] Use loose comparison for AIOSEO author archive show value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed from strict comparison to loose comparison (! $show_value) to properly detect when the setting is false. This handles any type variations in how AIOSEO returns the value from its options API. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Co-authored-by: Sculptor --- .../integrations/aioseo/class-archive-author.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php index ee3f43be0..e2d4b9d9d 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php @@ -78,11 +78,12 @@ public function should_add_task() { // Check if author archives are already disabled in AIOSEO. // AIOSEO uses 'show' property - when false, archives are hidden from search results. - $options = \aioseo()->options->searchAppearance->archives; + // Get a fresh copy of the options to avoid caching issues. + $show_value = \aioseo()->options->searchAppearance->archives->author->show; - // Check if author archives show is disabled. - // Using empty() to catch false, 0, '0', null, etc. - if ( isset( $options->author ) && isset( $options->author->show ) && empty( $options->author->show ) ) { + // If show is false (disabled), the task is complete (return false means don't add task). + // Using loose comparison to handle string/int/bool variations. + if ( ! $show_value ) { return false; } From 8da9d56b1138872cf466a72a16b807858c832917 Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Tue, 30 Sep 2025 19:50:42 +0000 Subject: [PATCH 25/60] Fix AIOSEO date archive completion detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applied the same loose comparison fix as author archives. Uses ! $show_value instead of strict comparison to properly detect when date archives are disabled in AIOSEO settings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Co-authored-by: Sculptor --- .../providers/integrations/aioseo/class-archive-date.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php index dc64c2293..dd22ff7b8 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php @@ -61,10 +61,12 @@ public function should_add_task() { } // Check if date archives are already disabled in AIOSEO. - $options = \aioseo()->options->searchAppearance->archives; + // AIOSEO uses 'show' property - when false, archives are hidden from search results. + $show_value = \aioseo()->options->searchAppearance->archives->date->show; - // Check if date archives show in search results is set to false. - if ( isset( $options->date->show ) && false === $options->date->show ) { + // If show is false (disabled), the task is complete (return false means don't add task). + // Using loose comparison to handle string/int/bool variations. + if ( ! $show_value ) { return false; } From 4a97de88419183146d06448767e47bad39bcc8c9 Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Tue, 30 Sep 2025 19:52:44 +0000 Subject: [PATCH 26/60] Remove format archives task for AIOSEO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AIOSEO doesn't have a specific setting for post format archives like Yoast does. Removed the Archive_Format provider and its registration since this recommendation is not applicable to AIOSEO. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Co-authored-by: Sculptor --- .../aioseo/class-add-aioseo-providers.php | 1 - .../aioseo/class-archive-format.php | 120 ------------------ 2 files changed, 121 deletions(-) delete mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php index ed4fc3b01..137b84edb 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php @@ -38,7 +38,6 @@ public function add_providers( $providers ) { $this->providers = [ new Archive_Author(), new Archive_Date(), - new Archive_Format(), new Media_Pages(), new Crawl_Settings_Emoji_Scripts(), new Crawl_Settings_Feed_Authors(), diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php deleted file mode 100644 index 439c1e68f..000000000 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-format.php +++ /dev/null @@ -1,120 +0,0 @@ -is_task_relevant() ) { - return false; - } - - // Note: AIOSEO may not have a specific "format archives" toggle. - // This task will guide users to the Archives settings where they can manage post format taxonomies. - // Check if post format archives are disabled via noindex or show setting. - $options = \aioseo()->options->searchAppearance->archives; - - // Check if post format archive exists and is set to not show. - if ( isset( $options->postFormat->show ) && false === $options->postFormat->show ) { - return false; - } - - return true; - } - - /** - * Check if the task is still relevant. - * For example, we have a task to disable author archives if there is only one author. - * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. - * - * @return bool - */ - public function is_task_relevant() { - // If there are more than X posts with a post format, we don't need to add the task. X is set in the class. - return $this->get_data_collector()->collect() <= static::MINIMUM_POSTS_WITH_FORMAT; - } - - /** - * Add task actions specific to this task. - * - * @param array $data The task data. - * @param array $actions The existing actions. - * - * @return array - */ - public function add_task_actions( $data = [], $actions = [] ) { - $actions[] = [ - 'priority' => 10, - 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', - ]; - - return $actions; - } -} From f3d87a3ddc29f246b73ae6b251778eb8a7636d6f Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Tue, 30 Sep 2025 20:00:36 +0000 Subject: [PATCH 27/60] Fix AIOSEO media pages redirect completion detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrected the option path to searchAppearance->postTypes->attachment->redirectAttachmentUrls and updated task to specifically recommend 'attachment' redirect option. Task now completes properly when user sets redirect to 'attachment'. Updated title to be more specific about recommending attachment redirect. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Co-authored-by: Sculptor --- .../integrations/aioseo/class-media-pages.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php index bfaf1bd3a..d29d023c4 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php @@ -41,7 +41,7 @@ protected function get_url() { * @return string */ protected function get_title() { - return \esc_html__( 'All in One SEO: redirect media/attachment pages', 'progress-planner' ); + return \esc_html__( 'All in One SEO: redirect media/attachment pages to attachment', 'progress-planner' ); } /** @@ -55,13 +55,14 @@ public function should_add_task() { return false; } - // Check if redirect attachment URLs is already enabled. - $options = \aioseo()->options->searchAppearance; + // Check if redirect attachment URLs is already set to 'attachment'. + // The setting is stored in searchAppearance -> postTypes -> attachment -> redirectAttachmentUrls. + $redirect_value = \aioseo()->options->searchAppearance->postTypes->attachment->redirectAttachmentUrls; - // Check if redirectAttachmentUrls is set to 'attachment' or 'attachmentParent'. - // The setting should be 'disabled', 'attachment', or 'attachmentParent'. - // We want to recommend setting it to 'attachmentParent'. - if ( isset( $options->redirectAttachmentUrls ) && 'disabled' !== $options->redirectAttachmentUrls ) { + // The task is complete if redirectAttachmentUrls is set to 'attachment'. + // Possible values: 'disabled', 'attachment', or 'attachmentParent'. + // We recommend 'attachment' as it redirects to the attachment file itself. + if ( 'attachment' === $redirect_value ) { return false; } From 1828f2be02500bb1f12b526ac5729bb7f7687e5f Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Tue, 30 Sep 2025 20:03:38 +0000 Subject: [PATCH 28/60] Use dynamicOptions for AIOSEO media redirect setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The redirectAttachmentUrls setting is stored in aioseo_options_dynamic, not the regular options. Updated to use aioseo()->dynamicOptions instead of aioseo()->options to properly access the attachment redirect setting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Co-authored-by: Sculptor --- .../integrations/aioseo/class-media-pages.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php index d29d023c4..d6ea3a9cd 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php @@ -56,8 +56,15 @@ public function should_add_task() { } // Check if redirect attachment URLs is already set to 'attachment'. - // The setting is stored in searchAppearance -> postTypes -> attachment -> redirectAttachmentUrls. - $redirect_value = \aioseo()->options->searchAppearance->postTypes->attachment->redirectAttachmentUrls; + // This setting is stored in aioseo_options_dynamic under: + // searchAppearance -> postTypes -> attachment -> redirectAttachmentUrls. + $dynamic_options = \aioseo()->dynamicOptions->searchAppearance->postTypes->attachment ?? null; + + if ( ! $dynamic_options ) { + return true; + } + + $redirect_value = $dynamic_options->redirectAttachmentUrls ?? null; // The task is complete if redirectAttachmentUrls is set to 'attachment'. // Possible values: 'disabled', 'attachment', or 'attachmentParent'. From ae8c91616f805a39ee6324ea183b5db6b0ce79f6 Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Tue, 30 Sep 2025 22:59:29 +0200 Subject: [PATCH 29/60] Make changes --- .../aioseo/class-add-aioseo-providers.php | 1 - .../aioseo/class-archive-author.php | 2 +- .../aioseo/class-archive-date.php | 2 +- .../class-crawl-settings-emoji-scripts.php | 85 ------------------- .../class-crawl-settings-feed-authors.php | 10 +-- ...ss-crawl-settings-feed-global-comments.php | 10 +-- .../integrations/aioseo/class-media-pages.php | 10 +-- .../aioseo/class-organization-logo.php | 22 ++--- 8 files changed, 14 insertions(+), 128 deletions(-) delete mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php index 137b84edb..4eabc077a 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php @@ -39,7 +39,6 @@ public function add_providers( $providers ) { new Archive_Author(), new Archive_Date(), new Media_Pages(), - new Crawl_Settings_Emoji_Scripts(), new Crawl_Settings_Feed_Authors(), new Crawl_Settings_Feed_Global_Comments(), new Organization_Logo(), diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php index e2d4b9d9d..78314054c 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php @@ -57,7 +57,7 @@ protected function get_url() { * @return string */ protected function get_title() { - return \esc_html__( 'All in One SEO: disable the author archive', 'progress-planner' ); + return \esc_html__( 'All in One SEO: noindex the author archive', 'progress-planner' ); } /** diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php index dd22ff7b8..4cf7c8ebc 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php @@ -41,7 +41,7 @@ protected function get_url() { * @return string */ protected function get_title() { - return \esc_html__( 'All in One SEO: disable the date archive', 'progress-planner' ); + return \esc_html__( 'All in One SEO: noindex the date archive', 'progress-planner' ); } /** diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php deleted file mode 100644 index 886a3dd8a..000000000 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-emoji-scripts.php +++ /dev/null @@ -1,85 +0,0 @@ -options->searchAppearance->advanced; - - // Check if crawlCleanup is enabled. - if ( isset( $options->crawlCleanup ) && true === $options->crawlCleanup ) { - return false; - } - - return true; - } - - /** - * Add task actions specific to this task. - * - * @param array $data The task data. - * @param array $actions The existing actions. - * - * @return array - */ - public function add_task_actions( $data = [], $actions = [] ) { - $actions[] = [ - 'priority' => 10, - 'html' => '' . \esc_html__( 'Enable', 'progress-planner' ) . '', - ]; - - return $actions; - } -} diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php index 55eade186..bf4725179 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php @@ -77,16 +77,10 @@ public function should_add_task() { } // Check if crawl cleanup is enabled and author feeds are disabled. - $options = \aioseo()->options->searchAppearance->advanced; - - // First check if crawl cleanup is enabled. - if ( ! isset( $options->crawlCleanup ) || false === $options->crawlCleanup ) { - // If crawl cleanup is not enabled, this task is not yet applicable. - return false; - } + $disable_author_feed = \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->authors; // Check if author feeds are already disabled. - if ( isset( $options->feeds ) && isset( $options->feeds->author ) && false === $options->feeds->author ) { + if ( $disable_author_feed === false ) { return false; } diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php index a68c63368..8268fdbbc 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php @@ -56,16 +56,10 @@ public function should_add_task() { } // Check if crawl cleanup is enabled and comment feeds are disabled. - $options = \aioseo()->options->searchAppearance->advanced; - - // First check if crawl cleanup is enabled. - if ( ! isset( $options->crawlCleanup ) || false === $options->crawlCleanup ) { - // If crawl cleanup is not enabled, this task is not yet applicable. - return false; - } + $disable_comment_feed = \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->globalComments; // Check if comment feeds are already disabled. - if ( isset( $options->feeds ) && isset( $options->feeds->comments ) && false === $options->feeds->comments ) { + if ( $disable_comment_feed === false ) { return false; } diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php index d6ea3a9cd..24d8d4d49 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php @@ -58,18 +58,12 @@ public function should_add_task() { // Check if redirect attachment URLs is already set to 'attachment'. // This setting is stored in aioseo_options_dynamic under: // searchAppearance -> postTypes -> attachment -> redirectAttachmentUrls. - $dynamic_options = \aioseo()->dynamicOptions->searchAppearance->postTypes->attachment ?? null; - - if ( ! $dynamic_options ) { - return true; - } - - $redirect_value = $dynamic_options->redirectAttachmentUrls ?? null; + $redirect = \aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls; // The task is complete if redirectAttachmentUrls is set to 'attachment'. // Possible values: 'disabled', 'attachment', or 'attachmentParent'. // We recommend 'attachment' as it redirects to the attachment file itself. - if ( 'attachment' === $redirect_value ) { + if ( 'attachment' === $redirect ) { return false; } diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php b/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php index 6f9884477..3c505291e 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php @@ -39,11 +39,7 @@ protected function get_title() { return \esc_html__( 'All in One SEO: set your organization logo', 'progress-planner' ); } - $options = \aioseo()->options->searchAppearance->global->schema; - $is_person = isset( $options->personOrOrganization ) && 'person' === $options->personOrOrganization; - return $is_person - ? \esc_html__( 'All in One SEO: set your person logo', 'progress-planner' ) - : \esc_html__( 'All in One SEO: set your organization logo', 'progress-planner' ); + return \esc_html__( 'All in One SEO: set your organization logo', 'progress-planner' ); } /** @@ -58,7 +54,7 @@ public function get_external_link_url() { } $options = \aioseo()->options->searchAppearance->global->schema; - $is_person = isset( $options->personOrOrganization ) && 'person' === $options->personOrOrganization; + $is_person = isset( $options->siteRepresents ) && 'person' === $options->siteRepresents; return $is_person ? \progress_planner()->get_ui__branding()->get_url( 'https://prpl.fyi/aioseo-person-logo' ) : \progress_planner()->get_ui__branding()->get_url( 'https://prpl.fyi/aioseo-organization-logo' ); @@ -75,20 +71,14 @@ public function should_add_task() { return false; } - $options = \aioseo()->options->searchAppearance->global->schema; - - // Check if person or organization. - $is_person = isset( $options->personOrOrganization ) && 'person' === $options->personOrOrganization; + $represents = \aioseo()->options->searchAppearance->global->schema->siteRepresents; // Check if logo is already set. - if ( $is_person ) { - // Check person logo. - if ( isset( $options->person->image ) && ! empty( $options->person->image ) ) { - return false; - } + if ( $represents === 'person' ) { + return false; } else { // Check organization logo. - if ( isset( $options->organizationLogo ) && ! empty( $options->organizationLogo ) ) { + if ( \aioseo()->options->searchAppearance->global->schema->organizationLogo !== '' ) { return false; } } From c068404973c7292a1a610e0c1015ed1ac77f9269 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 1 Oct 2025 09:15:34 +0300 Subject: [PATCH 30/60] minor tweak & CS & PHPStan fixes --- .../integrations/aioseo/class-organization-logo.php | 10 +++------- phpstan.neon.dist | 6 ++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php b/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php index 3c505291e..dfc1b4e41 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php @@ -54,7 +54,7 @@ public function get_external_link_url() { } $options = \aioseo()->options->searchAppearance->global->schema; - $is_person = isset( $options->siteRepresents ) && 'person' === $options->siteRepresents; + $is_person = isset( $options->siteRepresents ) && 'person' === $options->siteRepresents; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase return $is_person ? \progress_planner()->get_ui__branding()->get_url( 'https://prpl.fyi/aioseo-person-logo' ) : \progress_planner()->get_ui__branding()->get_url( 'https://prpl.fyi/aioseo-organization-logo' ); @@ -76,14 +76,10 @@ public function should_add_task() { // Check if logo is already set. if ( $represents === 'person' ) { return false; - } else { - // Check organization logo. - if ( \aioseo()->options->searchAppearance->global->schema->organizationLogo !== '' ) { - return false; - } } - return true; + // Check organization logo. + return \aioseo()->options->searchAppearance->global->schema->organizationLogo === ''; } /** diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 0f6866622..ca24110c3 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -42,3 +42,9 @@ parameters: - classes/suggested-tasks/data-collector/class-terms-without-description.php - classes/suggested-tasks/data-collector/class-post-tag-count.php - classes/suggested-tasks/data-collector/class-post-author.php + - classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php + - classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php + - classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php + - classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php + - classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php + - classes/suggested-tasks/providers/integrations/aioseo/class-organization-logo.php From 57a4fb3e787532b17672a59942951994bdbea2d8 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Wed, 1 Oct 2025 10:08:32 +0200 Subject: [PATCH 31/60] add scroll to where possible --- .../providers/integrations/aioseo/class-archive-author.php | 2 +- .../providers/integrations/aioseo/class-archive-date.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php index 78314054c..de291fb46 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php @@ -113,7 +113,7 @@ public function is_task_relevant() { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', ]; return $actions; diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php index 4cf7c8ebc..bcee9f44b 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php @@ -99,7 +99,7 @@ public function is_task_relevant() { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', ]; return $actions; From 1c3c171e9b2c222b0722e650d7075b25386ebd9f Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Wed, 1 Oct 2025 10:08:58 +0200 Subject: [PATCH 32/60] changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4386c328f..621dea718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ = 1.9.0 = +In this release we've added an integration with the **All In One Seo** plugin so you’ll now see personalized suggestions based on your current SEO configuration. + +Added these recommendations from Ravi: + +* All In One SEO: [disable the author archive](https://prpl.fyi/aioseo-author-archive) +* All In One SEO: [disable the date archive](https://prpl.fyi/aioseo-date-archive) +* All In One SEO: [Remove post authors feeds](https://prpl.fyi/aioseo-crawl-optimization-feed-authors) +* All In One SEO: [Remove global comment feeds](https://prpl.fyi/aioseo-crawl-optimization-feed-global-comments) +* All In One SEO: [disable the media pages](https://prpl.fyi/aioseo-media-pages) +* All In One SEO: set your [organization](https://prpl.fyi/aioseo-organization-logo) or [person](https://prpl.fyi/aioseo-person-logo) logo + Enhancements: * Add "Show all Recommendations" button to the "Ravi's Recommendations" widget From 6aabb2a4bce4c6ee50cf993f99f4ca65ee0d9e5c Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Wed, 1 Oct 2025 11:25:24 +0200 Subject: [PATCH 33/60] make (some) tasks interactive --- .../aioseo-crawl-settings-feed-authors.js | 25 ++++++ ...seo-crawl-settings-feed-global-comments.js | 25 ++++++ .../class-aioseo-interactive-provider.php | 30 +++++++ .../class-crawl-settings-feed-authors.php | 82 ++++++++++++++++++- ...ss-crawl-settings-feed-global-comments.php | 82 ++++++++++++++++++- 5 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 assets/js/recommendations/aioseo-crawl-settings-feed-authors.js create mode 100644 assets/js/recommendations/aioseo-crawl-settings-feed-global-comments.js create mode 100644 classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-interactive-provider.php diff --git a/assets/js/recommendations/aioseo-crawl-settings-feed-authors.js b/assets/js/recommendations/aioseo-crawl-settings-feed-authors.js new file mode 100644 index 000000000..d2e4e6341 --- /dev/null +++ b/assets/js/recommendations/aioseo-crawl-settings-feed-authors.js @@ -0,0 +1,25 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * All in One SEO: disable author RSS feeds. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'aioseo-crawl-settings-feed-authors', + popoverId: 'prpl-popover-aioseo-crawl-settings-feed-authors', + callback: () => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_aioseo-crawl-settings-feed-authors', + nonce: progressPlanner.nonce, + disable_author_feed: true, + } ), + } ); + }, +} ); diff --git a/assets/js/recommendations/aioseo-crawl-settings-feed-global-comments.js b/assets/js/recommendations/aioseo-crawl-settings-feed-global-comments.js new file mode 100644 index 000000000..cecc008d7 --- /dev/null +++ b/assets/js/recommendations/aioseo-crawl-settings-feed-global-comments.js @@ -0,0 +1,25 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * All in One SEO: disable global comment RSS feeds. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'aioseo-crawl-settings-feed-global-comments', + popoverId: 'prpl-popover-aioseo-crawl-settings-feed-global-comments', + callback: () => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_aioseo-crawl-settings-feed-global-comments', + nonce: progressPlanner.nonce, + disable_global_comment_feed: true, + } ), + } ); + }, +} ); diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-interactive-provider.php b/classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-interactive-provider.php new file mode 100644 index 000000000..37205c680 --- /dev/null +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-aioseo-interactive-provider.php @@ -0,0 +1,30 @@ +get_data_collector()->collect() <= self::MINIMUM_AUTHOR_WITH_POSTS; } + /** + * Get the description. + * + * @return void + */ + public function print_popover_instructions() { + echo '

    '; + \esc_html_e( 'Remove URLs which provide information about recent posts by specific authors.', 'progress-planner' ); + echo '

    '; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + ?> + + \esc_html__( 'AIOSEO is not active.', 'progress-planner' ) ] ); + } + + // Check the nonce. + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['disable_author_feed'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Missing value.', 'progress-planner' ) ] ); + } + + $disable_author_feed = \sanitize_text_field( \wp_unslash( $_POST['disable_author_feed'] ) ); + + if ( empty( $disable_author_feed ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid author feed.', 'progress-planner' ) ] ); + } + + \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->authors = false; + + // Update the option. + \aioseo()->options->save(); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + /** * Add task actions specific to this task. * @@ -110,7 +188,7 @@ public function is_task_relevant() { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', ]; return $actions; diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php index 8268fdbbc..005bf9370 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php @@ -10,7 +10,7 @@ /** * Add task for All in One SEO: disable global comment RSS feeds. */ -class Crawl_Settings_Feed_Global_Comments extends AIOSEO_Provider { +class Crawl_Settings_Feed_Global_Comments extends AIOSEO_Interactive_Provider { /** * The provider ID. @@ -19,6 +19,13 @@ class Crawl_Settings_Feed_Global_Comments extends AIOSEO_Provider { */ protected const PROVIDER_ID = 'aioseo-crawl-settings-feed-global-comments'; + /** + * The popover ID. + * + * @var string + */ + const POPOVER_ID = 'aioseo-crawl-settings-feed-global-comments'; + /** * The external link URL. * @@ -26,6 +33,15 @@ class Crawl_Settings_Feed_Global_Comments extends AIOSEO_Provider { */ protected const EXTERNAL_LINK_URL = 'https://prpl.fyi/aioseo-crawl-optimization-feed-global-comments'; + /** + * Initialize the task. + * + * @return void + */ + public function init() { + \add_action( 'wp_ajax_prpl_interactive_task_submit_aioseo-crawl-settings-feed-global-comments', [ $this, 'handle_interactive_task_specific_submit' ] ); + } + /** * Get the task URL. * @@ -66,6 +82,68 @@ public function should_add_task() { return true; } + /** + * Get the description. + * + * @return void + */ + public function print_popover_instructions() { + echo '

    '; + \esc_html_e( 'Remove URLs which provide information about recent comments.', 'progress-planner' ); + echo '

    '; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + ?> + + \esc_html__( 'AIOSEO is not active.', 'progress-planner' ) ] ); + } + + // Check the nonce. + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['disable_global_comment_feed'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Missing value.', 'progress-planner' ) ] ); + } + + $disable_global_comment_feed = \sanitize_text_field( \wp_unslash( $_POST['disable_global_comment_feed'] ) ); + + if ( empty( $disable_global_comment_feed ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid global comment feed.', 'progress-planner' ) ] ); + } + + \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->globalComments = false; + + // Update the option. + \aioseo()->options->save(); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + /** * Add task actions specific to this task. * @@ -77,7 +155,7 @@ public function should_add_task() { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', ]; return $actions; From 60855669185bbdfd42fe0f45a32c0d8687836bd3 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Wed, 1 Oct 2025 12:22:31 +0200 Subject: [PATCH 34/60] update wording & convert more tasks to interactive --- CHANGELOG.md | 6 +- .../recommendations/aioseo-author-archive.js | 24 +++++++ .../aioseo-crawl-settings-feed-authors.js | 1 - ...=> aioseo-crawl-settings-feed-comments.js} | 7 +- .../js/recommendations/aioseo-date-archive.js | 24 +++++++ .../js/recommendations/aioseo-media-pages.js | 24 +++++++ .../aioseo/class-add-aioseo-providers.php | 2 +- .../aioseo/class-archive-author.php | 71 ++++++++++++++++++- .../aioseo/class-archive-date.php | 71 ++++++++++++++++++- .../class-crawl-settings-feed-authors.php | 13 +--- ...=> class-crawl-settings-feed-comments.php} | 38 +++++----- .../integrations/aioseo/class-media-pages.php | 71 ++++++++++++++++++- 12 files changed, 305 insertions(+), 47 deletions(-) create mode 100644 assets/js/recommendations/aioseo-author-archive.js rename assets/js/recommendations/{aioseo-crawl-settings-feed-global-comments.js => aioseo-crawl-settings-feed-comments.js} (74%) create mode 100644 assets/js/recommendations/aioseo-date-archive.js create mode 100644 assets/js/recommendations/aioseo-media-pages.js rename classes/suggested-tasks/providers/integrations/aioseo/{class-crawl-settings-feed-global-comments.php => class-crawl-settings-feed-comments.php} (61%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 621dea718..b8e152eb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,10 @@ In this release we've added an integration with the **All In One Seo** plugin so Added these recommendations from Ravi: -* All In One SEO: [disable the author archive](https://prpl.fyi/aioseo-author-archive) -* All In One SEO: [disable the date archive](https://prpl.fyi/aioseo-date-archive) +* All In One SEO: [noindex the author archive](https://prpl.fyi/aioseo-author-archive) +* All In One SEO: [noindex the date archive](https://prpl.fyi/aioseo-date-archive) * All In One SEO: [Remove post authors feeds](https://prpl.fyi/aioseo-crawl-optimization-feed-authors) -* All In One SEO: [Remove global comment feeds](https://prpl.fyi/aioseo-crawl-optimization-feed-global-comments) +* All In One SEO: [Remove comment feeds](https://prpl.fyi/aioseo-crawl-optimization-feed-comments) * All In One SEO: [disable the media pages](https://prpl.fyi/aioseo-media-pages) * All In One SEO: set your [organization](https://prpl.fyi/aioseo-organization-logo) or [person](https://prpl.fyi/aioseo-person-logo) logo diff --git a/assets/js/recommendations/aioseo-author-archive.js b/assets/js/recommendations/aioseo-author-archive.js new file mode 100644 index 000000000..0e5174444 --- /dev/null +++ b/assets/js/recommendations/aioseo-author-archive.js @@ -0,0 +1,24 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * All in One SEO: noindex the author archive. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'aioseo-author-archive', + popoverId: 'prpl-popover-aioseo-author-archive', + callback: () => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_aioseo-author-archive', + nonce: progressPlanner.nonce, + } ), + } ); + }, +} ); diff --git a/assets/js/recommendations/aioseo-crawl-settings-feed-authors.js b/assets/js/recommendations/aioseo-crawl-settings-feed-authors.js index d2e4e6341..a22477dce 100644 --- a/assets/js/recommendations/aioseo-crawl-settings-feed-authors.js +++ b/assets/js/recommendations/aioseo-crawl-settings-feed-authors.js @@ -18,7 +18,6 @@ prplInteractiveTaskFormListener.customSubmit( { body: new URLSearchParams( { action: 'prpl_interactive_task_submit_aioseo-crawl-settings-feed-authors', nonce: progressPlanner.nonce, - disable_author_feed: true, } ), } ); }, diff --git a/assets/js/recommendations/aioseo-crawl-settings-feed-global-comments.js b/assets/js/recommendations/aioseo-crawl-settings-feed-comments.js similarity index 74% rename from assets/js/recommendations/aioseo-crawl-settings-feed-global-comments.js rename to assets/js/recommendations/aioseo-crawl-settings-feed-comments.js index cecc008d7..147ad14a4 100644 --- a/assets/js/recommendations/aioseo-crawl-settings-feed-global-comments.js +++ b/assets/js/recommendations/aioseo-crawl-settings-feed-comments.js @@ -7,8 +7,8 @@ */ prplInteractiveTaskFormListener.customSubmit( { - taskId: 'aioseo-crawl-settings-feed-global-comments', - popoverId: 'prpl-popover-aioseo-crawl-settings-feed-global-comments', + taskId: 'aioseo-crawl-settings-feed-comments', + popoverId: 'prpl-popover-aioseo-crawl-settings-feed-comments', callback: () => { fetch( progressPlanner.ajaxUrl, { method: 'POST', @@ -16,9 +16,8 @@ prplInteractiveTaskFormListener.customSubmit( { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams( { - action: 'prpl_interactive_task_submit_aioseo-crawl-settings-feed-global-comments', + action: 'prpl_interactive_task_submit_aioseo-crawl-settings-feed-comments', nonce: progressPlanner.nonce, - disable_global_comment_feed: true, } ), } ); }, diff --git a/assets/js/recommendations/aioseo-date-archive.js b/assets/js/recommendations/aioseo-date-archive.js new file mode 100644 index 000000000..454853fa3 --- /dev/null +++ b/assets/js/recommendations/aioseo-date-archive.js @@ -0,0 +1,24 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * All in One SEO: noindex the date archive. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'aioseo-date-archive', + popoverId: 'prpl-popover-aioseo-date-archive', + callback: () => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_aioseo-date-archive', + nonce: progressPlanner.nonce, + } ), + } ); + }, +} ); diff --git a/assets/js/recommendations/aioseo-media-pages.js b/assets/js/recommendations/aioseo-media-pages.js new file mode 100644 index 000000000..727072d0b --- /dev/null +++ b/assets/js/recommendations/aioseo-media-pages.js @@ -0,0 +1,24 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * All in One SEO: redirect media pages. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'aioseo-media-pages', + popoverId: 'prpl-popover-aioseo-media-pages', + callback: () => { + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_aioseo-media-pages', + nonce: progressPlanner.nonce, + } ), + } ); + }, +} ); diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php index 4eabc077a..8b797d3bd 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-add-aioseo-providers.php @@ -40,7 +40,7 @@ public function add_providers( $providers ) { new Archive_Date(), new Media_Pages(), new Crawl_Settings_Feed_Authors(), - new Crawl_Settings_Feed_Global_Comments(), + new Crawl_Settings_Feed_Comments(), new Organization_Logo(), ]; diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php index de291fb46..d055b64ab 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-author.php @@ -12,7 +12,7 @@ /** * Add task for All in One SEO: disable the author archive. */ -class Archive_Author extends AIOSEO_Provider { +class Archive_Author extends AIOSEO_Interactive_Provider { /** * The minimum number of posts with a post format to add the task. @@ -28,6 +28,13 @@ class Archive_Author extends AIOSEO_Provider { */ protected const PROVIDER_ID = 'aioseo-author-archive'; + /** + * The popover ID. + * + * @var string + */ + const POPOVER_ID = 'aioseo-author-archive'; + /** * The data collector class name. * @@ -42,6 +49,15 @@ class Archive_Author extends AIOSEO_Provider { */ protected const EXTERNAL_LINK_URL = 'https://prpl.fyi/aioseo-author-archive'; + /** + * Initialize the task. + * + * @return void + */ + public function init() { + \add_action( 'wp_ajax_prpl_interactive_task_submit_aioseo-author-archive', [ $this, 'handle_interactive_task_specific_submit' ] ); + } + /** * Get the task URL. * @@ -102,6 +118,57 @@ public function is_task_relevant() { return $this->get_data_collector()->collect() <= self::MINIMUM_AUTHOR_WITH_POSTS; } + /** + * Get the description. + * + * @return void + */ + public function print_popover_instructions() { + echo '

    '; + \esc_html_e( 'Your author archives are the same as your normal archives because you have only one author, so there\'s no reason for search engines to index these. That\'s why we suggest keeping them out of search results.', 'progress-planner' ); + echo '

    '; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + ?> + + \esc_html__( 'AIOSEO is not active.', 'progress-planner' ) ] ); + } + + // Check the nonce. + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + \aioseo()->options->searchAppearance->archives->author->show = false; + + // Update the option. + \aioseo()->options->save(); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + /** * Add task actions specific to this task. * @@ -113,7 +180,7 @@ public function is_task_relevant() { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Noindex', 'progress-planner' ) . '', ]; return $actions; diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php index bcee9f44b..6c5471731 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-archive-date.php @@ -10,7 +10,7 @@ /** * Add task for All in One SEO: disable the date archive. */ -class Archive_Date extends AIOSEO_Provider { +class Archive_Date extends AIOSEO_Interactive_Provider { /** * The provider ID. @@ -19,6 +19,13 @@ class Archive_Date extends AIOSEO_Provider { */ protected const PROVIDER_ID = 'aioseo-date-archive'; + /** + * The popover ID. + * + * @var string + */ + const POPOVER_ID = 'aioseo-date-archive'; + /** * The external link URL. * @@ -26,6 +33,15 @@ class Archive_Date extends AIOSEO_Provider { */ protected const EXTERNAL_LINK_URL = 'https://prpl.fyi/aioseo-date-archive'; + /** + * Initialize the task. + * + * @return void + */ + public function init() { + \add_action( 'wp_ajax_prpl_interactive_task_submit_aioseo-date-archive', [ $this, 'handle_interactive_task_specific_submit' ] ); + } + /** * Get the task URL. * @@ -88,6 +104,57 @@ public function is_task_relevant() { && \strpos( $permalink_structure, '%day%' ) === false; } + /** + * Get the description. + * + * @return void + */ + public function print_popover_instructions() { + echo '

    '; + \esc_html_e( 'Date archives rarely add any real value for users or search engines, so there\'s no reason for search engines to index these. That\'s why we suggest keeping them out of search results.', 'progress-planner' ); + echo '

    '; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + ?> + + \esc_html__( 'AIOSEO is not active.', 'progress-planner' ) ] ); + } + + // Check the nonce. + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + \aioseo()->options->searchAppearance->archives->date->show = false; + + // Update the option. + \aioseo()->options->save(); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + /** * Add task actions specific to this task. * @@ -99,7 +166,7 @@ public function is_task_relevant() { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '' . \esc_html__( 'Disable', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Noindex', 'progress-planner' ) . '', ]; return $actions; diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php index 506f46e5e..cd9b35e2c 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-authors.php @@ -122,7 +122,7 @@ public function is_task_relevant() { */ public function print_popover_instructions() { echo '

    '; - \esc_html_e( 'Remove URLs which provide information about recent posts by specific authors.', 'progress-planner' ); + \esc_html_e( 'The author feed on your site will be similar to your main feed if you have only one author, so there\'s no reason to have it.', 'progress-planner' ); echo '

    '; } @@ -144,7 +144,6 @@ public function print_popover_form_contents() { * * This is only for interactive tasks that change non-core settings. * The $_POST data is expected to be: - * - disable_author_feed: (boolean) Just a boolean. * - nonce: (string) The nonce. * * @return void @@ -159,16 +158,6 @@ public function handle_interactive_task_specific_submit() { \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); } - if ( ! isset( $_POST['disable_author_feed'] ) ) { - \wp_send_json_error( [ 'message' => \esc_html__( 'Missing value.', 'progress-planner' ) ] ); - } - - $disable_author_feed = \sanitize_text_field( \wp_unslash( $_POST['disable_author_feed'] ) ); - - if ( empty( $disable_author_feed ) ) { - \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid author feed.', 'progress-planner' ) ] ); - } - \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->authors = false; // Update the option. diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-comments.php similarity index 61% rename from classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php rename to classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-comments.php index 005bf9370..aae197d54 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-global-comments.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-crawl-settings-feed-comments.php @@ -10,28 +10,28 @@ /** * Add task for All in One SEO: disable global comment RSS feeds. */ -class Crawl_Settings_Feed_Global_Comments extends AIOSEO_Interactive_Provider { +class Crawl_Settings_Feed_Comments extends AIOSEO_Interactive_Provider { /** * The provider ID. * * @var string */ - protected const PROVIDER_ID = 'aioseo-crawl-settings-feed-global-comments'; + protected const PROVIDER_ID = 'aioseo-crawl-settings-feed-comments'; /** * The popover ID. * * @var string */ - const POPOVER_ID = 'aioseo-crawl-settings-feed-global-comments'; + const POPOVER_ID = 'aioseo-crawl-settings-feed-comments'; /** * The external link URL. * * @var string */ - protected const EXTERNAL_LINK_URL = 'https://prpl.fyi/aioseo-crawl-optimization-feed-global-comments'; + protected const EXTERNAL_LINK_URL = 'https://prpl.fyi/aioseo-crawl-optimization-feed-comments'; /** * Initialize the task. @@ -39,7 +39,7 @@ class Crawl_Settings_Feed_Global_Comments extends AIOSEO_Interactive_Provider { * @return void */ public function init() { - \add_action( 'wp_ajax_prpl_interactive_task_submit_aioseo-crawl-settings-feed-global-comments', [ $this, 'handle_interactive_task_specific_submit' ] ); + \add_action( 'wp_ajax_prpl_interactive_task_submit_aioseo-crawl-settings-feed-comments', [ $this, 'handle_interactive_task_specific_submit' ] ); } /** @@ -57,7 +57,7 @@ protected function get_url() { * @return string */ protected function get_title() { - return \esc_html__( 'All in One SEO: disable global comment RSS feeds', 'progress-planner' ); + return \esc_html__( 'All in One SEO: disable comment RSS feeds', 'progress-planner' ); } /** @@ -72,10 +72,11 @@ public function should_add_task() { } // Check if crawl cleanup is enabled and comment feeds are disabled. - $disable_comment_feed = \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->globalComments; + $disable_global_comment_feed = \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->globalComments; // @phpstan-ignore-line + $disable_post_comment_feed = \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->postComments; // @phpstan-ignore-line // Check if comment feeds are already disabled. - if ( $disable_comment_feed === false ) { + if ( $disable_global_comment_feed === false && $disable_post_comment_feed === false ) { return false; } @@ -89,7 +90,7 @@ public function should_add_task() { */ public function print_popover_instructions() { echo '

    '; - \esc_html_e( 'Remove URLs which provide information about recent comments.', 'progress-planner' ); + \esc_html_e( 'We suggest disabling both the global "recent comments feed" from your site as well as the "comments feed" per post that WordPress generates. These feeds are rarely used by real users, but get crawled a lot. They don\'t have any interesting information for crawlers, so removing them leads to less bot-traffic on your site without downsides.', 'progress-planner' ); echo '

    '; } @@ -101,7 +102,7 @@ public function print_popover_instructions() { public function print_popover_form_contents() { ?> \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); } - if ( ! isset( $_POST['disable_global_comment_feed'] ) ) { - \wp_send_json_error( [ 'message' => \esc_html__( 'Missing value.', 'progress-planner' ) ] ); + // Global comment feed. + if ( \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->globalComments ) { // @phpstan-ignore-line + \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->globalComments = false; // @phpstan-ignore-line } - $disable_global_comment_feed = \sanitize_text_field( \wp_unslash( $_POST['disable_global_comment_feed'] ) ); - - if ( empty( $disable_global_comment_feed ) ) { - \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid global comment feed.', 'progress-planner' ) ] ); + // Post comment feed. + if ( \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->postComments ) { // @phpstan-ignore-line + \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->postComments = false; // @phpstan-ignore-line } - \aioseo()->options->searchAppearance->advanced->crawlCleanup->feeds->globalComments = false; - // Update the option. - \aioseo()->options->save(); + \aioseo()->options->save(); // @phpstan-ignore-line \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); } diff --git a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php index 24d8d4d49..c4a445c42 100644 --- a/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php +++ b/classes/suggested-tasks/providers/integrations/aioseo/class-media-pages.php @@ -10,7 +10,7 @@ /** * Add task for All in One SEO: redirect media/attachment pages. */ -class Media_Pages extends AIOSEO_Provider { +class Media_Pages extends AIOSEO_Interactive_Provider { /** * The provider ID. @@ -19,6 +19,13 @@ class Media_Pages extends AIOSEO_Provider { */ protected const PROVIDER_ID = 'aioseo-media-pages'; + /** + * The popover ID. + * + * @var string + */ + const POPOVER_ID = 'aioseo-media-pages'; + /** * The external link URL. * @@ -26,6 +33,15 @@ class Media_Pages extends AIOSEO_Provider { */ protected const EXTERNAL_LINK_URL = 'https://prpl.fyi/aioseo-media-pages'; + /** + * Initialize the task. + * + * @return void + */ + public function init() { + \add_action( 'wp_ajax_prpl_interactive_task_submit_aioseo-media-pages', [ $this, 'handle_interactive_task_specific_submit' ] ); + } + /** * Get the task URL. * @@ -70,6 +86,57 @@ public function should_add_task() { return true; } + /** + * Get the description. + * + * @return void + */ + public function print_popover_instructions() { + echo '

    '; + \esc_html_e( 'WordPress creates a "page" for every image you upload. These don\'t add any value but do cause more crawling on your site, so we suggest removing those.', 'progress-planner' ); + echo '

    '; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + ?> + + \esc_html__( 'AIOSEO is not active.', 'progress-planner' ) ] ); + } + + // Check the nonce. + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + \aioseo()->dynamicOptions->searchAppearance->postTypes->attachment->redirectAttachmentUrls = 'attachment'; + + // Update the option. + \aioseo()->options->save(); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + /** * Add task actions specific to this task. * @@ -81,7 +148,7 @@ public function should_add_task() { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '' . \esc_html__( 'Configure redirect', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Redirect', 'progress-planner' ) . '', ]; return $actions; From 5a0eddfa50f8731ab6910c0b054c55f76e09c32e Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 2 Oct 2025 14:45:05 +0200 Subject: [PATCH 35/60] rename method --- classes/class-suggested-tasks.php | 4 ++-- classes/front-end/class-front-end-onboarding.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/class-suggested-tasks.php b/classes/class-suggested-tasks.php index d0879a9ba..fb72ebe9a 100644 --- a/classes/class-suggested-tasks.php +++ b/classes/class-suggested-tasks.php @@ -200,7 +200,7 @@ public function maybe_complete_task() { return; } - $this->complete_task( $task_id ); + $this->mark_task_as_completed( $task_id ); } /** @@ -210,7 +210,7 @@ public function maybe_complete_task() { * * @return bool */ - public function complete_task( $task_id ) { + public function mark_task_as_completed( $task_id ) { if ( ! $this->was_task_completed( $task_id ) ) { $task = \progress_planner()->get_suggested_tasks_db()->get_post( $task_id ); diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index d0598170d..ac7fb216b 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -167,7 +167,7 @@ public function ajax_complete_task() { // Note: Completing task will set it it to pending, so user will get celebration. // Do we want that? - $result = \progress_planner()->get_suggested_tasks()->complete_task( $task_id ); + $result = \progress_planner()->get_suggested_tasks()->mark_task_as_completed( $task_id ); if ( ! $result ) { \error_log( 'Task not completed: ' . $task_id ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log From bdfc8680e9b3bd1e856c050691d31a3c04f4a6c2 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 2 Oct 2025 15:02:22 +0200 Subject: [PATCH 36/60] update comment --- classes/front-end/class-front-end-onboarding.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index ac7fb216b..7fd8476d1 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -165,8 +165,7 @@ public function ajax_complete_task() { $form_values = \json_decode( $form_values, true ); } - // Note: Completing task will set it it to pending, so user will get celebration. - // Do we want that? + // Note: Marking task as completed will set it it to pending, so user will get celebration. Do we want that? $result = \progress_planner()->get_suggested_tasks()->mark_task_as_completed( $task_id ); if ( ! $result ) { From 5a544f353093fb8262f873ec6dae7ac597e7af09 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 2 Oct 2025 15:03:19 +0200 Subject: [PATCH 37/60] WIP: prototype of how task can be programmatically completed --- .../front-end/class-front-end-onboarding.php | 15 +++++++++++++++ .../suggested-tasks/class-tasks-interface.php | 10 ++++++++++ .../providers/class-blog-description.php | 17 +++++++++++++++++ .../suggested-tasks/providers/class-tasks.php | 11 +++++++++++ 4 files changed, 53 insertions(+) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 7fd8476d1..2e64b3a12 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -165,6 +165,21 @@ public function ajax_complete_task() { $form_values = \json_decode( $form_values, true ); } + // Get the task. + $task = \progress_planner()->get_suggested_tasks_db()->get_post( $task_id ); + if ( ! $task ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Task not found.', 'progress-planner' ) ] ); + } + + // To get the provider and complete the task, we need to use the provider. + $provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( $task->get_provider_id() ); + if ( ! $provider ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Provider not found.', 'progress-planner' ) ] ); + } + + // Complete the task. + $provider->complete_task( $form_values, $task_id ); + // Note: Marking task as completed will set it it to pending, so user will get celebration. Do we want that? $result = \progress_planner()->get_suggested_tasks()->mark_task_as_completed( $task_id ); diff --git a/classes/suggested-tasks/class-tasks-interface.php b/classes/suggested-tasks/class-tasks-interface.php index 66c550b22..f93a1b9f6 100644 --- a/classes/suggested-tasks/class-tasks-interface.php +++ b/classes/suggested-tasks/class-tasks-interface.php @@ -129,4 +129,14 @@ public function add_task_actions( $data = [], $actions = [] ); * @return bool */ public function task_has_activity( $task_id = '' ); + + /** + * Complete the task. + * + * @param array $args The task data. + * @param string $task_id The task ID. + * + * @return void + */ + public function complete_task( $args = [], $task_id = '' ); } diff --git a/classes/suggested-tasks/providers/class-blog-description.php b/classes/suggested-tasks/providers/class-blog-description.php index fce46d6dc..2dbc8fa93 100644 --- a/classes/suggested-tasks/providers/class-blog-description.php +++ b/classes/suggested-tasks/providers/class-blog-description.php @@ -139,4 +139,21 @@ public function add_task_actions( $data = [], $actions = [] ) { return $actions; } + + /** + * Complete the task. + * + * @param array $args The task data. + * @param string $task_id The task ID. + * + * @return void + */ + public function complete_task( $args = [], $task_id = '' ) { + + if ( ! isset( $args['blogdescription'] ) ) { + return; + } + + \update_option( 'blogdescription', \sanitize_text_field( $args['blogdescription'] ) ); + } } diff --git a/classes/suggested-tasks/providers/class-tasks.php b/classes/suggested-tasks/providers/class-tasks.php index 00b18bbb6..5bc8d239a 100644 --- a/classes/suggested-tasks/providers/class-tasks.php +++ b/classes/suggested-tasks/providers/class-tasks.php @@ -721,4 +721,15 @@ public function task_has_activity( $task_id = '' ) { return ! empty( $activity ); } + + /** + * Complete the task. + * + * @param array $args The task data. + * @param string $task_id The task ID. + * + * @return void + */ + public function complete_task( $args = [], $task_id = '' ) { + } } From 4302f41d55b107849bf941ca2dfd940518dd993f Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 11:39:04 +0200 Subject: [PATCH 38/60] blog description as first task --- .../front-end/class-front-end-onboarding.php | 36 +++++++++++++++++-- views/front-end-onboarding/first-task.php | 10 ++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 2e64b3a12..c2ad0bc7a 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -221,6 +221,34 @@ public function add_popover() { * @return void */ public function add_popover_step_templates() { + + // First task. + $first_task = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'task_id' => 'core-blogdescription' ] ); + + // If there is no 'blog description' task, create it. + if ( ! $first_task ) { + $first_task_provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( 'core-blogdescription' ); + + if ( $first_task_provider ) { + $first_task_data = $first_task_provider->get_task_details(); + + \progress_planner()->get_suggested_tasks_db()->add( $first_task_data ); + + // Now get the task. + $first_task = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'task_id' => 'core-blogdescription' ] ); + } + } + + $first_task = [ + 'task_id' => $first_task[0]->task_id, + 'title' => $first_task[0]->post_title, + 'url' => $first_task[0]->url, + 'provider_id' => $first_task[0]->get_provider_id(), + 'points' => $first_task[0]->points, + 'site_description' => \get_bloginfo( 'description' ), + ]; + + // Other tasks. $ravis_recommendations = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'post_status' => 'publish', @@ -235,7 +263,8 @@ public function add_popover_step_templates() { ], ] ); - $tasks = []; + + $tasks = []; foreach ( $ravis_recommendations as $recommendation ) { $tasks[] = [ @@ -246,10 +275,11 @@ public function add_popover_step_templates() { 'points' => $recommendation->points, ]; } + \progress_planner()->the_view( 'front-end-onboarding/welcome.php' ); - \progress_planner()->the_view( 'front-end-onboarding/first-task.php', [ 'task' => $tasks[0] ] ); // WIP: We need only 1 task for this step. + \progress_planner()->the_view( 'front-end-onboarding/first-task.php', [ 'task' => $first_task ] ); // WIP: We need only 1 task for this step. \progress_planner()->the_view( 'front-end-onboarding/badges.php' ); - \progress_planner()->the_view( 'front-end-onboarding/more-tasks.php', [ 'tasks' => array_slice( $tasks, 1, 5 ) ] ); // WIP: We need up to 5 tasks for this step. + \progress_planner()->the_view( 'front-end-onboarding/more-tasks.php', [ 'tasks' => $tasks ] ); // WIP: We need up to 5 tasks for this step. \progress_planner()->the_view( 'front-end-onboarding/finish.php' ); ?> + + + + diff --git a/views/front-end-onboarding/tasks/blog-description.php b/views/front-end-onboarding/tasks/blog-description.php new file mode 100644 index 000000000..16749e578 --- /dev/null +++ b/views/front-end-onboarding/tasks/blog-description.php @@ -0,0 +1,37 @@ + + +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    + + + + +
    diff --git a/views/front-end-onboarding/tasks/select-locale.php b/views/front-end-onboarding/tasks/select-locale.php new file mode 100644 index 000000000..f7e8c5061 --- /dev/null +++ b/views/front-end-onboarding/tasks/select-locale.php @@ -0,0 +1,61 @@ + + +
    +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    +
    + 'language', + 'id' => 'language', + 'selected' => $prpl_locale, + 'languages' => $prpl_languages, + 'translations' => $prpl_translations, + 'show_available_translations' => \current_user_can( 'install_languages' ) && \wp_can_install_language_pack(), + ] + ); + ?> + +
    +
    +
    diff --git a/views/front-end-onboarding/tasks/select-timezone.php b/views/front-end-onboarding/tasks/select-timezone.php new file mode 100644 index 000000000..1a151a640 --- /dev/null +++ b/views/front-end-onboarding/tasks/select-timezone.php @@ -0,0 +1,41 @@ + + +
    +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    +
    + + +
    +
    +
    From e8d945f1bddab84458fd767e228813de297bc7d9 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 16:46:28 +0200 Subject: [PATCH 40/60] secondary button styling --- assets/css/front-end-onboarding.css | 11 +++++++++++ classes/front-end/class-front-end-onboarding.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/assets/css/front-end-onboarding.css b/assets/css/front-end-onboarding.css index d5ac31d6b..8591c325c 100644 --- a/assets/css/front-end-onboarding.css +++ b/assets/css/front-end-onboarding.css @@ -111,6 +111,17 @@ &:not([disabled]):focus { background: #cf2441; } + + &.prpl-btn-secondary { + background: #f9b23c; + color: #4b5563; + + &:not([disabled]):hover, + &:not([disabled]):focus { + background: #f9b23c; + color: #4b5563; + } + } } .prpl-complete-task-btn:not(.prpl-btn) { diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 6c068b914..a94cb03df 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -208,7 +208,7 @@ public function add_popover() { From 2440fd23f025373a6215a028d1d5c638f3c10d2a Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 16:47:47 +0200 Subject: [PATCH 41/60] add permission check --- classes/front-end/class-front-end-onboarding.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index a94cb03df..dca0019eb 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -146,6 +146,10 @@ public function ajax_save_tour_progress() { */ public function ajax_complete_task() { + if ( ! \current_user_can( 'manage_options' ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'You do not have permission to complete this task.', 'progress-planner' ) ] ); + } + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); } From 090761c8007f82e75e7160b592a78fc592a0341c Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 17:35:51 +0200 Subject: [PATCH 42/60] wip styling for + diff --git a/views/front-end-onboarding/tasks/core-siteicon.php b/views/front-end-onboarding/tasks/core-siteicon.php new file mode 100644 index 000000000..cc2e5c8ac --- /dev/null +++ b/views/front-end-onboarding/tasks/core-siteicon.php @@ -0,0 +1,50 @@ + +
    +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    +
    + +
    +

    + ', + '' + ); + ?> +

    + +
    +
    + +
    +
    +
    diff --git a/views/front-end-onboarding/tasks/select-locale.php b/views/front-end-onboarding/tasks/select-locale.php index f2b361fec..65a1ed17a 100644 --- a/views/front-end-onboarding/tasks/select-locale.php +++ b/views/front-end-onboarding/tasks/select-locale.php @@ -44,8 +44,8 @@
    'language', 'id' => 'language', @@ -56,7 +56,6 @@ 'echo' => false, ] ); - $prpl_dropdown_html = ob_get_clean(); // Add data-validate attribute to the select element. $prpl_dropdown_html = str_replace( From 566b1db6afd9def8771d1d420f44de703d0f2473 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Tue, 7 Oct 2025 16:57:55 +0200 Subject: [PATCH 47/60] small tweak --- views/front-end-onboarding/tasks/core-siteicon.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views/front-end-onboarding/tasks/core-siteicon.php b/views/front-end-onboarding/tasks/core-siteicon.php index cc2e5c8ac..57f40ffb8 100644 --- a/views/front-end-onboarding/tasks/core-siteicon.php +++ b/views/front-end-onboarding/tasks/core-siteicon.php @@ -34,16 +34,16 @@ printf( // translators: %1$s is opening label tag, %2$s is the closing label tag. \esc_html__( 'Drag & drop a file here, or %1$s browse %2$s', 'progress-planner' ), - '