• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 56db0408fe2a0dbffe1c2c6cfad9b5c2f6941e34

14 Apr 2025 12:24PM UTC coverage: 52.454% (-2.1%) from 54.594%
56db0408fe2a0dbffe1c2c6cfad9b5c2f6941e34

Pull #22077

github

enricobattocchi
Adjust carryforward in Coveralls action
Pull Request #22077: Drop compatibility with PHP 7.2 and 7.3

7827 of 13877 branches covered (56.4%)

Branch coverage included in aggregate %.

29025 of 56379 relevant lines covered (51.48%)

42277.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/integrations/admin/workouts-integration.php
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
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc