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.

install_plugin_information()   F
last analyzed

Complexity

Conditions 83
Paths > 20000

Size

Total Lines 604

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 83
nc 4294967295
nop 0
dl 0
loc 604
rs 0
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', $this->_fs->get_id() );
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( $this->_fs->add_show_pending( "/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
            $latest = null;
146
147
            if ( ! $has_paid_plan && $selected_addon->is_wp_org_compliant ) {
148
                $repo_data = FS_Plugin_Updater::_fetch_plugin_info_from_repository(
149
                    'plugin_information', (object) array(
150
                    'slug'   => $selected_addon->slug,
151
                    'is_ssl' => is_ssl(),
152
                    'fields' => array(
153
                        'banners'         => true,
154
                        'reviews'         => true,
155
                        'downloaded'      => false,
156
                        'active_installs' => true
157
                    )
158
                ) );
159
160
                if ( ! empty( $repo_data ) ) {
161
                    $data                 = $repo_data;
162
                    $data->wp_org_missing = false;
163
                } else {
164
                    // Couldn't find plugin on .org.
165
                    $selected_addon->is_wp_org_compliant = false;
166
167
                    // Plugin is missing, not on Freemius nor WP.org.
168
                    $data->wp_org_missing = true;
169
                }
170
171
                $data->fs_missing = ( ! $has_free_plan || $data->wp_org_missing );
172
            } else {
173
                $data->wp_org_missing = false;
174
175
                $current_addon_version = false;
176
                if ( $this->_fs->is_addon_activated( $selected_addon->id ) ) {
177
                    $current_addon_version = $this->_fs->get_addon_instance( $selected_addon->id )->get_plugin_version();
178
                } else if ( $this->_fs->is_addon_installed( $selected_addon->id ) ) {
179
                    $addon_plugin_data = get_plugin_data(
180
                        ( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $selected_addon->id ) ),
181
                        false,
182
                        false
183
                    );
184
185
                    if ( ! empty( $addon_plugin_data ) ) {
186
                        $current_addon_version = $addon_plugin_data['Version'];
187
                    }
188
                }
189
190
                // Fetch latest version from Freemius.
191
                $latest = $this->_fs->_fetch_latest_version(
192
                    $selected_addon->id,
193
                    true,
194
                    WP_FS__TIME_24_HOURS_IN_SEC,
195
                    $current_addon_version
196
                );
197
198
                if ( $has_paid_plan ) {
199
                    $data->checkout_link = $this->_fs->checkout_url();
200
                }
201
                if ( $has_free_plan ) {
202
                    $data->download_link = $this->_fs->_get_latest_download_local_url( $selected_addon->id );
203
                }
204
205
                $data->fs_missing = ( false === $latest );
206
207
208
                // Fetch as much as possible info from local files.
209
                $plugin_local_data = $this->_fs->get_plugin_data();
210
                $data->author      = $plugin_local_data['Author'];
211
212
                if ( ! empty( $selected_addon->info->banner_url ) ) {
213
                    $data->banners = array(
214
                        'low' => $selected_addon->info->banner_url,
215
                    );
216
                }
217
218
                if ( ! empty( $selected_addon->info->screenshots ) ) {
219
                    $view_vars                     = array(
220
                        'screenshots' => $selected_addon->info->screenshots,
221
                        'plugin'      => $selected_addon,
222
                    );
223
                    $data->sections['screenshots'] = fs_get_template( '/plugin-info/screenshots.php', $view_vars );
224
                }
225
226
                if ( is_object( $latest ) ) {
227
                    $data->version      = $latest->version;
228
                    $data->last_updated = $latest->created;
229
                    $data->requires     = $latest->requires_platform_version;
230
                    $data->tested       = $latest->tested_up_to_version;
231
                } else {
232
                    // Add dummy version.
233
                    $data->version = '1.0.0';
234
235
                    // Add message to developer to deploy the plugin through Freemius.
236
                }
237
            }
238
239
            $data->name = $selected_addon->title;
240
            $view_vars  = array( 'plugin' => $selected_addon );
241
242
            if ( is_object( $latest ) && isset( $latest->readme ) && is_object( $latest->readme ) ) {
243
                $latest_version_readme_data = $latest->readme;
244
                if ( isset( $latest_version_readme_data->sections ) ) {
245
                    $data->sections = (array) $latest_version_readme_data->sections;
246
                } else {
247
                    $data->sections = array();
248
                }
249
            }
250
251
            $data->sections['description'] = fs_get_template( '/plugin-info/description.php', $view_vars );
252
253
            if ( $has_pricing ) {
254
                // Add plans to data.
255
                $data->plans = $plans;
256
257
                if ( $has_features ) {
258
                    $view_vars                  = array(
259
                        'plans'  => $plans,
260
                        'plugin' => $selected_addon,
261
                    );
262
                    $data->sections['features'] = fs_get_template( '/plugin-info/features.php', $view_vars );
263
                }
264
            }
265
266
            $data->has_free_plan       = $has_free_plan;
267
            $data->has_paid_plan       = $has_paid_plan;
268
            $data->is_paid             = $has_paid_plan;
269
            $data->is_wp_org_compliant = $selected_addon->is_wp_org_compliant;
270
271
            return $data;
272
        }
273
274
        /**
275
         * @author Vova Feldman (@svovaf)
276
         * @since  1.1.7
277
         *
278
         * @param FS_Plugin_Plan $plan
279
         *
280
         * @return string
281
         */
282
        private function get_billing_cycle( FS_Plugin_Plan $plan ) {
283
            $billing_cycle = null;
284
285
            if ( 1 === count( $plan->pricing ) && 1 == $plan->pricing[0]->licenses ) {
286
                $pricing = $plan->pricing[0];
287
                if ( isset( $pricing->annual_price ) ) {
288
                    $billing_cycle = 'annual';
289
                } else if ( isset( $pricing->monthly_price ) ) {
290
                    $billing_cycle = 'monthly';
291
                } else if ( isset( $pricing->lifetime_price ) ) {
292
                    $billing_cycle = 'lifetime';
293
                }
294
            } else {
295
                foreach ( $plan->pricing as $pricing ) {
296
                    if ( isset( $pricing->annual_price ) ) {
297
                        $billing_cycle = 'annual';
298
                    } else if ( isset( $pricing->monthly_price ) ) {
299
                        $billing_cycle = 'monthly';
300
                    } else if ( isset( $pricing->lifetime_price ) ) {
301
                        $billing_cycle = 'lifetime';
302
                    }
303
304
                    if ( ! is_null( $billing_cycle ) ) {
305
                        break;
306
                    }
307
                }
308
            }
309
310
            return $billing_cycle;
311
        }
312
313
        /**
314
         * @author Vova Feldman (@svovaf)
315
         * @since  2.0.0
316
         *
317
         * @param FS_Plugin_Plan $plan
318
         * @param FS_Pricing     $pricing
319
         *
320
         * @return float|null|string
321
         */
322
        private function get_price_tag( FS_Plugin_Plan $plan, FS_Pricing $pricing ) {
323
            $price_tag = '';
324
            if ( isset( $pricing->annual_price ) ) {
325
                $price_tag = $pricing->annual_price . ( $plan->is_block_features ? ' / year' : '' );
326
            } else if ( isset( $pricing->monthly_price ) ) {
327
                $price_tag = $pricing->monthly_price . ' / mo';
328
            } else if ( isset( $pricing->lifetime_price ) ) {
329
                $price_tag = $pricing->lifetime_price;
330
            }
331
332
            return '$' . $price_tag;
333
        }
334
335
        /**
336
         * @author Vova Feldman (@svovaf)
337
         * @since  1.1.7
338
         *
339
         * @param object         $api
340
         * @param FS_Plugin_Plan $plan
341
         *
342
         * @return string
343
         */
344
        private function get_checkout_cta( $api, $plan = null ) {
345
            if ( empty( $api->checkout_link ) ||
346
                 ! isset( $api->plans ) ||
347
                 ! is_array( $api->plans ) ||
348
                 0 == count( $api->plans )
349
            ) {
350
                return '';
351
            }
352
353
            if ( is_null( $plan ) ) {
354
                foreach ( $api->plans as $p ) {
355
                    if ( ! empty( $p->pricing ) ) {
356
                        $plan = $p;
357
                        break;
358
                    }
359
                }
360
            }
361
362
            return '<a class="button button-primary fs-checkout-button right" href="' . $this->_fs->addon_checkout_url(
363
                $plan->plugin_id,
364
                $plan->pricing[0]->id,
365
                $this->get_billing_cycle( $plan ),
366
                $plan->has_trial()
367
            ) . '" target="_parent">' .
368
                   ( ! $plan->has_trial() ?
369
                       fs_text_x_inline( 'Purchase', 'verb', 'purchase', $api->slug ) :
370
                       sprintf(
371
                       /* translators: %s: N-days trial */
372
                           fs_text_inline( 'Start my free %s', 'start-free-x', $api->slug ),
373
                           $this->get_trial_period( $plan )
374
                       )
375
                   ) .
376
                   '</a>';
377
        }
378
379
        /**
380
         * @author Vova Feldman (@svovaf)
381
         * @since  2.0.0
382
         *
383
         * @param object $api
384
         * @param bool   $is_primary
385
         *
386
         * @return string
387
         */
388
        private function get_download_cta( $api, $is_primary = true ) {
389
            if ( empty( $api->download_link ) ) {
390
                return '';
391
            }
392
393
            $status = install_plugin_install_status( $api );
394
395
            $has_paid_version = $api->has_paid_plan;
396
397
            // Hosted on WordPress.org.
398
            switch ( $status['status'] ) {
399
                case 'install':
400
                    if ( $api->is_wp_org_compliant ||
401
                         ! $this->_fs->is_org_repo_compliant() ||
402
                         $this->_fs->is_premium()
403
                    ) {
404
                        /**
405
                         * Allow immediate installation if one of the following:
406
                         *  1. WordPress.org add-on.
407
                         *  2. The core module is NOT wp.org compliant.
408
                         *  3. The core module is running the premium version which is not wp.org compliant.
409
                         */
410
                        if ( $status['url'] ) {
411
                            return $this->get_cta(
412
                                ( $has_paid_version ?
413
                                    fs_esc_html_inline( 'Install Free Version Now', 'install-free-version-now', $api->slug ) :
414
                                    fs_esc_html_inline( 'Install Now', 'install-now', $api->slug ) ),
415
                                $is_primary,
416
                                false,
417
                                $status['url'],
418
                                '_parent'
419
                            );
420
                        }
421
                    }
422
423
                    return $this->get_cta(
424
                        ( $has_paid_version ?
425
                            fs_esc_html_x_inline( 'Download Latest Free Version', 'as download latest version', 'download-latest-free-version', $api->slug ) :
426
                            fs_esc_html_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $api->slug ) ),
427
                        $is_primary,
428
                        false,
429
                        esc_url( $api->download_link )
430
                    );
431
                    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...
432
                case 'update_available':
433
                    if ( $status['url'] ) {
434
                        return $this->get_cta(
435
                            ( $has_paid_version ?
436
                                fs_esc_html_inline( 'Install Free Version Update Now', 'install-free-version-update-now', $api->slug ) :
437
                                fs_esc_html_inline( 'Install Update Now', 'install-update-now', $api->slug ) ),
438
                            $is_primary,
439
                            false,
440
                            $status['url'],
441
                            '_parent'
442
                        );
443
                    }
444
                    break;
445
                case 'newer_installed':
446
                    return $this->get_cta(
447
                        ( $has_paid_version ?
448
                            esc_html( sprintf( fs_text_inline( 'Newer Free Version (%s) Installed', 'newer-free-installed', $api->slug ), $status['version'] ) ) :
449
                            esc_html( sprintf( fs_text_inline( 'Newer Version (%s) Installed', 'newer-installed', $api->slug ), $status['version'] ) ) ),
450
                        $is_primary,
451
                        true
452
                    );
453
                    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...
454
                case 'latest_installed':
455
                    return $this->get_cta(
456
                        ( $has_paid_version ?
457
                            fs_esc_html_inline( 'Latest Free Version Installed', 'latest-free-installed', $api->slug ) :
458
                            fs_esc_html_inline( 'Latest Version Installed', 'latest-installed', $api->slug ) ),
459
                        $is_primary,
460
                        true
461
                    );
462
                    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...
463
            }
464
465
            return '';
466
        }
467
468
        /**
469
         * Helper method to get a CTA button HTML.
470
         *
471
         * @author Vova Feldman (@svovaf)
472
         * @since  2.0.0
473
         *
474
         * @param string $label
475
         * @param bool   $is_primary
476
         * @param bool   $is_disabled
477
         * @param string $href
478
         * @param string $target
479
         *
480
         * @return string
481
         */
482
        private function get_cta(
483
            $label,
484
            $is_primary = true,
485
            $is_disabled = false,
486
            $href = '',
487
            $target = '_blank'
488
        ) {
489
            $classes = array();
490
491
            if ( ! $is_primary ) {
492
                $classes[] = 'left';
493
            } else {
494
                $classes[] = 'button-primary';
495
                $classes[] = 'right';
496
            }
497
498
            if ( $is_disabled ) {
499
                $classes[] = 'disabled';
500
            }
501
502
            return sprintf(
503
                '<a %s class="button %s">%s</a>',
504
                empty( $href ) ? '' : 'href="' . $href . '" target="' . $target . '"',
505
                implode( ' ', $classes ),
506
                $label
507
            );
508
        }
509
510
        /**
511
         * @author Vova Feldman (@svovaf)
512
         * @since  1.1.7
513
         *
514
         * @param FS_Plugin_Plan $plan
515
         *
516
         * @return string
517
         */
518
        private function get_trial_period( $plan ) {
519
            $trial_period = (int) $plan->trial_period;
520
521
            switch ( $trial_period ) {
522
                case 30:
523
                    return 'month';
524
                case 60:
525
                    return '2 months';
526
                default:
527
                    return "{$plan->trial_period} days";
528
            }
529
        }
530
531
        /**
532
         * Display plugin information in dialog box form.
533
         *
534
         * Based on core install_plugin_information() function.
535
         *
536
         * @author Vova Feldman (@svovaf)
537
         * @since  1.0.6
538
         */
539
        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...
540
            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...
541
542
            if ( empty( $_REQUEST['plugin'] ) ) {
543
                return;
544
            }
545
546
            $args = array(
547
                'slug'   => wp_unslash( $_REQUEST['plugin'] ),
548
                'is_ssl' => is_ssl(),
549
                'fields' => array(
550
                    'banners'         => true,
551
                    'reviews'         => true,
552
                    'downloaded'      => false,
553
                    'active_installs' => true
554
                )
555
            );
556
557
            if ( is_array( $args ) ) {
558
                $args = (object) $args;
559
            }
560
561
            if ( ! isset( $args->per_page ) ) {
562
                $args->per_page = 24;
563
            }
564
565
            if ( ! isset( $args->locale ) ) {
566
                $args->locale = get_locale();
567
            }
568
569
            $api = apply_filters( 'fs_plugins_api', false, 'plugin_information', $args );
570
571
            if ( is_wp_error( $api ) ) {
572
                wp_die( $api );
573
            }
574
575
            $plugins_allowedtags = array(
576
                'a'       => array(
577
                    'href'   => array(),
578
                    'title'  => array(),
579
                    'target' => array(),
580
                    // Add image style for screenshots.
581
                    'class'  => array()
582
                ),
583
                'style'   => array(),
584
                'abbr'    => array( 'title' => array() ),
585
                'acronym' => array( 'title' => array() ),
586
                'code'    => array(),
587
                'pre'     => array(),
588
                'em'      => array(),
589
                'strong'  => array(),
590
                'div'     => array( 'class' => array() ),
591
                'span'    => array( 'class' => array() ),
592
                'p'       => array(),
593
                'ul'      => array(),
594
                'ol'      => array(),
595
                'li'      => array( 'class' => array() ),
596
                'i'       => array( 'class' => array() ),
597
                'h1'      => array(),
598
                'h2'      => array(),
599
                'h3'      => array(),
600
                'h4'      => array(),
601
                'h5'      => array(),
602
                'h6'      => array(),
603
                'img'     => array( 'src' => array(), 'class' => array(), 'alt' => array() ),
604
//			'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...
605
//			'td' => array(),
606
//			'tr' => array(),
607
//			'th' => array(),
608
//			'thead' => array(),
609
//			'tbody' => array(),
610
            );
611
612
            $plugins_section_titles = array(
613
                'description'  => fs_text_x_inline( 'Description', 'Plugin installer section title', 'description', $api->slug ),
614
                'installation' => fs_text_x_inline( 'Installation', 'Plugin installer section title', 'installation', $api->slug ),
615
                'faq'          => fs_text_x_inline( 'FAQ', 'Plugin installer section title', 'faq', $api->slug ),
616
                'screenshots'  => fs_text_inline( 'Screenshots', 'screenshots', $api->slug ),
617
                'changelog'    => fs_text_x_inline( 'Changelog', 'Plugin installer section title', 'changelog', $api->slug ),
618
                'reviews'      => fs_text_x_inline( 'Reviews', 'Plugin installer section title', 'reviews', $api->slug ),
619
                'other_notes'  => fs_text_x_inline( 'Other Notes', 'Plugin installer section title', 'other-notes', $api->slug ),
620
            );
621
622
            // 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...
623
//		foreach ( (array) $api->sections as $section_name => $content ) {
624
//			$api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags );
625
//		}
626
627
            foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) {
628
                if ( isset( $api->$key ) ) {
629
                    $api->$key = wp_kses( $api->$key, $plugins_allowedtags );
630
                }
631
            }
632
633
            // Add after $api->slug is ready.
634
            $plugins_section_titles['features'] = fs_text_x_inline( 'Features & Pricing', 'Plugin installer section title', 'features-and-pricing', $api->slug );
635
636
            $_tab = esc_attr( $tab );
637
638
            $section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English.
639
            if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) {
640
                $section_titles = array_keys( (array) $api->sections );
641
                $section        = array_shift( $section_titles );
642
            }
643
644
            iframe_header( fs_text_inline( 'Plugin Install', 'plugin-install', $api->slug ) );
645
646
            $_with_banner = '';
647
648
//	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...
649
            if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) {
650
                $_with_banner = 'with-banner';
651
                $low          = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low'];
652
                $high         = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high'];
653
                ?>
654
                <style type="text/css">
655
                    #plugin-information-title.with-banner
656
                    {
657
                        background-image: url( <?php echo esc_url( $low ); ?> );
658
                    }
659
660
                    @media only screen and ( -webkit-min-device-pixel-ratio: 1.5 )
661
                    {
662
                        #plugin-information-title.with-banner
663
                        {
664
                            background-image: url( <?php echo esc_url( $high ); ?> );
665
                        }
666
                    }
667
                </style>
668
                <?php
669
            }
670
671
            echo '<div id="plugin-information-scrollable">';
672
            echo "<div id='{$_tab}-title' class='{$_with_banner}'><div class='vignette'></div><h2>{$api->name}</h2></div>";
673
            echo "<div id='{$_tab}-tabs' class='{$_with_banner}'>\n";
674
675
            foreach ( (array) $api->sections as $section_name => $content ) {
676
                if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) {
677
                    continue;
678
                }
679
680
                if ( isset( $plugins_section_titles[ $section_name ] ) ) {
681
                    $title = $plugins_section_titles[ $section_name ];
682
                } else {
683
                    $title = ucwords( str_replace( '_', ' ', $section_name ) );
684
                }
685
686
                $class       = ( $section_name === $section ) ? ' class="current"' : '';
687
                $href        = add_query_arg( array( 'tab' => $tab, 'section' => $section_name ) );
688
                $href        = esc_url( $href );
689
                $san_section = esc_attr( $section_name );
690
                echo "\t<a name='$san_section' href='$href' $class>$title</a>\n";
691
            }
692
693
            echo "</div>\n";
694
695
            ?>
696
        <div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'>
697
            <div class="fyi">
698
                <?php if ( $api->is_paid ) : ?>
699
                    <?php if ( isset( $api->plans ) ) : ?>
700
                        <div class="plugin-information-pricing">
701
                        <?php foreach ( $api->plans as $plan ) : ?>
702
                            <?php
703
                            if ( empty( $plan->pricing ) ) {
704
                                continue;
705
                            }
706
707
                            /**
708
                             * @var FS_Plugin_Plan $plan
709
                             */
710
                            ?>
711
                            <?php $first_pricing = $plan->pricing[0] ?>
712
                            <?php $is_multi_cycle = $first_pricing->is_multi_cycle() ?>
713
                            <div class="fs-plan<?php if ( ! $is_multi_cycle ) {
714
                                echo ' fs-single-cycle';
715
                            } ?>" data-plan-id="<?php echo $plan->id ?>">
716
                                <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>
717
                                <?php $has_annual = $first_pricing->has_annual() ?>
718
                                <?php $has_monthly = $first_pricing->has_monthly() ?>
719
                                <div class="nav-tab-wrapper">
720
                                    <?php $billing_cycles = array( 'monthly', 'annual', 'lifetime' ) ?>
721
                                    <?php $i = 0;
722
                                        foreach ( $billing_cycles as $cycle ) : ?>
723
                                            <?php $prop = "{$cycle}_price";
724
                                            if ( isset( $first_pricing->{$prop} ) ) : ?>
725
                                                <?php $is_featured = ( 'annual' === $cycle && $is_multi_cycle ) ?>
726
                                                <?php
727
                                                $prices = array();
728
                                                foreach ( $plan->pricing as $pricing ) {
729
                                                    if ( isset( $pricing->{$prop} ) ) {
730
                                                        $prices[] = array(
731
                                                            'id'       => $pricing->id,
732
                                                            'licenses' => $pricing->licenses,
733
                                                            'price'    => $pricing->{$prop}
734
                                                        );
735
                                                    }
736
                                                }
737
                                                ?>
738
                                                <a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>"
739
                                                   data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>">
740
                                                    <?php if ( $is_featured ) : ?>
741
                                                        <label>
742
                                                            &#9733; <?php fs_esc_html_echo_x_inline( 'Best', 'e.g. the best product', 'best', $api->slug ) ?>
743
                                                            &#9733;</label>
744
                                                    <?php endif ?>
745
                                                    <?php
746
                                                        switch ( $cycle ) {
747
                                                            case 'monthly':
748
                                                                fs_esc_html_echo_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug );
749
                                                                break;
750
                                                            case 'annual':
751
                                                                fs_esc_html_echo_x_inline( 'Annual', 'as once a year', 'annual', $api->slug );
752
                                                                break;
753
                                                            case 'lifetime':
754
                                                                fs_esc_html_echo_inline( 'Lifetime', 'lifetime', $api->slug );
755
                                                                break;
756
                                                        }
757
                                                    ?>
758
                                                </a>
759
                                            <?php endif ?>
760
                                            <?php $i ++; endforeach ?>
761
                                    <?php wp_enqueue_script( 'jquery' ) ?>
762
                                    <script type="text/javascript">
763
                                        (function ($, undef) {
764
                                            var
765
                                                _formatBillingFrequency = function (cycle) {
766
                                                    switch (cycle) {
767
                                                        case 'monthly':
768
                                                            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 ) ) ?>';
769
                                                        case 'annual':
770
                                                            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 ) ) ?>';
771
                                                        case 'lifetime':
772
                                                            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 ) ) ?>';
773
                                                    }
774
                                                },
775
                                                _formatLicensesTitle    = function (pricing) {
776
                                                    switch (pricing.licenses) {
777
                                                        case 1:
778
                                                            return '<?php fs_esc_attr_echo_inline( 'Single Site License', 'license-single-site', $api->slug ) ?>';
779
                                                        case null:
780
                                                            return '<?php fs_esc_attr_echo_inline( 'Unlimited Licenses', 'license-unlimited', $api->slug ) ?>';
781
                                                        default:
782
                                                            return '<?php fs_esc_attr_echo_inline( 'Up to %s Sites', 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses);
783
                                                    }
784
                                                },
785
                                                _formatPrice            = function (pricing, cycle, multipleLicenses) {
786
                                                    if (undef === multipleLicenses)
787
                                                        multipleLicenses = true;
788
789
                                                    var priceCycle;
790
                                                    switch (cycle) {
791
                                                        case 'monthly':
792
                                                            priceCycle = ' / <?php fs_echo_x_inline( 'mo', 'as monthly period', 'mo', $api->slug ) ?>';
793
                                                            break;
794
                                                        case 'lifetime':
795
                                                            priceCycle = '';
796
                                                            break;
797
                                                        case 'annual':
798
                                                        default:
799
                                                            priceCycle = ' / <?php fs_echo_x_inline( 'year', 'as annual period', 'year', $api->slug ) ?>';
800
                                                            break;
801
                                                    }
802
803
                                                    if (!multipleLicenses && 1 == pricing.licenses) {
804
                                                        return '$' + pricing.price + priceCycle;
805
                                                    }
806
807
                                                    return _formatLicensesTitle(pricing) + ' - <var class="fs-price">$' + pricing.price + priceCycle + '</var>';
808
                                                },
809
                                                _checkoutUrl            = function (plan, pricing, cycle) {
810
                                                    return '<?php echo esc_url_raw( remove_query_arg( 'billing_cycle', add_query_arg( array( 'plugin_id' => $plan->plugin_id ), $api->checkout_link ) ) ) ?>' +
811
                                                        '&plan_id=' + plan +
812
                                                        '&pricing_id=' + pricing +
813
                                                        '&billing_cycle=' + cycle<?php if ( $plan->has_trial() ) {
814
                                                        echo " + '&trial=true'";
815
                                                    }?>;
816
                                                },
817
                                                _updateCtaUrl           = function (plan, pricing, cycle) {
818
                                                    $('.plugin-information-pricing .button, #plugin-information-footer .button.fs-checkout-button').attr('href', _checkoutUrl(plan, pricing, cycle));
819
                                                };
820
821
                                            $(document).ready(function () {
822
                                                var $plan = $('.plugin-information-pricing .fs-plan[data-plan-id=<?php echo $plan->id ?>]');
823
                                                $plan.find('input[type=radio]').live('click', function () {
824
                                                    _updateCtaUrl(
825
                                                        $plan.attr('data-plan-id'),
826
                                                        $(this).val(),
827
                                                        $plan.find('.nav-tab-active').attr('data-billing-cycle')
828
                                                    );
829
830
                                                    $plan.find('.fs-trial-terms .fs-price').html(
831
                                                        $(this).parents('label').find('.fs-price').html()
832
                                                    );
833
                                                });
834
835
                                                $plan.find('.nav-tab').click(function () {
836
                                                    if ($(this).hasClass('nav-tab-active'))
837
                                                        return;
838
839
                                                    var $this        = $(this),
840
                                                        billingCycle = $this.attr('data-billing-cycle'),
841
                                                        pricing      = JSON.parse($this.attr('data-pricing')),
842
                                                        $pricesList  = $this.parents('.fs-plan').find('.fs-pricing-body .fs-licenses'),
843
                                                        html         = '';
844
845
                                                    // Un-select previously selected tab.
846
                                                    $plan.find('.nav-tab').removeClass('nav-tab-active');
847
848
                                                    // Select current tab.
849
                                                    $this.addClass('nav-tab-active');
850
851
                                                    // Render licenses prices.
852
                                                    if (1 == pricing.length) {
853
                                                        html = '<li><label><?php echo fs_esc_attr_x_inline( 'Price', 'noun', 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>';
854
                                                    } else {
855
                                                        for (var i = 0; i < pricing.length; i++) {
856
                                                            html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>';
857
                                                        }
858
                                                    }
859
                                                    $pricesList.html(html);
860
861
                                                    if (1 < pricing.length) {
862
                                                        // Select first license option.
863
                                                        $pricesList.find('li:first input').click();
864
                                                    }
865
                                                    else {
866
                                                        _updateCtaUrl(
867
                                                            $plan.attr('data-plan-id'),
868
                                                            pricing[0].id,
869
                                                            billingCycle
870
                                                        );
871
                                                    }
872
873
                                                    // Update billing frequency.
874
                                                    $plan.find('.fs-billing-frequency').html(_formatBillingFrequency(billingCycle));
875
876
                                                    if ('annual' === billingCycle) {
877
                                                        $plan.find('.fs-annual-discount').show();
878
                                                    } else {
879
                                                        $plan.find('.fs-annual-discount').hide();
880
                                                    }
881
                                                });
882
883
                                                <?php if ( $has_annual ) : ?>
884
                                                // Select annual by default.
885
                                                $plan.find('.nav-tab[data-billing-cycle=annual]').click();
886
                                                <?php else : ?>
887
                                                // Select first tab.
888
                                                $plan.find('.nav-tab:first').click();
889
                                                <?php endif ?>
890
                                            });
891
                                        }(jQuery));
892
                                    </script>
893
                                </div>
894
                                <div class="fs-pricing-body">
895
                                    <span class="fs-billing-frequency"></span>
896
                                    <?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?>
897
                                    <?php if ( $annual_discount > 0 ) : ?>
898
                                        <span
899
                                            class="fs-annual-discount"><?php printf(
900
                                            /* translators: %s: Discount (e.g. discount of $5 or 10%) */
901
                                                fs_esc_html_inline( 'Save %s', 'save-x', $api->slug ), $annual_discount . '%' ) ?></span>
902
                                    <?php endif ?>
903
                                    <ul class="fs-licenses">
904
                                    </ul>
905
                                    <?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...
906
                                    <div style="clear:both"></div>
907
                                    <?php if ( $plan->has_trial() ) : ?>
908
                                        <?php $trial_period = $this->get_trial_period( $plan ) ?>
909
                                        <ul class="fs-trial-terms">
910
                                            <li>
911
                                                <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 ) ) ?>
912
                                            </li>
913
                                            <li>
914
                                                <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>' ) ?>
915
                                            </li>
916
                                        </ul>
917
                                    <?php endif ?>
918
                                </div>
919
                            </div>
920
                            </div>
921
                        <?php endforeach ?>
922
                    <?php endif ?>
923
                <?php endif ?>
924
                <div>
925
                    <h3><?php fs_echo_inline( 'Details', 'details', $api->slug ) ?></h3>
926
                    <ul>
927
                        <?php if ( ! empty( $api->version ) ) { ?>
928
                            <li>
929
                                <strong><?php fs_esc_html_echo_x_inline( 'Version', 'product version', 'version', $api->slug ); ?>
930
                                    :</strong> <?php echo $api->version; ?></li>
931
                            <?php
932
                        }
933
                            if ( ! empty( $api->author ) ) {
934
                                ?>
935
                                <li>
936
                                    <strong><?php fs_echo_x_inline( 'Author', 'as the plugin author', 'author', $api->slug ); ?>
937
                                        :</strong> <?php echo links_add_target( $api->author, '_blank' ); ?>
938
                                </li>
939
                                <?php
940
                            }
941
                            if ( ! empty( $api->last_updated ) ) {
942
                                ?>
943
                                <li><strong><?php fs_echo_inline( 'Last Updated', 'last-updated', $api->slug ); ?>
944
                                        :</strong> <span
945
                                        title="<?php echo $api->last_updated; ?>">
946
				<?php echo esc_html( sprintf(
947
                /* translators: %s: time period (e.g. "2 hours" ago) */
948
                    fs_text_x_inline( '%s ago', 'x-ago', $api->slug ),
949
                    human_time_diff( strtotime( $api->last_updated ) )
950
                ) ) ?>
951
			</span></li>
952
                                <?php
953
                            }
954
                            if ( ! empty( $api->requires ) ) {
955
                                ?>
956
                                <li>
957
                                    <strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>
958
                                        :</strong> <?php echo esc_html( sprintf( fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires ) ) ?>
959
                                </li>
960
                                <?php
961
                            }
962
                            if ( ! empty( $api->tested ) ) {
963
                                ?>
964
                                <li>
965
                                    <strong><?php fs_esc_html_echo_inline( 'Compatible up to', 'compatible-up-to', $api->slug ); ?>
966
                                        :</strong> <?php echo $api->tested; ?>
967
                                </li>
968
                                <?php
969
                            }
970
                            if ( ! empty( $api->downloaded ) ) {
971
                                ?>
972
                                <li>
973
                                    <strong><?php fs_esc_html_echo_inline( 'Downloaded', 'downloaded', $api->slug ) ?>
974
                                        :</strong> <?php echo esc_html( sprintf(
975
                                        ( ( 1 == $api->downloaded ) ?
976
                                            /* translators: %s: 1 or One (Number of times downloaded) */
977
                                            fs_text_inline( '%s time', 'x-time', $api->slug ) :
978
                                            /* translators: %s: Number of times downloaded */
979
                                            fs_text_inline( '%s times', 'x-times', $api->slug )
980
                                        ),
981
                                        number_format_i18n( $api->downloaded )
982
                                    ) ); ?>
983
                                </li>
984
                                <?php
985
                            }
986
                            if ( ! empty( $api->slug ) && true == $api->is_wp_org_compliant ) {
987
                                ?>
988
                                <li><a target="_blank"
989
                                       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 ) ?>
990
                                        &#187;</a>
991
                                </li>
992
                                <?php
993
                            }
994
                            if ( ! empty( $api->homepage ) ) {
995
                                ?>
996
                                <li><a target="_blank"
997
                                       href="<?php echo esc_url( $api->homepage ); ?>"><?php fs_esc_html_echo_inline( 'Plugin Homepage', 'plugin-homepage', $api->slug ) ?>
998
                                        &#187;</a>
999
                                </li>
1000
                                <?php
1001
                            }
1002
                            if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) {
1003
                                ?>
1004
                                <li><a target="_blank"
1005
                                       href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_esc_html_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
1006
                                        &#187;</a>
1007
                                </li>
1008
                            <?php } ?>
1009
                    </ul>
1010
                </div>
1011
                <?php if ( ! empty( $api->rating ) ) { ?>
1012
                    <h3><?php fs_echo_inline( 'Average Rating', 'average-rating', $api->slug ); ?></h3>
1013
                    <?php wp_star_rating( array(
1014
                        'rating' => $api->rating,
1015
                        'type'   => 'percent',
1016
                        'number' => $api->num_ratings
1017
                    ) ); ?>
1018
                    <small>(<?php echo esc_html( sprintf(
1019
                            fs_text_inline( 'based on %s', 'based-on-x', $api->slug ),
1020
                            sprintf(
1021
                                ( ( 1 == $api->num_ratings ) ?
1022
                                    /* translators: %s: 1 or One */
1023
                                    fs_text_inline( '%s rating', 'x-rating', $api->slug ) :
1024
                                    /* translators: %s: Number larger than 1 */
1025
                                    fs_text_inline( '%s ratings', 'x-ratings', $api->slug )
1026
                                ),
1027
                                number_format_i18n( $api->num_ratings )
1028
                            ) ) ) ?>)
1029
                    </small>
1030
                    <?php
1031
                }
1032
1033
                    if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) {
1034
                        foreach ( $api->ratings as $key => $ratecount ) {
1035
                            // Avoid div-by-zero.
1036
                            $_rating     = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
1037
                            $stars_label = sprintf(
1038
                                ( ( 1 == $key ) ?
1039
                                    /* translators: %s: 1 or One */
1040
                                    fs_text_inline( '%s star', 'x-star', $api->slug ) :
1041
                                    /* translators: %s: Number larger than 1 */
1042
                                    fs_text_inline( '%s stars', 'x-stars', $api->slug )
1043
                                ),
1044
                                number_format_i18n( $key )
1045
                            );
1046
                            ?>
1047
                            <div class="counter-container">
1048
					<span class="counter-label"><a
1049
                            href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
1050
                            target="_blank"
1051
                            title="<?php echo esc_attr( sprintf(
1052
                            /* translators: %s: # of stars (e.g. 5 stars) */
1053
                                fs_text_inline( 'Click to see reviews that provided a rating of %s', 'click-to-reviews', $api->slug ),
1054
                                $stars_label
1055
                            ) ) ?>"><?php echo $stars_label ?></a></span>
1056
                                <span class="counter-back">
1057
						<span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
1058
					</span>
1059
                                <span class="counter-count"><?php echo number_format_i18n( $ratecount ); ?></span>
1060
                            </div>
1061
                            <?php
1062
                        }
1063
                    }
1064
                    if ( ! empty( $api->contributors ) ) {
1065
                        ?>
1066
                        <h3><?php fs_echo_inline( 'Contributors', 'contributors', $api->slug ); ?></h3>
1067
                        <ul class="contributors">
1068
                            <?php
1069
                                foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) {
1070
                                    if ( empty( $contrib_username ) && empty( $contrib_profile ) ) {
1071
                                        continue;
1072
                                    }
1073
                                    if ( empty( $contrib_username ) ) {
1074
                                        $contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile );
1075
                                    }
1076
                                    $contrib_username = sanitize_user( $contrib_username );
1077
                                    if ( empty( $contrib_profile ) ) {
1078
                                        echo "<li><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&amp;s=36' width='18' height='18' />{$contrib_username}</li>";
1079
                                    } else {
1080
                                        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>";
1081
                                    }
1082
                                }
1083
                            ?>
1084
                        </ul>
1085
                        <?php if ( ! empty( $api->donate_link ) ) { ?>
1086
                            <a target="_blank"
1087
                               href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
1088
                                &#187;</a>
1089
                        <?php } ?>
1090
                    <?php } ?>
1091
            </div>
1092
            <div id="section-holder" class="wrap">
1093
            <?php
1094
            if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
1095
                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>';
1096
            } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
1097
                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>';
1098
            }
1099
1100
            foreach ( (array) $api->sections as $section_name => $content ) {
1101
                $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' );
1102
                $content = links_add_target( $content, '_blank' );
1103
1104
                $san_section = esc_attr( $section_name );
1105
1106
                $display = ( $section_name === $section ) ? 'block' : 'none';
1107
1108
                if ( 'description' === $section_name &&
1109
                     ( ( $api->is_wp_org_compliant && $api->wp_org_missing ) ||
1110
                       ( ! $api->is_wp_org_compliant && $api->fs_missing ) )
1111
                ) {
1112
                    $missing_notice = array(
1113
                        'type'    => 'error',
1114
                        'id'      => md5( microtime() ),
1115
                        'message' => $api->is_paid ?
1116
                            fs_text_inline( 'Paid add-on must be deployed to Freemius.', 'paid-addon-not-deployed', $api->slug ) :
1117
                            fs_text_inline( 'Add-on must be deployed to WordPress.org or Freemius.', 'free-addon-not-deployed', $api->slug ),
1118
                    );
1119
                    fs_require_template( 'admin-notice.php', $missing_notice );
1120
                }
1121
                echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n";
1122
                echo $content;
1123
                echo "\t</div>\n";
1124
            }
1125
            echo "</div>\n";
1126
            echo "</div>\n";
1127
            echo "</div>\n"; // #plugin-information-scrollable
1128
            echo "<div id='$tab-footer'>\n";
1129
1130
            if ( $api->has_paid_plan && ! empty( $api->checkout_link ) ) {
1131
                echo $this->get_checkout_cta( $api );
1132
            }
1133
1134
            if ( ! empty( $api->download_link ) ) {
1135
                echo $this->get_download_cta( $api, empty( $api->checkout_link ) );
1136
            }
1137
1138
            echo "</div>\n";
1139
1140
            iframe_footer();
1141
            exit;
1142
        }
1143
    }