Skip to content

Commit d4120fd

Browse files
authored
Merge pull request #676 from ProgressPlanner/ari/add-recommendation/install-seo-plugin
Add a new recommendation to install an SEO plugin
2 parents 840eb01 + 1de7e1c commit d4120fd

5 files changed

Lines changed: 312 additions & 0 deletions

File tree

classes/suggested-tasks/class-tasks-manager.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use Progress_Planner\Suggested_Tasks\Providers\Collaborator;
3737
use Progress_Planner\Suggested_Tasks\Providers\Select_Timezone;
3838
use Progress_Planner\Suggested_Tasks\Providers\Set_Date_Format;
39+
use Progress_Planner\Suggested_Tasks\Providers\SEO_Plugin;
3940

4041
/**
4142
* Tasks_Manager class.
@@ -82,6 +83,7 @@ public function __construct() {
8283
new Collaborator(),
8384
new Select_Timezone(),
8485
new Set_Date_Format(),
86+
new SEO_Plugin(),
8587
];
8688

8789
// Add the plugin integration.

classes/suggested-tasks/data-collector/class-data-collector-manager.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Progress_Planner\Suggested_Tasks\Data_Collector\Published_Post_Count;
2121
use Progress_Planner\Suggested_Tasks\Data_Collector\Yoast_Orphaned_Content;
2222
use Progress_Planner\Suggested_Tasks\Data_Collector\Unpublished_Content;
23+
use Progress_Planner\Suggested_Tasks\Data_Collector\SEO_Plugin;
2324

2425
/**
2526
* Base data collector.
@@ -52,6 +53,7 @@ public function __construct() {
5253
new Post_Tag_Count(),
5354
new Published_Post_Count(),
5455
new Unpublished_Content(),
56+
new SEO_Plugin(),
5557
];
5658

5759
// Add the plugin integration.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
/**
3+
* Data collector for SEO plugin detection.
4+
*
5+
* @package Progress_Planner
6+
*/
7+
8+
namespace Progress_Planner\Suggested_Tasks\Data_Collector;
9+
10+
/**
11+
* Data collector for SEO plugin detection.
12+
*/
13+
class SEO_Plugin extends Base_Data_Collector {
14+
15+
/**
16+
* The data key.
17+
*
18+
* @var string
19+
*/
20+
protected const DATA_KEY = 'seo_plugin_installed';
21+
22+
/**
23+
* List of popular SEO plugins to check.
24+
*
25+
* @var array<string, array<string, mixed>>
26+
*/
27+
private $seo_plugins = [
28+
'wordpress-seo' => [
29+
'name' => 'Yoast SEO',
30+
'slug' => 'wordpress-seo',
31+
'constants' => [ 'WPSEO_VERSION', 'WPSEO_FILE' ],
32+
'classes' => [ 'WPSEO_Options', 'Yoast\WP\SEO\Main' ],
33+
],
34+
'seo-by-rank-math' => [
35+
'name' => 'Rank Math SEO',
36+
'slug' => 'seo-by-rank-math',
37+
'constants' => [ 'RANK_MATH_VERSION', 'RANK_MATH_FILE' ],
38+
'classes' => [ 'RankMath', 'RankMathPro\Plugin' ],
39+
],
40+
'all-in-one-seo-pack' => [
41+
'name' => 'All in One SEO',
42+
'slug' => 'all-in-one-seo-pack',
43+
'constants' => [ 'AIOSEO_VERSION', 'AIOSEO_FILE' ],
44+
'classes' => [ 'AIOSEO\Plugin\AIOSEO', 'AIOSEOPro\Plugin\AIOSEO' ],
45+
],
46+
];
47+
48+
/**
49+
* Get the list of SEO plugins.
50+
*
51+
* @return array<string, array<string, mixed>>
52+
*/
53+
public function get_seo_plugins() {
54+
return $this->seo_plugins;
55+
}
56+
57+
/**
58+
* Calculate data.
59+
*
60+
* @return bool Returns false if no SEO plugin is detected, true otherwise.
61+
*/
62+
protected function calculate_data() {
63+
// Check each SEO plugin.
64+
foreach ( $this->seo_plugins as $plugin_data ) {
65+
// First, check if the plugin is activated by slug.
66+
if ( \progress_planner()->get_plugin_installer()->is_plugin_activated( $plugin_data['slug'] ) ) {
67+
return true;
68+
}
69+
70+
// Fallback: Check for plugin constants.
71+
if ( ! empty( $plugin_data['constants'] ) ) {
72+
foreach ( $plugin_data['constants'] as $constant ) {
73+
if ( \defined( $constant ) ) {
74+
return true;
75+
}
76+
}
77+
}
78+
79+
// Fallback: Check for plugin classes.
80+
if ( ! empty( $plugin_data['classes'] ) ) {
81+
foreach ( $plugin_data['classes'] as $class ) {
82+
if ( \class_exists( $class ) ) {
83+
return true;
84+
}
85+
}
86+
}
87+
}
88+
89+
return false;
90+
}
91+
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
<?php
2+
/**
3+
* Add task to install and activate an SEO plugin.
4+
*
5+
* @package Progress_Planner
6+
*/
7+
8+
namespace Progress_Planner\Suggested_Tasks\Providers;
9+
10+
use Progress_Planner\Suggested_Tasks\Data_Collector\SEO_Plugin as SEO_Plugin_Data_Collector;
11+
12+
/**
13+
* Add task to install and activate an SEO plugin.
14+
*/
15+
class SEO_Plugin extends Tasks_Interactive {
16+
17+
/**
18+
* Whether the task is an onboarding task.
19+
*
20+
* @var bool
21+
*/
22+
protected const IS_ONBOARDING_TASK = true;
23+
24+
/**
25+
* The provider ID.
26+
*
27+
* @var string
28+
*/
29+
protected const PROVIDER_ID = 'seo-plugin';
30+
31+
/**
32+
* The popover ID.
33+
*
34+
* @var string
35+
*/
36+
const POPOVER_ID = 'seo-plugin';
37+
38+
/**
39+
* The data collector class name.
40+
*
41+
* @var string
42+
*/
43+
protected const DATA_COLLECTOR_CLASS = SEO_Plugin_Data_Collector::class;
44+
45+
/**
46+
* The external link URL.
47+
*
48+
* @var string
49+
*/
50+
protected const EXTERNAL_LINK_URL = 'https://prpl.fyi/install-seo-plugin';
51+
52+
/**
53+
* The priority of the task.
54+
*
55+
* @var int
56+
*/
57+
protected $priority = self::PRIORITY_HIGH;
58+
59+
/**
60+
* Get the task URL.
61+
*
62+
* @return string
63+
*/
64+
protected function get_url() {
65+
return \admin_url( 'plugins.php' );
66+
}
67+
68+
/**
69+
* Get the task title.
70+
*
71+
* @return string
72+
*/
73+
protected function get_title() {
74+
return \esc_html__( 'Install an SEO plugin', 'progress-planner' );
75+
}
76+
77+
/**
78+
* Check if the task should be added.
79+
*
80+
* @return bool
81+
*/
82+
public function should_add_task() {
83+
// Only add the task if no SEO plugin is detected.
84+
return ! $this->get_data_collector()->collect();
85+
}
86+
87+
/**
88+
* Check if the task is completed.
89+
*
90+
* @param string $task_id The task ID.
91+
*
92+
* @return bool
93+
*/
94+
public function is_task_completed( $task_id = '' ) {
95+
// Task is completed if an SEO plugin is detected.
96+
return $this->get_data_collector()->collect();
97+
}
98+
99+
/**
100+
* Get the popover instructions.
101+
*
102+
* @return void
103+
*/
104+
public function print_popover_instructions() {
105+
$seo_plugin_recommendation_slug = \progress_planner()->get_ui__branding()->get_seo_plugin_recommendation_slug();
106+
$seo_plugin_recommendation_name = $this->get_plugin_name_by_slug( $seo_plugin_recommendation_slug );
107+
if ( ! $seo_plugin_recommendation_name ) {
108+
return;
109+
}
110+
?>
111+
<p>
112+
<?php \esc_html_e( 'Search Engine Optimization (SEO) is essential for improving your website\'s visibility in search engines like Google. An SEO plugin helps you optimize your content, manage meta tags, generate sitemaps, and follow best practices to rank higher in search results.', 'progress-planner' ); ?>
113+
</p>
114+
<p>
115+
<?php
116+
printf(
117+
/* translators: %s is the plugin name */
118+
\esc_html__( 'We recommend installing %s, one of the most popular and comprehensive SEO plugins for WordPress.', 'progress-planner' ),
119+
sprintf(
120+
'<a href="https://w.org/plugins/%1$s/" target="_blank">%2$s</a>',
121+
\esc_attr( $seo_plugin_recommendation_slug ),
122+
\esc_html( $seo_plugin_recommendation_name )
123+
)
124+
);
125+
?>
126+
</p>
127+
<?php
128+
}
129+
130+
/**
131+
* Print the popover input field for the form.
132+
*
133+
* @return void
134+
*/
135+
public function print_popover_form_contents() {
136+
$seo_plugin_recommendation_slug = \progress_planner()->get_ui__branding()->get_seo_plugin_recommendation_slug();
137+
$seo_plugin_recommendation_name = $this->get_plugin_name_by_slug( $seo_plugin_recommendation_slug );
138+
if ( ! $seo_plugin_recommendation_name ) {
139+
return;
140+
}
141+
142+
?>
143+
<?php if ( ! \is_multisite() && \current_user_can( 'install_plugins' ) ) : ?>
144+
<prpl-install-plugin
145+
data-plugin-name="<?php echo \esc_html( $seo_plugin_recommendation_name ); ?>"
146+
data-plugin-slug="<?php echo \esc_attr( $seo_plugin_recommendation_slug ); ?>"
147+
data-action="<?php echo \progress_planner()->get_plugin_installer()->is_plugin_installed( $seo_plugin_recommendation_slug ) ? 'activate' : 'install'; ?>"
148+
data-provider-id="<?php echo \esc_attr( self::PROVIDER_ID ); ?>"
149+
></prpl-install-plugin>
150+
<?php else : ?>
151+
<p>
152+
<?php
153+
if ( \is_multisite() ) {
154+
\esc_html_e( 'Plugin installation is disabled on this multisite installation.', 'progress-planner' );
155+
} else {
156+
\esc_html_e( 'You do not have permission to install plugins.', 'progress-planner' );
157+
}
158+
?>
159+
</p>
160+
<p>
161+
<a href="<?php echo \esc_url( \admin_url( 'plugins.php' ) ); ?>" class="prpl-button prpl-button-primary">
162+
<?php \esc_html_e( 'Go to Plugins', 'progress-planner' ); ?>
163+
</a>
164+
</p>
165+
<?php endif; ?>
166+
<?php
167+
}
168+
169+
/**
170+
* Get plugin name from the data collector by slug.
171+
*
172+
* @param string $slug The plugin slug.
173+
*
174+
* @return string|null Plugin name or null if not found.
175+
*/
176+
public function get_plugin_name_by_slug( $slug ) {
177+
$data_collector = $this->get_data_collector();
178+
if ( ! $data_collector instanceof SEO_Plugin_Data_Collector ) {
179+
return null;
180+
}
181+
182+
$seo_plugins = $data_collector->get_seo_plugins();
183+
if ( ! isset( $seo_plugins[ $slug ]['name'] ) ) {
184+
return null;
185+
}
186+
187+
return $seo_plugins[ $slug ]['name'];
188+
}
189+
190+
/**
191+
* Add task actions specific to this task.
192+
*
193+
* @param array $data The task data.
194+
* @param array $actions The existing actions.
195+
*
196+
* @return array
197+
*/
198+
public function add_task_actions( $data = [], $actions = [] ) {
199+
$actions[] = [
200+
'priority' => 10,
201+
'html' => '<a href="#" class="prpl-tooltip-action-text" role="button" onclick="document.getElementById(\'prpl-popover-' . \esc_attr( static::POPOVER_ID ) . '\')?.showPopover()">' . \esc_html__( 'Install plugin', 'progress-planner' ) . '</a>',
202+
];
203+
204+
return $actions;
205+
}
206+
}

classes/ui/class-branding.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,15 @@ public function get_url( $default_url = '' ) {
292292

293293
return $default_url;
294294
}
295+
296+
/**
297+
* Get the recommended SEO plugin slug.
298+
*
299+
* @return string
300+
*/
301+
public function get_seo_plugin_recommendation_slug(): string {
302+
return empty( $this->get_api_data() ) || ! isset( $this->get_api_data()['seo_plugin_recommendation_slug'] )
303+
? 'wordpress-seo'
304+
: $this->get_api_data()['seo_plugin_recommendation_slug'];
305+
}
295306
}

0 commit comments

Comments
 (0)