GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( c642a0...737b79 )
by Brad
03:35
created

FS_Plugin_Info_Dialog::get_download_cta()   C

Complexity

Conditions 16
Paths 9

Size

Total Lines 79
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 55
nc 9
nop 2
dl 0
loc 79
rs 5.0904
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 19 and the first side effect is on line 10.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
    /**
3
     * @package     Freemius
4
     * @copyright   Copyright (c) 2015, Freemius, Inc.
5
     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
     * @since       1.0.6
7
     */
8
9
    if ( ! defined( 'ABSPATH' ) ) {
10
        exit;
11
    }
12
13
    /**
14
     * Class FS_Plugin_Info_Dialog
15
     *
16
     * @author Vova Feldman (@svovaf)
17
     * @since  1.1.7
18
     */
19
    class FS_Plugin_Info_Dialog {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
20
        /**
21
         * @since 1.1.7
22
         *
23
         * @var FS_Logger
24
         */
25
        private $_logger;
26
27
        /**
28
         * @since 1.1.7
29
         *
30
         * @var Freemius
31
         */
32
        private $_fs;
33
34
        function __construct( Freemius $fs ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
35
            $this->_fs = $fs;
36
37
            $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $fs->get_slug() . '_info', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
38
39
            // Remove default plugin information action.
40
            remove_all_actions( 'install_plugins_pre_plugin-information' );
41
42
            // Override action with custom plugins function for add-ons.
43
            add_action( 'install_plugins_pre_plugin-information', array( &$this, 'install_plugin_information' ) );
44
45
            // Override request for plugin information for Add-ons.
46
            add_filter(
47
                'fs_plugins_api',
48
                array( &$this, '_get_addon_info_filter' ),
49
                WP_FS__DEFAULT_PRIORITY, 3 );
50
        }
51
52
        /**
53
         * Generate add-on plugin information.
54
         *
55
         * @author Vova Feldman (@svovaf)
56
         * @since  1.0.6
57
         *
58
         * @param array       $data
59
         * @param string      $action
60
         * @param object|null $args
61
         *
62
         * @return array|null
63
         */
64
        function _get_addon_info_filter( $data, $action = '', $args = null ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
65
            $this->_logger->entrance();
66
67
            $parent_plugin_id = fs_request_get( 'parent_plugin_id', false );
68
69
            if ( $this->_fs->get_id() != $parent_plugin_id ||
70
                 ( 'plugin_information' !== $action ) ||
71
                 ! isset( $args->slug )
72
            ) {
73
                return $data;
74
            }
75
76
            // Find add-on by slug.
77
            $selected_addon = $this->_fs->get_addon_by_slug( $args->slug, WP_FS__DEV_MODE );
78
79
            if ( false === $selected_addon ) {
80
                return $data;
81
            }
82
83
            if ( ! isset( $selected_addon->info ) ) {
84
                // Setup some default info.
85
                $selected_addon->info                  = new stdClass();
86
                $selected_addon->info->selling_point_0 = 'Selling Point 1';
87
                $selected_addon->info->selling_point_1 = 'Selling Point 2';
88
                $selected_addon->info->selling_point_2 = 'Selling Point 3';
89
                $selected_addon->info->description     = '<p>Tell your users all about your add-on</p>';
90
            }
91
92
            fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' );
93
94
            $data = $args;
95
96
            $has_free_plan = false;
97
            $has_paid_plan = false;
98
99
            // Load add-on pricing.
100
            $has_pricing  = false;
101
            $has_features = false;
102
            $plans        = false;
103
104
            $result = $this->_fs->get_api_plugin_scope()->get( "/addons/{$selected_addon->id}/pricing.json?type=visible" );
105
106
            if ( ! isset( $result->error ) ) {
107
                $plans = $result->plans;
108
109
                if ( is_array( $plans ) ) {
110
                    for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
111
                        $pricing  = isset( $plans[ $i ]->pricing ) ? $plans[ $i ]->pricing : null;
112
                        $features = isset( $plans[ $i ]->features ) ? $plans[ $i ]->features : null;
113
114
                        $plans[ $i ] = new FS_Plugin_Plan( $plans[ $i ] );
115
                        $plan        = $plans[ $i ];
116
117
                        if ( 'free' == $plans[ $i ]->name ||
118
                             ! is_array( $pricing ) ||
119
                             0 == count( $pricing )
120
                        ) {
121
                            $has_free_plan = true;
122
                        }
123
124
                        if ( is_array( $pricing ) && 0 < count( $pricing ) ) {
125
                            $has_paid_plan = true;
126
127
                            foreach ( $pricing as &$prices ) {
128
                                $prices = new FS_Pricing( $prices );
129
                            }
130
131
                            $plan->pricing = $pricing;
132
133
                            $has_pricing = true;
134
                        }
135
136
                        if ( is_array( $features ) && 0 < count( $features ) ) {
137
                            $plan->features = $features;
0 ignored issues
show
Bug introduced by
The property features does not seem to exist. Did you mean is_block_features?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
138
139
                            $has_features = true;
140
                        }
141
                    }
142
                }
143
            }
144
145
            if ( ! $has_paid_plan && $selected_addon->is_wp_org_compliant ) {
146
                $repo_data = FS_Plugin_Updater::_fetch_plugin_info_from_repository(
147
                    'plugin_information', (object) array(
148
                    'slug'   => $selected_addon->slug,
149
                    'is_ssl' => is_ssl(),
150
                    'fields' => array(
151
                        'banners'         => true,
152
                        'reviews'         => true,
153
                        'downloaded'      => false,
154
                        'active_installs' => true
155
                    )
156
                ) );
157
158
                if ( ! empty( $repo_data ) ) {
159
                    $data                 = $repo_data;
160
                    $data->wp_org_missing = false;
161
                } else {
162
                    // Couldn't find plugin on .org.
163
                    $selected_addon->is_wp_org_compliant = false;
164
165
                    // Plugin is missing, not on Freemius nor WP.org.
166
                    $data->wp_org_missing = true;
167
                }
168
            } else {
169
                $data->wp_org_missing = false;
170
171
                // Fetch latest version from Freemius.
172
                $latest = $this->_fs->_fetch_latest_version( $selected_addon->id );
173
174
                if ( $has_paid_plan ) {
175
                    $data->checkout_link = $this->_fs->checkout_url();
176
                }
177
                if ( $has_free_plan ) {
178
                    $data->download_link = $this->_fs->_get_latest_download_local_url( $selected_addon->id );
179
                }
180
181
                $data->fs_missing = ( false === $latest );
182
183
184
                // Fetch as much as possible info from local files.
185
                $plugin_local_data = $this->_fs->get_plugin_data();
186
                $data->name        = $selected_addon->title;
187
                $data->author      = $plugin_local_data['Author'];
188
                $view_vars         = array( 'plugin' => $selected_addon );
189
                $data->sections    = array(
190
                    'description' => fs_get_template( '/plugin-info/description.php', $view_vars ),
191
                );
192
193
                if ( ! empty( $selected_addon->info->banner_url ) ) {
194
                    $data->banners = array(
195
                        'low' => $selected_addon->info->banner_url,
196
                    );
197
                }
198
199
                if ( ! empty( $selected_addon->info->screenshots ) ) {
200
                    $view_vars                     = array(
201
                        'screenshots' => $selected_addon->info->screenshots,
202
                        'plugin'      => $selected_addon,
203
                    );
204
                    $data->sections['screenshots'] = fs_get_template( '/plugin-info/screenshots.php', $view_vars );
205
                }
206
207
                if ( is_object( $latest ) ) {
208
                    $data->version      = $latest->version;
209
                    $data->last_updated = ! is_null( $latest->updated ) ? $latest->updated : $latest->created;
210
                    $data->requires     = $latest->requires_platform_version;
211
                    $data->tested       = $latest->tested_up_to_version;
212
                } else {
213
                    // Add dummy version.
214
                    $data->version = '1.0.0';
215
216
                    // Add message to developer to deploy the plugin through Freemius.
217
                }
218
            }
219
220
            if ( $has_pricing ) {
221
                // Add plans to data.
222
                $data->plans = $plans;
223
224
                if ( $has_features ) {
225
                    $view_vars                  = array(
226
                        'plans'  => $plans,
227
                        'plugin' => $selected_addon,
228
                    );
229
                    $data->sections['features'] = fs_get_template( '/plugin-info/features.php', $view_vars );
230
                }
231
            }
232
233
            $data->has_free_plan       = $has_free_plan;
234
            $data->has_paid_plan       = $has_paid_plan;
235
            $data->is_paid             = $has_paid_plan;
236
            $data->is_wp_org_compliant = $selected_addon->is_wp_org_compliant;
237
238
            return $data;
239
        }
240
241
        /**
242
         * @author Vova Feldman (@svovaf)
243
         * @since  1.1.7
244
         *
245
         * @param FS_Plugin_Plan $plan
246
         *
247
         * @return string
248
         */
249
        private function get_billing_cycle( FS_Plugin_Plan $plan ) {
250
            $billing_cycle = null;
251
252
            if ( 1 === count( $plan->pricing ) && 1 == $plan->pricing[0]->licenses ) {
253
                $pricing = $plan->pricing[0];
254
                if ( isset( $pricing->annual_price ) ) {
255
                    $billing_cycle = 'annual';
256
                } else if ( isset( $pricing->monthly_price ) ) {
257
                    $billing_cycle = 'monthly';
258
                } else if ( isset( $pricing->lifetime_price ) ) {
259
                    $billing_cycle = 'lifetime';
260
                }
261
            } else {
262
                foreach ( $plan->pricing as $pricing ) {
263
                    if ( isset( $pricing->annual_price ) ) {
264
                        $billing_cycle = 'annual';
265
                    } else if ( isset( $pricing->monthly_price ) ) {
266
                        $billing_cycle = 'monthly';
267
                    } else if ( isset( $pricing->lifetime_price ) ) {
268
                        $billing_cycle = 'lifetime';
269
                    }
270
271
                    if ( ! is_null( $billing_cycle ) ) {
272
                        break;
273
                    }
274
                }
275
            }
276
277
            return $billing_cycle;
278
        }
279
280
        /**
281
         * @author Vova Feldman (@svovaf)
282
         * @since  2.0.0
283
         *
284
         * @param FS_Plugin_Plan $plan
285
         * @param FS_Pricing     $pricing
286
         *
287
         * @return float|null|string
288
         */
289
        private function get_price_tag( FS_Plugin_Plan $plan, FS_Pricing $pricing ) {
290
            $price_tag = '';
291
            if ( isset( $pricing->annual_price ) ) {
292
                $price_tag = $pricing->annual_price . ( $plan->is_block_features ? ' / year' : '' );
293
            } else if ( isset( $pricing->monthly_price ) ) {
294
                $price_tag = $pricing->monthly_price . ' / mo';
295
            } else if ( isset( $pricing->lifetime_price ) ) {
296
                $price_tag = $pricing->lifetime_price;
297
            }
298
299
            return '$' . $price_tag;
300
        }
301
302
        /**
303
         * @author Vova Feldman (@svovaf)
304
         * @since  1.1.7
305
         *
306
         * @param object         $api
307
         * @param FS_Plugin_Plan $plan
308
         *
309
         * @return string
310
         */
311
        private function get_checkout_cta( $api, $plan = null ) {
312
            if ( empty( $api->checkout_link ) ||
313
                 ! isset( $api->plans ) ||
314
                 ! is_array( $api->plans ) ||
315
                 0 == count( $api->plans )
316
            ) {
317
                return '';
318
            }
319
320
            if ( is_null( $plan ) ) {
321
                foreach ( $api->plans as $p ) {
322
                    if ( ! empty( $p->pricing ) ) {
323
                        $plan = $p;
324
                        break;
325
                    }
326
                }
327
            }
328
329
            return '<a class="button button-primary fs-checkout-button right" href="' . $this->_fs->addon_checkout_url(
330
                $plan->plugin_id,
331
                $plan->pricing[0]->id,
332
                $this->get_billing_cycle( $plan ),
333
                $plan->has_trial()
334
            ) . '" target="_parent">' .
335
                   ( ! $plan->has_trial() ?
336
                       fs_text_x_inline( 'Purchase', 'verb', 'purchase', $api->slug ) :
337
                       sprintf(
338
                       /* translators: %s: N-days trial */
339
                           fs_text_inline( 'Start my free %s', 'start-free-x', $api->slug ),
340
                           $this->get_trial_period( $plan )
341
                       )
342
                   ) .
343
                   '</a>';
344
        }
345
346
        /**
347
         * @author Vova Feldman (@svovaf)
348
         * @since  2.0.0
349
         *
350
         * @param object $api
351
         * @param bool   $is_primary
352
         *
353
         * @return string
354
         */
355
        private function get_download_cta( $api, $is_primary = true ) {
356
            if ( empty( $api->download_link ) ) {
357
                return '';
358
            }
359
360
            $status = install_plugin_install_status( $api );
361
362
            $has_paid_version = $api->has_paid_plan;
363
364
            // Hosted on WordPress.org.
365
            switch ( $status['status'] ) {
366
                case 'install':
367
                    if ( $api->is_wp_org_compliant ||
368
                         ! $this->_fs->is_org_repo_compliant() ||
369
                         $this->_fs->is_premium()
370
                    ) {
371
                        /**
372
                         * Allow immediate installation if one of the following:
373
                         *  1. WordPress.org add-on.
374
                         *  2. The core module is NOT wp.org compliant.
375
                         *  3. The core module is running the premium version which is not wp.org compliant.
376
                         */
377
                        if ( $status['url'] ) {
378
                            return $this->get_cta(
379
                                ( $has_paid_version ?
380
                                    fs_esc_html_inline( 'Install Free Version Now', 'install-free-version-now', $api->slug ) :
381
                                    fs_esc_html_inline( 'Install Now', 'install-now', $api->slug ) ),
382
                                $is_primary,
383
                                false,
384
                                $status['url'],
385
                                '_parent'
386
                            );
387
                        }
388
                    }
389
390
                    return $this->get_cta(
391
                        ( $has_paid_version ?
392
                            fs_esc_html_x_inline( 'Download Latest Free Version', 'as download latest version', 'download-latest-free-version', $api->slug ) :
393
                            fs_esc_html_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $api->slug ) ),
394
                        $is_primary,
395
                        false,
396
                        esc_url( $api->download_link )
397
                    );
398
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
399
                case 'update_available':
400
                    if ( $status['url'] ) {
401
                        return $this->get_cta(
402
                            ( $has_paid_version ?
403
                                fs_esc_html_inline( 'Install Free Version Update Now', 'install-free-version-update-now', $api->slug ) :
404
                                fs_esc_html_inline( 'Install Update Now', 'install-update-now', $api->slug ) ),
405
                            $is_primary,
406
                            false,
407
                            $status['url'],
408
                            '_parent'
409
                        );
410
                    }
411
                    break;
412
                case 'newer_installed':
413
                    return $this->get_cta(
414
                        ( $has_paid_version ?
415
                            esc_html( sprintf( fs_text_inline( 'Newer Free Version (%s) Installed', 'newer-free-installed', $api->slug ), $status['version'] ) ) :
416
                            esc_html( sprintf( fs_text_inline( 'Newer Version (%s) Installed', 'newer-installed', $api->slug ), $status['version'] ) ) ),
417
                        $is_primary,
418
                        true
419
                    );
420
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
421
                case 'latest_installed':
422
                    return $this->get_cta(
423
                        ( $has_paid_version ?
424
                            fs_esc_html_inline( 'Latest Free Version Installed', 'latest-free-installed', $api->slug ) :
425
                            fs_esc_html_inline( 'Latest Version Installed', 'latest-installed', $api->slug ) ),
426
                        $is_primary,
427
                        true
428
                    );
429
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
430
            }
431
432
            return '';
433
        }
434
435
        /**
436
         * Helper method to get a CTA button HTML.
437
         *
438
         * @author Vova Feldman (@svovaf)
439
         * @since  2.0.0
440
         *
441
         * @param string $label
442
         * @param bool   $is_primary
443
         * @param bool   $is_disabled
444
         * @param string $href
445
         * @param string $target
446
         *
447
         * @return string
448
         */
449
        private function get_cta(
450
            $label,
451
            $is_primary = true,
452
            $is_disabled = false,
453
            $href = '',
454
            $target = '_blank'
455
        ) {
456
            $classes = array();
457
458
            if ( ! $is_primary ) {
459
                $classes[] = 'left';
460
            } else {
461
                $classes[] = 'button-primary';
462
                $classes[] = 'right';
463
            }
464
465
            if ( $is_disabled ) {
466
                $classes[] = 'disabled';
467
            }
468
469
            return sprintf(
470
                '<a %s class="button %s">%s</a>',
471
                empty( $href ) ? '' : 'href="' . $href . '" target="' . $target . '"',
472
                implode( ' ', $classes ),
473
                $label
474
            );
475
        }
476
477
        /**
478
         * @author Vova Feldman (@svovaf)
479
         * @since  1.1.7
480
         *
481
         * @param FS_Plugin_Plan $plan
482
         *
483
         * @return string
484
         */
485
        private function get_trial_period( $plan ) {
486
            $trial_period = (int) $plan->trial_period;
487
488
            switch ( $trial_period ) {
489
                case 30:
490
                    return 'month';
491
                case 60:
492
                    return '2 months';
493
                default:
494
                    return "{$plan->trial_period} days";
495
            }
496
        }
497
498
        /**
499
         * Display plugin information in dialog box form.
500
         *
501
         * Based on core install_plugin_information() function.
502
         *
503
         * @author Vova Feldman (@svovaf)
504
         * @since  1.0.6
505
         */
506
        function install_plugin_information() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Coding Style introduced by
install_plugin_information uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
install_plugin_information uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
507
            global $tab;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
508
509
            if ( empty( $_REQUEST['plugin'] ) ) {
510
                return;
511
            }
512
513
            $args = array(
514
                'slug'   => wp_unslash( $_REQUEST['plugin'] ),
515
                'is_ssl' => is_ssl(),
516
                'fields' => array(
517
                    'banners'         => true,
518
                    'reviews'         => true,
519
                    'downloaded'      => false,
520
                    'active_installs' => true
521
                )
522
            );
523
524
            if ( is_array( $args ) ) {
525
                $args = (object) $args;
526
            }
527
528
            if ( ! isset( $args->per_page ) ) {
529
                $args->per_page = 24;
530
            }
531
532
            if ( ! isset( $args->locale ) ) {
533
                $args->locale = get_locale();
534
            }
535
536
            $api = apply_filters( 'fs_plugins_api', false, 'plugin_information', $args );
537
538
            if ( is_wp_error( $api ) ) {
539
                wp_die( $api );
540
            }
541
542
            $plugins_allowedtags = array(
543
                'a'       => array(
544
                    'href'   => array(),
545
                    'title'  => array(),
546
                    'target' => array(),
547
                    // Add image style for screenshots.
548
                    'class'  => array()
549
                ),
550
                'style'   => array(),
551
                'abbr'    => array( 'title' => array() ),
552
                'acronym' => array( 'title' => array() ),
553
                'code'    => array(),
554
                'pre'     => array(),
555
                'em'      => array(),
556
                'strong'  => array(),
557
                'div'     => array( 'class' => array() ),
558
                'span'    => array( 'class' => array() ),
559
                'p'       => array(),
560
                'ul'      => array(),
561
                'ol'      => array(),
562
                'li'      => array( 'class' => array() ),
563
                'i'       => array( 'class' => array() ),
564
                'h1'      => array(),
565
                'h2'      => array(),
566
                'h3'      => array(),
567
                'h4'      => array(),
568
                'h5'      => array(),
569
                'h6'      => array(),
570
                'img'     => array( 'src' => array(), 'class' => array(), 'alt' => array() ),
571
//			'table' => array(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
51% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
572
//			'td' => array(),
573
//			'tr' => array(),
574
//			'th' => array(),
575
//			'thead' => array(),
576
//			'tbody' => array(),
577
            );
578
579
            $plugins_section_titles = array(
580
                'description'  => fs_text_x_inline( 'Description', 'Plugin installer section title', 'description', $api->slug ),
581
                'installation' => fs_text_x_inline( 'Installation', 'Plugin installer section title', 'installation', $api->slug ),
582
                'faq'          => fs_text_x_inline( 'FAQ', 'Plugin installer section title', 'faq', $api->slug ),
583
                'screenshots'  => fs_text_inline( 'Screenshots', 'screenshots', $api->slug ),
584
                'changelog'    => fs_text_x_inline( 'Changelog', 'Plugin installer section title', 'changelog', $api->slug ),
585
                'reviews'      => fs_text_x_inline( 'Reviews', 'Plugin installer section title', 'reviews', $api->slug ),
586
                'other_notes'  => fs_text_x_inline( 'Other Notes', 'Plugin installer section title', 'other-notes', $api->slug ),
587
            );
588
589
            // Sanitize HTML
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
590
//		foreach ( (array) $api->sections as $section_name => $content ) {
591
//			$api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags );
592
//		}
593
594
            foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) {
595
                if ( isset( $api->$key ) ) {
596
                    $api->$key = wp_kses( $api->$key, $plugins_allowedtags );
597
                }
598
            }
599
600
            // Add after $api->slug is ready.
601
            $plugins_section_titles['features'] = fs_text_x_inline( 'Features & Pricing', 'Plugin installer section title', 'features-and-pricing', $api->slug );
602
603
            $_tab = esc_attr( $tab );
604
605
            $section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English.
606
            if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) {
607
                $section_titles = array_keys( (array) $api->sections );
608
                $section        = array_shift( $section_titles );
609
            }
610
611
            iframe_header( fs_text_inline( 'Plugin Install', 'plugin-install', $api->slug ) );
612
613
            $_with_banner = '';
614
615
//	var_dump($api->banners);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
616
            if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) {
617
                $_with_banner = 'with-banner';
618
                $low          = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low'];
619
                $high         = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high'];
620
                ?>
621
                <style type="text/css">
622
                    #plugin-information-title.with-banner
623
                    {
624
                        background-image: url( <?php echo esc_url( $low ); ?> );
625
                    }
626
627
                    @media only screen and ( -webkit-min-device-pixel-ratio: 1.5 )
628
                    {
629
                        #plugin-information-title.with-banner
630
                        {
631
                            background-image: url( <?php echo esc_url( $high ); ?> );
632
                        }
633
                    }
634
                </style>
635
                <?php
636
            }
637
638
            echo '<div id="plugin-information-scrollable">';
639
            echo "<div id='{$_tab}-title' class='{$_with_banner}'><div class='vignette'></div><h2>{$api->name}</h2></div>";
640
            echo "<div id='{$_tab}-tabs' class='{$_with_banner}'>\n";
641
642
            foreach ( (array) $api->sections as $section_name => $content ) {
643
                if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) {
644
                    continue;
645
                }
646
647
                if ( isset( $plugins_section_titles[ $section_name ] ) ) {
648
                    $title = $plugins_section_titles[ $section_name ];
649
                } else {
650
                    $title = ucwords( str_replace( '_', ' ', $section_name ) );
651
                }
652
653
                $class       = ( $section_name === $section ) ? ' class="current"' : '';
654
                $href        = add_query_arg( array( 'tab' => $tab, 'section' => $section_name ) );
655
                $href        = esc_url( $href );
656
                $san_section = esc_attr( $section_name );
657
                echo "\t<a name='$san_section' href='$href' $class>$title</a>\n";
658
            }
659
660
            echo "</div>\n";
661
662
            ?>
663
        <div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'>
664
            <div class="fyi">
665
                <?php if ( $api->is_paid ) : ?>
666
                    <?php if ( isset( $api->plans ) ) : ?>
667
                        <div class="plugin-information-pricing">
668
                        <?php foreach ( $api->plans as $plan ) : ?>
669
                            <?php
670
                            if ( empty( $plan->pricing ) ) {
671
                                continue;
672
                            }
673
674
                            /**
675
                             * @var FS_Plugin_Plan $plan
676
                             */
677
                            ?>
678
                            <?php $first_pricing = $plan->pricing[0] ?>
679
                            <?php $is_multi_cycle = $first_pricing->is_multi_cycle() ?>
680
                            <div class="fs-plan<?php if ( ! $is_multi_cycle ) {
681
                                echo ' fs-single-cycle';
682
                            } ?>" data-plan-id="<?php echo $plan->id ?>">
683
                                <h3 data-plan="<?php echo $plan->id ?>"><?php echo esc_html( sprintf( fs_text_x_inline( '%s Plan', 'e.g. Professional Plan', 'x-plan', $api->slug ), $plan->title ) ) ?></h3>
684
                                <?php $has_annual = $first_pricing->has_annual() ?>
685
                                <?php $has_monthly = $first_pricing->has_monthly() ?>
686
                                <div class="nav-tab-wrapper">
687
                                    <?php $billing_cycles = array( 'monthly', 'annual', 'lifetime' ) ?>
688
                                    <?php $i = 0;
689
                                        foreach ( $billing_cycles as $cycle ) : ?>
690
                                            <?php $prop = "{$cycle}_price";
691
                                            if ( isset( $first_pricing->{$prop} ) ) : ?>
692
                                                <?php $is_featured = ( 'annual' === $cycle && $is_multi_cycle ) ?>
693
                                                <?php
694
                                                $prices = array();
695
                                                foreach ( $plan->pricing as $pricing ) {
696
                                                    if ( isset( $pricing->{$prop} ) ) {
697
                                                        $prices[] = array(
698
                                                            'id'       => $pricing->id,
699
                                                            'licenses' => $pricing->licenses,
700
                                                            'price'    => $pricing->{$prop}
701
                                                        );
702
                                                    }
703
                                                }
704
                                                ?>
705
                                                <a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>"
706
                                                   data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>">
707
                                                    <?php if ( $is_featured ) : ?>
708
                                                        <label>
709
                                                            &#9733; <?php fs_esc_html_echo_x_inline( 'Best', 'e.g. the best product', 'best', $api->slug ) ?>
710
                                                            &#9733;</label>
711
                                                    <?php endif ?>
712
                                                    <?php
713
                                                        switch ( $cycle ) {
714
                                                            case 'monthly':
715
                                                                fs_esc_html_echo_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug );
716
                                                                break;
717
                                                            case 'annual':
718
                                                                fs_esc_html_echo_x_inline( 'Annual', 'as once a year', 'annual', $api->slug );
719
                                                                break;
720
                                                            case 'lifetime':
721
                                                                fs_esc_html_echo_inline( 'Lifetime', 'lifetime', $api->slug );
722
                                                                break;
723
                                                        }
724
                                                    ?>
725
                                                </a>
726
                                            <?php endif ?>
727
                                            <?php $i ++; endforeach ?>
728
                                    <?php wp_enqueue_script( 'jquery' ) ?>
729
                                    <script type="text/javascript">
730
                                        (function ($, undef) {
731
                                            var
732
                                                _formatBillingFrequency = function (cycle) {
733
                                                    switch (cycle) {
734
                                                        case 'monthly':
735
                                                            return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug ) ) ?>';
736
                                                        case 'annual':
737
                                                            return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Annually', 'as once a year', 'annually', $api->slug ) ) ?>';
738
                                                        case 'lifetime':
739
                                                            return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Once', 'as once a year', 'once', $api->slug ) ) ?>';
740
                                                    }
741
                                                },
742
                                                _formatLicensesTitle    = function (pricing) {
743
                                                    switch (pricing.licenses) {
744
                                                        case 1:
745
                                                            return '<?php fs_esc_attr_echo_inline( 'Single Site License', 'license-single-site', $api->slug ) ?>';
746
                                                        case null:
747
                                                            return '<?php fs_esc_attr_echo_inline( 'Unlimited Licenses', 'license-unlimited', $api->slug ) ?>';
748
                                                        default:
749
                                                            return '<?php fs_esc_attr_echo_inline( 'Up to %s Sites', 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses);
750
                                                    }
751
                                                },
752
                                                _formatPrice            = function (pricing, cycle, multipleLicenses) {
753
                                                    if (undef === multipleLicenses)
754
                                                        multipleLicenses = true;
755
756
                                                    var priceCycle;
757
                                                    switch (cycle) {
758
                                                        case 'monthly':
759
                                                            priceCycle = ' / <?php fs_echo_x_inline( 'mo', 'as monthly period', 'mo', $api->slug ) ?>';
760
                                                            break;
761
                                                        case 'lifetime':
762
                                                            priceCycle = '';
763
                                                            break;
764
                                                        case 'annual':
765
                                                        default:
766
                                                            priceCycle = ' / <?php fs_echo_x_inline( 'year', 'as annual period', 'year', $api->slug ) ?>';
767
                                                            break;
768
                                                    }
769
770
                                                    if (!multipleLicenses && 1 == pricing.licenses) {
771
                                                        return '$' + pricing.price + priceCycle;
772
                                                    }
773
774
                                                    return _formatLicensesTitle(pricing) + ' - <var class="fs-price">$' + pricing.price + priceCycle + '</var>';
775
                                                },
776
                                                _checkoutUrl            = function (plan, pricing, cycle) {
777
                                                    return '<?php echo esc_url_raw( remove_query_arg( 'billing_cycle', add_query_arg( array( 'plugin_id' => $plan->plugin_id ), $api->checkout_link ) ) ) ?>' +
778
                                                        '&plan_id=' + plan +
779
                                                        '&pricing_id=' + pricing +
780
                                                        '&billing_cycle=' + cycle<?php if ( $plan->has_trial() ) {
781
                                                        echo " + '&trial=true'";
782
                                                    }?>;
783
                                                },
784
                                                _updateCtaUrl           = function (plan, pricing, cycle) {
785
                                                    $('.plugin-information-pricing .button, #plugin-information-footer .button.fs-checkout-button').attr('href', _checkoutUrl(plan, pricing, cycle));
786
                                                };
787
788
                                            $(document).ready(function () {
789
                                                var $plan = $('.plugin-information-pricing .fs-plan[data-plan-id=<?php echo $plan->id ?>]');
790
                                                $plan.find('input[type=radio]').live('click', function () {
791
                                                    _updateCtaUrl(
792
                                                        $plan.attr('data-plan-id'),
793
                                                        $(this).val(),
794
                                                        $plan.find('.nav-tab-active').attr('data-billing-cycle')
795
                                                    );
796
797
                                                    $plan.find('.fs-trial-terms .fs-price').html(
798
                                                        $(this).parents('label').find('.fs-price').html()
799
                                                    );
800
                                                });
801
802
                                                $plan.find('.nav-tab').click(function () {
803
                                                    if ($(this).hasClass('nav-tab-active'))
804
                                                        return;
805
806
                                                    var $this        = $(this),
807
                                                        billingCycle = $this.attr('data-billing-cycle'),
808
                                                        pricing      = JSON.parse($this.attr('data-pricing')),
809
                                                        $pricesList  = $this.parents('.fs-plan').find('.fs-pricing-body .fs-licenses'),
810
                                                        html         = '';
811
812
                                                    // Un-select previously selected tab.
813
                                                    $plan.find('.nav-tab').removeClass('nav-tab-active');
814
815
                                                    // Select current tab.
816
                                                    $this.addClass('nav-tab-active');
817
818
                                                    // Render licenses prices.
819
                                                    if (1 == pricing.length) {
820
                                                        html = '<li><label><?php echo fs_esc_attr_x_inline( 'Price', 'noun', 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>';
821
                                                    } else {
822
                                                        for (var i = 0; i < pricing.length; i++) {
823
                                                            html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>';
824
                                                        }
825
                                                    }
826
                                                    $pricesList.html(html);
827
828
                                                    if (1 < pricing.length) {
829
                                                        // Select first license option.
830
                                                        $pricesList.find('li:first input').click();
831
                                                    }
832
                                                    else {
833
                                                        _updateCtaUrl(
834
                                                            $plan.attr('data-plan-id'),
835
                                                            pricing[0].id,
836
                                                            billingCycle
837
                                                        );
838
                                                    }
839
840
                                                    // Update billing frequency.
841
                                                    $plan.find('.fs-billing-frequency').html(_formatBillingFrequency(billingCycle));
842
843
                                                    if ('annual' === billingCycle) {
844
                                                        $plan.find('.fs-annual-discount').show();
845
                                                    } else {
846
                                                        $plan.find('.fs-annual-discount').hide();
847
                                                    }
848
                                                });
849
850
                                                <?php if ( $has_annual ) : ?>
851
                                                // Select annual by default.
852
                                                $plan.find('.nav-tab[data-billing-cycle=annual]').click();
853
                                                <?php else : ?>
854
                                                // Select first tab.
855
                                                $plan.find('.nav-tab:first').click();
856
                                                <?php endif ?>
857
                                            });
858
                                        }(jQuery));
859
                                    </script>
860
                                </div>
861
                                <div class="fs-pricing-body">
862
                                    <span class="fs-billing-frequency"></span>
863
                                    <?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?>
864
                                    <?php if ( $annual_discount > 0 ) : ?>
865
                                        <span
866
                                            class="fs-annual-discount"><?php printf(
867
                                            /* translators: %s: Discount (e.g. discount of $5 or 10%) */
868
                                                fs_esc_html_inline( 'Save %s', 'save-x', $api->slug ), $annual_discount . '%' ) ?></span>
869
                                    <?php endif ?>
870
                                    <ul class="fs-licenses">
871
                                    </ul>
872
                                    <?php echo $this->get_checkout_cta( $api, $plan, false ) ?>
0 ignored issues
show
Unused Code introduced by
The call to FS_Plugin_Info_Dialog::get_checkout_cta() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
873
                                    <div style="clear:both"></div>
874
                                    <?php if ( $plan->has_trial() ) : ?>
875
                                        <?php $trial_period = $this->get_trial_period( $plan ) ?>
876
                                        <ul class="fs-trial-terms">
877
                                            <li>
878
                                                <i class="dashicons dashicons-yes"></i><?php echo esc_html( sprintf( fs_text_inline( 'No commitment for %s - cancel anytime', 'no-commitment-x', $api->slug ), $trial_period ) ) ?>
879
                                            </li>
880
                                            <li>
881
                                                <i class="dashicons dashicons-yes"></i><?php printf( esc_html( fs_text_inline( 'After your free %s, pay as little as %s', 'after-x-pay-as-little-y', $api->slug ) ), $trial_period, '<var class="fs-price">' . $this->get_price_tag( $plan, $plan->pricing[0] ) . '</var>' ) ?>
882
                                            </li>
883
                                        </ul>
884
                                    <?php endif ?>
885
                                </div>
886
                            </div>
887
                            </div>
888
                        <?php endforeach ?>
889
                    <?php endif ?>
890
                <?php endif ?>
891
                <div>
892
                    <h3><?php fs_echo_inline( 'Details', 'details', $api->slug ) ?></h3>
893
                    <ul>
894
                        <?php if ( ! empty( $api->version ) ) { ?>
895
                            <li>
896
                                <strong><?php fs_esc_html_echo_x_inline( 'Version', 'product version', 'version', $api->slug ); ?>
897
                                    :</strong> <?php echo $api->version; ?></li>
898
                            <?php
899
                        }
900
                            if ( ! empty( $api->author ) ) {
901
                                ?>
902
                                <li>
903
                                    <strong><?php fs_echo_x_inline( 'Author', 'as the plugin author', 'author', $api->slug ); ?>
904
                                        :</strong> <?php echo links_add_target( $api->author, '_blank' ); ?>
905
                                </li>
906
                                <?php
907
                            }
908
                            if ( ! empty( $api->last_updated ) ) {
909
                                ?>
910
                                <li><strong><?php fs_echo_inline( 'Last Updated', 'last-updated', $api->slug ); ?>
911
                                        :</strong> <span
912
                                        title="<?php echo $api->last_updated; ?>">
913
				<?php echo esc_html( sprintf(
914
                /* translators: %s: time period (e.g. "2 hours" ago) */
915
                    fs_text_x_inline( '%s ago', 'x-ago', $api->slug ),
916
                    human_time_diff( strtotime( $api->last_updated ) )
917
                ) ) ?>
918
			</span></li>
919
                                <?php
920
                            }
921
                            if ( ! empty( $api->requires ) ) {
922
                                ?>
923
                                <li>
924
                                    <strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>
925
                                        :</strong> <?php echo esc_html( sprintf( fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires ) ) ?>
926
                                </li>
927
                                <?php
928
                            }
929
                            if ( ! empty( $api->tested ) ) {
930
                                ?>
931
                                <li>
932
                                    <strong><?php fs_esc_html_echo_inline( 'Compatible up to', 'compatible-up-to', $api->slug ); ?>
933
                                        :</strong> <?php echo $api->tested; ?>
934
                                </li>
935
                                <?php
936
                            }
937
                            if ( ! empty( $api->downloaded ) ) {
938
                                ?>
939
                                <li>
940
                                    <strong><?php fs_esc_html_echo_inline( 'Downloaded', 'downloaded', $api->slug ) ?>
941
                                        :</strong> <?php echo esc_html( sprintf(
942
                                        ( ( 1 == $api->downloaded ) ?
943
                                            /* translators: %s: 1 or One (Number of times downloaded) */
944
                                            fs_text_inline( '%s time', 'x-time', $api->slug ) :
945
                                            /* translators: %s: Number of times downloaded */
946
                                            fs_text_inline( '%s times', 'x-times', $api->slug )
947
                                        ),
948
                                        number_format_i18n( $api->downloaded )
949
                                    ) ); ?>
950
                                </li>
951
                                <?php
952
                            }
953
                            if ( ! empty( $api->slug ) && empty( $api->is_wp_org_compliant ) ) {
954
                                ?>
955
                                <li><a target="_blank"
956
                                       href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php fs_esc_html_echo_inline( 'WordPress.org Plugin Page', 'wp-org-plugin-page', $api->slug ) ?>
957
                                        &#187;</a>
958
                                </li>
959
                                <?php
960
                            }
961
                            if ( ! empty( $api->homepage ) ) {
962
                                ?>
963
                                <li><a target="_blank"
964
                                       href="<?php echo esc_url( $api->homepage ); ?>"><?php fs_esc_html_echo_inline( 'Plugin Homepage', 'plugin-homepage', $api->slug ) ?>
965
                                        &#187;</a>
966
                                </li>
967
                                <?php
968
                            }
969
                            if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) {
970
                                ?>
971
                                <li><a target="_blank"
972
                                       href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_esc_html_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
973
                                        &#187;</a>
974
                                </li>
975
                            <?php } ?>
976
                    </ul>
977
                </div>
978
                <?php if ( ! empty( $api->rating ) ) { ?>
979
                    <h3><?php fs_echo_inline( 'Average Rating', 'average-rating', $api->slug ); ?></h3>
980
                    <?php wp_star_rating( array(
981
                        'rating' => $api->rating,
982
                        'type'   => 'percent',
983
                        'number' => $api->num_ratings
984
                    ) ); ?>
985
                    <small>(<?php echo esc_html( sprintf(
986
                            fs_text_inline( 'based on %s', 'based-on-x', $api->slug ),
987
                            sprintf(
988
                                ( ( 1 == $api->num_ratings ) ?
989
                                    /* translators: %s: 1 or One */
990
                                    fs_text_inline( '%s rating', 'x-rating', $api->slug ) :
991
                                    /* translators: %s: Number larger than 1 */
992
                                    fs_text_inline( '%s ratings', 'x-ratings', $api->slug )
993
                                ),
994
                                number_format_i18n( $api->num_ratings )
995
                            ) ) ) ?>)
996
                    </small>
997
                    <?php
998
                }
999
1000
                    if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) {
1001
                        foreach ( $api->ratings as $key => $ratecount ) {
1002
                            // Avoid div-by-zero.
1003
                            $_rating     = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
1004
                            $stars_label = sprintf(
1005
                                ( ( 1 == $key ) ?
1006
                                    /* translators: %s: 1 or One */
1007
                                    fs_text_inline( '%s star', 'x-star', $api->slug ) :
1008
                                    /* translators: %s: Number larger than 1 */
1009
                                    fs_text_inline( '%s stars', 'x-stars', $api->slug )
1010
                                ),
1011
                                number_format_i18n( $key )
1012
                            );
1013
                            ?>
1014
                            <div class="counter-container">
1015
					<span class="counter-label"><a
1016
                            href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
1017
                            target="_blank"
1018
                            title="<?php echo esc_attr( sprintf(
1019
                            /* translators: %s: # of stars (e.g. 5 stars) */
1020
                                fs_text_inline( 'Click to see reviews that provided a rating of %s', 'click-to-reviews', $api->slug ),
1021
                                $stars_label
1022
                            ) ) ?>"><?php echo $stars_label ?></a></span>
1023
                                <span class="counter-back">
1024
						<span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
1025
					</span>
1026
                                <span class="counter-count"><?php echo number_format_i18n( $ratecount ); ?></span>
1027
                            </div>
1028
                            <?php
1029
                        }
1030
                    }
1031
                    if ( ! empty( $api->contributors ) ) {
1032
                        ?>
1033
                        <h3><?php fs_echo_inline( 'Contributors', 'contributors', $api->slug ); ?></h3>
1034
                        <ul class="contributors">
1035
                            <?php
1036
                                foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) {
1037
                                    if ( empty( $contrib_username ) && empty( $contrib_profile ) ) {
1038
                                        continue;
1039
                                    }
1040
                                    if ( empty( $contrib_username ) ) {
1041
                                        $contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile );
1042
                                    }
1043
                                    $contrib_username = sanitize_user( $contrib_username );
1044
                                    if ( empty( $contrib_profile ) ) {
1045
                                        echo "<li><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&amp;s=36' width='18' height='18' />{$contrib_username}</li>";
1046
                                    } else {
1047
                                        echo "<li><a href='{$contrib_profile}' target='_blank'><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&amp;s=36' width='18' height='18' />{$contrib_username}</a></li>";
1048
                                    }
1049
                                }
1050
                            ?>
1051
                        </ul>
1052
                        <?php if ( ! empty( $api->donate_link ) ) { ?>
1053
                            <a target="_blank"
1054
                               href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
1055
                                &#187;</a>
1056
                        <?php } ?>
1057
                    <?php } ?>
1058
            </div>
1059
            <div id="section-holder" class="wrap">
1060
            <?php
1061
            if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
1062
                echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been tested with your current version of WordPress.', 'not-tested-warning', $api->slug ) . '</p></div>';
1063
            } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
1064
                echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been marked as compatible with your version of WordPress.', 'not-compatible-warning', $api->slug ) . '</p></div>';
1065
            }
1066
1067
            foreach ( (array) $api->sections as $section_name => $content ) {
1068
                $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' );
1069
                $content = links_add_target( $content, '_blank' );
1070
1071
                $san_section = esc_attr( $section_name );
1072
1073
                $display = ( $section_name === $section ) ? 'block' : 'none';
1074
1075
                if ( 'description' === $section_name &&
1076
                     ( ( $api->is_wp_org_compliant && $api->wp_org_missing ) ||
1077
                       ( ! $api->is_wp_org_compliant && $api->fs_missing ) )
1078
                ) {
1079
                    $missing_notice = array(
1080
                        'type'    => 'error',
1081
                        'id'      => md5( microtime() ),
1082
                        'message' => $api->is_paid ?
1083
                            fs_text_inline( 'Paid add-on must be deployed to Freemius.', 'paid-addon-not-deployed', $api->slug ) :
1084
                            fs_text_inline( 'Add-on must be deployed to WordPress.org or Freemius.', 'free-addon-not-deployed', $api->slug ),
1085
                    );
1086
                    fs_require_template( 'admin-notice.php', $missing_notice );
1087
                }
1088
                echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n";
1089
                echo $content;
1090
                echo "\t</div>\n";
1091
            }
1092
            echo "</div>\n";
1093
            echo "</div>\n";
1094
            echo "</div>\n"; // #plugin-information-scrollable
1095
            echo "<div id='$tab-footer'>\n";
1096
1097
            if ( ! empty( $api->checkout_link ) ) {
1098
                echo $this->get_checkout_cta( $api );
1099
            }
1100
1101
            if ( ! empty( $api->download_link ) ) {
1102
                echo $this->get_download_cta( $api, empty( $api->checkout_link ) );
1103
            }
1104
1105
            echo "</div>\n";
1106
1107
            iframe_footer();
1108
            exit;
1109
        }
1110
    }