+ 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.
+
+ 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, 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.
+
+ 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.
+
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.
+