PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.3
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.3
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / integrations / admin / workouts-integration.php

workouts-integration.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.3, at src/integrations/admin/workouts-integration.php

339 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yoast\WP\SEO\Integrations\Admin;
4
5 use WPSEO_Addon_Manager;
6 use WPSEO_Admin_Asset_Manager;
7 use Yoast\WP\SEO\Conditionals\Admin_Conditional;
8 use Yoast\WP\SEO\Helpers\Options_Helper;
9 use Yoast\WP\SEO\Helpers\Product_Helper;
10 use Yoast\WP\SEO\Integrations\Integration_Interface;
11 use Yoast\WP\SEO\Presenters\Admin\Notice_Presenter;
12
13 /**
14 * WorkoutsIntegration class
15 */
16 class Workouts_Integration implements Integration_Interface {
17
18 /**
19 * The admin asset manager.
20 *
21 * @var WPSEO_Admin_Asset_Manager
22 */
23 private $admin_asset_manager;
24
25 /**
26 * The addon manager.
27 *
28 * @var WPSEO_Addon_Manager
29 */
30 private $addon_manager;
31
32 /**
33 * The options helper.
34 *
35 * @var Options_Helper
36 */
37 private $options_helper;
38
39 /**
40 * The product helper.
41 *
42 * @var Product_Helper
43 */
44 private $product_helper;
45
46 /**
47 * {@inheritDoc}
48 */
49 public static function get_conditionals() {
50 return [ Admin_Conditional::class ];
51 }
52
53 /**
54 * Workouts_Integration constructor.
55 *
56 * @param WPSEO_Addon_Manager $addon_manager The addon manager.
57 * @param WPSEO_Admin_Asset_Manager $admin_asset_manager The admin asset manager.
58 * @param Options_Helper $options_helper The options helper.
59 * @param Product_Helper $product_helper The product helper.
60 */
61 public function __construct(
62 WPSEO_Addon_Manager $addon_manager,
63 WPSEO_Admin_Asset_Manager $admin_asset_manager,
64 Options_Helper $options_helper,
65 Product_Helper $product_helper
66 ) {
67 $this->addon_manager = $addon_manager;
68 $this->admin_asset_manager = $admin_asset_manager;
69 $this->options_helper = $options_helper;
70 $this->product_helper = $product_helper;
71 }
72
73 /**
74 * {@inheritDoc}
75 */
76 public function register_hooks() {
77 \add_filter( 'wpseo_submenu_pages', [ $this, 'add_submenu_page' ], 8 );
78 \add_filter( 'wpseo_submenu_pages', [ $this, 'remove_old_submenu_page' ], 10 );
79 \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ], 11 );
80 }
81
82 /**
83 * Adds the workouts submenu page.
84 *
85 * @param array $submenu_pages The Yoast SEO submenu pages.
86 *
87 * @return array The filtered submenu pages.
88 */
89 public function add_submenu_page( $submenu_pages ) {
90 $submenu_pages[] = [
91 'wpseo_dashboard',
92 '',
93 \__( 'Workouts', 'wordpress-seo' ) . ' <span class="yoast-badge yoast-premium-badge"></span>',
94 'edit_others_posts',
95 'wpseo_workouts',
96 [ $this, 'render_target' ],
97 ];
98
99 return $submenu_pages;
100 }
101
102 /**
103 * Removes the workouts submenu page from older Premium versions
104 *
105 * @param array $submenu_pages The Yoast SEO submenu pages.
106 *
107 * @return array The filtered submenu pages.
108 */
109 public function remove_old_submenu_page( $submenu_pages ) {
110 if ( ! $this->should_update_premium() ) {
111 return $submenu_pages;
112 }
113
114 // Copy only the Workouts page item that comes first in the array.
115 $result_submenu_pages = [];
116 $workouts_page_encountered = false;
117 foreach ( $submenu_pages as $item ) {
118 if ( $item[4] !== 'wpseo_workouts' || ! $workouts_page_encountered ) {
119 $result_submenu_pages[] = $item;
120 }
121 if ( $item[4] === 'wpseo_workouts' ) {
122 $workouts_page_encountered = true;
123 }
124 }
125
126 return $result_submenu_pages;
127 }
128
129 /**
130 * Enqueue the workouts app.
131 *
132 * @return void
133 */
134 public function enqueue_assets() {
135 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Date is not processed or saved.
136 if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'wpseo_workouts' ) {
137 return;
138 }
139
140 if ( $this->should_update_premium() ) {
141 \wp_dequeue_script( 'yoast-seo-premium-workouts' );
142 }
143
144 $this->admin_asset_manager->enqueue_style( 'workouts' );
145
146 $workouts_option = $this->get_workouts_option();
147 $ftc_url = \esc_url( \admin_url( 'admin.php?page=wpseo_dashboard#/first-time-configuration' ) );
148
149 $this->admin_asset_manager->enqueue_script( 'workouts' );
150 $this->admin_asset_manager->localize_script(
151 'workouts',
152 'wpseoWorkoutsData',
153 [
154 'workouts' => $workouts_option,
155 'homeUrl' => \home_url(),
156 'pluginUrl' => \esc_url( \plugins_url( '', \WPSEO_FILE ) ),
157 'toolsPageUrl' => \esc_url( \admin_url( 'admin.php?page=wpseo_tools' ) ),
158 'usersPageUrl' => \esc_url( \admin_url( 'users.php' ) ),
159 'firstTimeConfigurationUrl' => $ftc_url,
160 'isPremium' => $this->product_helper->is_premium(),
161 'upsellText' => $this->get_upsell_text(),
162 'upsellLink' => $this->get_upsell_link(),
163 ],
164 );
165 }
166
167 /**
168 * Renders the target for the React to mount to.
169 *
170 * @return void
171 */
172 public function render_target() {
173 if ( $this->should_update_premium() ) {
174 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in get_update_premium_notice.
175 echo $this->get_update_premium_notice();
176 }
177
178 echo '<div id="wpseo-workouts-container-free" class="yoast"></div>';
179 }
180
181 /**
182 * Gets the workouts option.
183 *
184 * @return mixed|null Returns workouts option if found, null if not.
185 */
186 private function get_workouts_option() {
187 $workouts_option = $this->options_helper->get( 'workouts_data' );
188
189 // This filter is documented in src/routes/workouts-route.php.
190 return \apply_filters( 'Yoast\WP\SEO\workouts_options', $workouts_option );
191 }
192
193 /**
194 * Returns the notification to show when Premium needs to be updated.
195 *
196 * @return string The notification to update Premium.
197 */
198 private function get_update_premium_notice() {
199 $url = $this->get_upsell_link();
200
201 if ( $this->has_premium_subscription_expired() ) {
202 /* translators: %s: expands to 'Yoast SEO Premium'. */
203 $title = \sprintf( \__( 'Renew your subscription of %s', 'wordpress-seo' ), 'Yoast SEO Premium' );
204 $copy = \sprintf(
205 /* translators: %s: expands to 'Yoast SEO Premium'. */
206 \esc_html__(
207 'Accessing the latest workouts requires an updated version of %s (at least 17.7), but it looks like your subscription has expired. Please renew your subscription to update and gain access to all the latest features.',
208 'wordpress-seo',
209 ),
210 'Yoast SEO Premium',
211 );
212 $button = '<a class="yoast-button yoast-button-upsell yoast-button--small" href="' . \esc_url( $url ) . '" target="_blank">'
213 . \esc_html__( 'Renew your subscription', 'wordpress-seo' )
214 /* translators: Hidden accessibility text. */
215 . '<span class="screen-reader-text">' . \__( '(Opens in a new browser tab)', 'wordpress-seo' ) . '</span>'
216 . '<span aria-hidden="true" class="yoast-button-upsell__caret"></span>'
217 . '</a>';
218 }
219 elseif ( $this->has_premium_subscription_activated() ) {
220 /* translators: %s: expands to 'Yoast SEO Premium'. */
221 $title = \sprintf( \__( 'Update to the latest version of %s', 'wordpress-seo' ), 'Yoast SEO Premium' );
222 $copy = \sprintf(
223 /* translators: 1: expands to 'Yoast SEO Premium', 2: Link start tag to the page to update Premium, 3: Link closing tag. */
224 \esc_html__( 'It looks like you\'re running an outdated version of %1$s, please %2$supdate to the latest version (at least 17.7)%3$s to gain access to our updated workouts section.', 'wordpress-seo' ),
225 'Yoast SEO Premium',
226 '<a href="' . \esc_url( $url ) . '">',
227 '</a>',
228 );
229 $button = null;
230 }
231 else {
232 /* translators: %s: expands to 'Yoast SEO Premium'. */
233 $title = \sprintf( \__( 'Activate your subscription of %s', 'wordpress-seo' ), 'Yoast SEO Premium' );
234 $url_button = 'https://yoa.st/workouts-activate-notice-help';
235 $copy = \sprintf(
236 /* translators: 1: expands to 'Yoast SEO Premium', 2: Link start tag to the page to update Premium, 3: Link closing tag. */
237 \esc_html__( 'It looks like you’re running an outdated and unactivated version of %1$s, please activate your subscription in %2$sMyYoast%3$s and update to the latest version (at least 17.7) to gain access to our updated workouts section.', 'wordpress-seo' ),
238 'Yoast SEO Premium',
239 '<a href="' . \esc_url( $url ) . '">',
240 '</a>',
241 );
242 $button = '<a class="yoast-button yoast-button--primary yoast-button--small" href="' . \esc_url( $url_button ) . '" target="_blank">'
243 . \esc_html__( 'Get help activating your subscription', 'wordpress-seo' )
244 /* translators: Hidden accessibility text. */
245 . '<span class="screen-reader-text">' . \__( '(Opens in a new browser tab)', 'wordpress-seo' ) . '</span>'
246 . '</a>';
247 }
248
249 $notice = new Notice_Presenter(
250 $title,
251 $copy,
252 null,
253 $button,
254 );
255
256 return $notice->present();
257 }
258
259 /**
260 * Check whether Premium should be updated.
261 *
262 * @return bool Returns true when Premium is enabled and the version is below 17.7.
263 */
264 private function should_update_premium() {
265 $premium_version = $this->product_helper->get_premium_version();
266 return $premium_version !== null && \version_compare( $premium_version, '17.7-RC1', '<' );
267 }
268
269 /**
270 * Check whether the Premium subscription has expired.
271 *
272 * @return bool Returns true when Premium subscription has expired.
273 */
274 private function has_premium_subscription_expired() {
275 $subscription = $this->addon_manager->get_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG );
276
277 return ( isset( $subscription->expiry_date ) && ( \strtotime( $subscription->expiry_date ) - \time() ) < 0 );
278 }
279
280 /**
281 * Check whether the Premium subscription is activated.
282 *
283 * @return bool Returns true when Premium subscription is activated.
284 */
285 private function has_premium_subscription_activated() {
286 return $this->addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG );
287 }
288
289 /**
290 * Returns the upsell/update copy to show in the card buttons.
291 *
292 * @return string Returns a string with the upsell/update copy for the card buttons.
293 */
294 private function get_upsell_text() {
295 if ( ! $this->product_helper->is_premium() || ! $this->should_update_premium() ) {
296 // Use the default defined in the component.
297 return '';
298 }
299 if ( $this->has_premium_subscription_expired() ) {
300 return \sprintf(
301 /* translators: %s: expands to 'Yoast SEO Premium'. */
302 \__( 'Renew %s', 'wordpress-seo' ),
303 'Yoast SEO Premium',
304 );
305 }
306 if ( $this->has_premium_subscription_activated() ) {
307 return \sprintf(
308 /* translators: %s: expands to 'Yoast SEO Premium'. */
309 \__( 'Update %s', 'wordpress-seo' ),
310 'Yoast SEO Premium',
311 );
312 }
313 return \sprintf(
314 /* translators: %s: expands to 'Yoast SEO Premium'. */
315 \__( 'Activate %s', 'wordpress-seo' ),
316 'Yoast SEO Premium',
317 );
318 }
319
320 /**
321 * Returns the upsell/update link to show in the card buttons.
322 *
323 * @return string Returns a string with the upsell/update link for the card buttons.
324 */
325 private function get_upsell_link() {
326 if ( ! $this->product_helper->is_premium() || ! $this->should_update_premium() ) {
327 // Use the default defined in the component.
328 return '';
329 }
330 if ( $this->has_premium_subscription_expired() ) {
331 return 'https://yoa.st/workout-renew-notice';
332 }
333 if ( $this->has_premium_subscription_activated() ) {
334 return \wp_nonce_url( \self_admin_url( 'update.php?action=upgrade-plugin&plugin=wordpress-seo-premium/wp-seo-premium.php' ), 'upgrade-plugin_wordpress-seo-premium/wp-seo-premium.php' );
335 }
336 return 'https://yoa.st/workouts-activate-notice-myyoast';
337 }
338 }
339