PluginProbe
Tiered Pricing Table for WooCommerce / 2.3.1
Tiered Pricing Table for WooCommerce v2.3.1
7.1.7 7.1.5 7.1.4 7.1.1 6.5.0 6.4.0 6.1.0 trunk 1.0 1.1 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 All 90 releases
tier-pricing-table / freemius / includes / fs-plugin-info-dialog.php

fs-plugin-info-dialog.php in Tiered Pricing Table for WooCommerce 2.3.1, at freemius/includes/fs-plugin-info-dialog.php

1,610 lines 77.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
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 {
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 /**
35 * Collection of plugin installation, update, download, activation, and purchase actions. This is used in
36 * populating the actions dropdown list when there are at least 2 actions. If there's only 1 action, a button
37 * is used instead.
38 *
39 * @author Leo Fajardo (@leorw)
40 * @since 2.3.0
41 *
42 * @var string[]
43 */
44 private $actions;
45
46 /**
47 * Contains plugin status information that is used to determine which actions should be part of the actions
48 * dropdown list.
49 *
50 * @author Leo Fajardo (@leorw)
51 * @since 2.3.0
52 *
53 * @var string[]
54 */
55 private $status;
56
57 function __construct( Freemius $fs ) {
58 $this->_fs = $fs;
59
60 $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $fs->get_slug() . '_info', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
61
62 // Remove default plugin information action.
63 remove_all_actions( 'install_plugins_pre_plugin-information' );
64
65 // Override action with custom plugins function for add-ons.
66 add_action( 'install_plugins_pre_plugin-information', array( &$this, 'install_plugin_information' ) );
67
68 // Override request for plugin information for Add-ons.
69 add_filter(
70 'fs_plugins_api',
71 array( &$this, '_get_addon_info_filter' ),
72 WP_FS__DEFAULT_PRIORITY, 3 );
73 }
74
75 /**
76 * Generate add-on plugin information.
77 *
78 * @author Vova Feldman (@svovaf)
79 * @since 1.0.6
80 *
81 * @param array $data
82 * @param string $action
83 * @param object|null $args
84 *
85 * @return array|null
86 */
87 function _get_addon_info_filter( $data, $action = '', $args = null ) {
88 $this->_logger->entrance();
89
90 $parent_plugin_id = fs_request_get( 'parent_plugin_id', $this->_fs->get_id() );
91
92 if ( $this->_fs->get_id() != $parent_plugin_id ||
93 ( 'plugin_information' !== $action ) ||
94 ! isset( $args->slug )
95 ) {
96 return $data;
97 }
98
99 // Find add-on by slug.
100 $selected_addon = $this->_fs->get_addon_by_slug( $args->slug, WP_FS__DEV_MODE );
101
102 if ( false === $selected_addon ) {
103 return $data;
104 }
105
106 if ( ! isset( $selected_addon->info ) ) {
107 // Setup some default info.
108 $selected_addon->info = new stdClass();
109 $selected_addon->info->selling_point_0 = 'Selling Point 1';
110 $selected_addon->info->selling_point_1 = 'Selling Point 2';
111 $selected_addon->info->selling_point_2 = 'Selling Point 3';
112 $selected_addon->info->description = '<p>Tell your users all about your add-on</p>';
113 }
114
115 fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' );
116
117 $data = $args;
118
119 $has_free_plan = false;
120 $has_paid_plan = false;
121
122 // Load add-on pricing.
123 $has_pricing = false;
124 $has_features = false;
125 $plans = false;
126
127 $result = $this->_fs->get_api_plugin_scope()->get( $this->_fs->add_show_pending( "/addons/{$selected_addon->id}/pricing.json?type=visible" ) );
128
129 if ( ! isset( $result->error ) ) {
130 $plans = $result->plans;
131
132 if ( is_array( $plans ) ) {
133 for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
134 $pricing = isset( $plans[ $i ]->pricing ) ? $plans[ $i ]->pricing : null;
135 $features = isset( $plans[ $i ]->features ) ? $plans[ $i ]->features : null;
136
137 $plans[ $i ] = new FS_Plugin_Plan( $plans[ $i ] );
138 $plan = $plans[ $i ];
139
140 if ( 'free' == $plans[ $i ]->name ||
141 ! is_array( $pricing ) ||
142 0 == count( $pricing )
143 ) {
144 $has_free_plan = true;
145 }
146
147 if ( is_array( $pricing ) && 0 < count( $pricing ) ) {
148 $has_paid_plan = true;
149
150 foreach ( $pricing as &$prices ) {
151 $prices = new FS_Pricing( $prices );
152 }
153
154 $plan->pricing = $pricing;
155
156 $has_pricing = true;
157 }
158
159 if ( is_array( $features ) && 0 < count( $features ) ) {
160 $plan->features = $features;
161
162 $has_features = true;
163 }
164 }
165 }
166 }
167
168 $latest = null;
169
170 if ( ! $has_paid_plan && $selected_addon->is_wp_org_compliant ) {
171 $repo_data = FS_Plugin_Updater::_fetch_plugin_info_from_repository(
172 'plugin_information', (object) array(
173 'slug' => $selected_addon->slug,
174 'is_ssl' => is_ssl(),
175 'fields' => array(
176 'banners' => true,
177 'reviews' => true,
178 'downloaded' => false,
179 'active_installs' => true
180 )
181 ) );
182
183 if ( ! empty( $repo_data ) ) {
184 $data = $repo_data;
185 $data->wp_org_missing = false;
186 } else {
187 // Couldn't find plugin on .org.
188 $selected_addon->is_wp_org_compliant = false;
189
190 // Plugin is missing, not on Freemius nor WP.org.
191 $data->wp_org_missing = true;
192 }
193
194 $data->fs_missing = ( ! $has_free_plan || $data->wp_org_missing );
195 } else {
196 $data->has_purchased_license = false;
197 $data->wp_org_missing = false;
198
199 $fs_addon = null;
200 $current_addon_version = false;
201 if ( $this->_fs->is_addon_activated( $selected_addon->id ) ) {
202 $fs_addon = $this->_fs->get_addon_instance( $selected_addon->id );
203 $current_addon_version = $fs_addon->get_plugin_version();
204 } else if ( $this->_fs->is_addon_installed( $selected_addon->id ) ) {
205 $addon_plugin_data = get_plugin_data(
206 ( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $selected_addon->id ) ),
207 false,
208 false
209 );
210
211 if ( ! empty( $addon_plugin_data ) ) {
212 $current_addon_version = $addon_plugin_data['Version'];
213 }
214 }
215
216 // Fetch latest version from Freemius.
217 $latest = $this->_fs->_fetch_latest_version(
218 $selected_addon->id,
219 true,
220 WP_FS__TIME_24_HOURS_IN_SEC,
221 $current_addon_version
222 );
223
224 if ( $has_paid_plan ) {
225 $blog_id = fs_request_get( 'fs_blog_id' );
226 $has_valid_blog_id = is_numeric( $blog_id );
227
228 if ( $has_valid_blog_id ) {
229 switch_to_blog( $blog_id );
230 }
231
232 $data->checkout_link = $this->_fs->checkout_url(
233 WP_FS__PERIOD_ANNUALLY,
234 false,
235 array(),
236 ( $has_valid_blog_id ? false : null )
237 );
238
239 if ( $has_valid_blog_id ) {
240 restore_current_blog();
241 }
242
243 if ( is_object( $fs_addon ) ) {
244 $data->has_purchased_license = $fs_addon->has_active_valid_license();
245 } else {
246 $account_addons = $this->_fs->get_account_addons();
247 if ( ! empty( $account_addons ) && in_array( $selected_addon->id, $account_addons ) ) {
248 $data->has_purchased_license = true;
249 }
250 }
251 }
252
253 if ( $has_free_plan || $data->has_purchased_license ) {
254 $data->download_link = $this->_fs->_get_latest_download_local_url( $selected_addon->id );
255 }
256
257 $data->fs_missing = (
258 false === $latest &&
259 (
260 empty( $selected_addon->premium_releases_count ) ||
261 ! ( $selected_addon->premium_releases_count > 0 )
262 )
263 );
264
265 // Fetch as much as possible info from local files.
266 $plugin_local_data = $this->_fs->get_plugin_data();
267 $data->author = $plugin_local_data['Author'];
268
269 if ( ! empty( $selected_addon->info->banner_url ) ) {
270 $data->banners = array(
271 'low' => $selected_addon->info->banner_url,
272 );
273 }
274
275 if ( ! empty( $selected_addon->info->screenshots ) ) {
276 $view_vars = array(
277 'screenshots' => $selected_addon->info->screenshots,
278 'plugin' => $selected_addon,
279 );
280 $data->sections['screenshots'] = fs_get_template( '/plugin-info/screenshots.php', $view_vars );
281 }
282
283 if ( is_object( $latest ) ) {
284 $data->version = $latest->version;
285 $data->last_updated = $latest->created;
286 $data->requires = $latest->requires_platform_version;
287 $data->tested = $latest->tested_up_to_version;
288 } else if ( ! empty( $current_addon_version ) ) {
289 $data->version = $current_addon_version;
290 } else {
291 // Add dummy version.
292 $data->version = '1.0.0';
293
294 // Add message to developer to deploy the plugin through Freemius.
295 }
296 }
297
298 $data->name = $selected_addon->title;
299 $view_vars = array( 'plugin' => $selected_addon );
300
301 if ( is_object( $latest ) && isset( $latest->readme ) && is_object( $latest->readme ) ) {
302 $latest_version_readme_data = $latest->readme;
303 if ( isset( $latest_version_readme_data->sections ) ) {
304 $data->sections = (array) $latest_version_readme_data->sections;
305 } else {
306 $data->sections = array();
307 }
308 }
309
310 $data->sections['description'] = fs_get_template( '/plugin-info/description.php', $view_vars );
311
312 if ( $has_pricing ) {
313 // Add plans to data.
314 $data->plans = $plans;
315
316 if ( $has_features ) {
317 $view_vars = array(
318 'plans' => $plans,
319 'plugin' => $selected_addon,
320 );
321 $data->sections['features'] = fs_get_template( '/plugin-info/features.php', $view_vars );
322 }
323 }
324
325 $data->has_free_plan = $has_free_plan;
326 $data->has_paid_plan = $has_paid_plan;
327 $data->is_paid = $has_paid_plan;
328 $data->is_wp_org_compliant = $selected_addon->is_wp_org_compliant;
329 $data->premium_slug = $selected_addon->premium_slug;
330 $data->addon_id = $selected_addon->id;
331
332 if ( ! isset( $data->has_purchased_license ) ) {
333 $data->has_purchased_license = false;
334 }
335
336 return $data;
337 }
338
339 /**
340 * @author Vova Feldman (@svovaf)
341 * @since 1.1.7
342 *
343 * @param FS_Plugin_Plan $plan
344 *
345 * @return string
346 */
347 private function get_billing_cycle( FS_Plugin_Plan $plan ) {
348 $billing_cycle = null;
349
350 if ( 1 === count( $plan->pricing ) && 1 == $plan->pricing[0]->licenses ) {
351 $pricing = $plan->pricing[0];
352 if ( isset( $pricing->annual_price ) ) {
353 $billing_cycle = 'annual';
354 } else if ( isset( $pricing->monthly_price ) ) {
355 $billing_cycle = 'monthly';
356 } else if ( isset( $pricing->lifetime_price ) ) {
357 $billing_cycle = 'lifetime';
358 }
359 } else {
360 foreach ( $plan->pricing as $pricing ) {
361 if ( isset( $pricing->annual_price ) ) {
362 $billing_cycle = 'annual';
363 } else if ( isset( $pricing->monthly_price ) ) {
364 $billing_cycle = 'monthly';
365 } else if ( isset( $pricing->lifetime_price ) ) {
366 $billing_cycle = 'lifetime';
367 }
368
369 if ( ! is_null( $billing_cycle ) ) {
370 break;
371 }
372 }
373 }
374
375 return $billing_cycle;
376 }
377
378 /**
379 * @author Vova Feldman (@svovaf)
380 * @since 2.0.0
381 *
382 * @param FS_Plugin_Plan $plan
383 * @param FS_Pricing $pricing
384 *
385 * @return float|null|string
386 */
387 private function get_price_tag( FS_Plugin_Plan $plan, FS_Pricing $pricing ) {
388 $price_tag = '';
389 if ( isset( $pricing->annual_price ) ) {
390 $price_tag = $pricing->annual_price . ( $plan->is_block_features ? ' / year' : '' );
391 } else if ( isset( $pricing->monthly_price ) ) {
392 $price_tag = $pricing->monthly_price . ' / mo';
393 } else if ( isset( $pricing->lifetime_price ) ) {
394 $price_tag = $pricing->lifetime_price;
395 }
396
397 return '$' . $price_tag;
398 }
399
400 /**
401 * @author Leo Fajardo (@leorw)
402 * @since 2.3.0
403 *
404 * @param object $api
405 * @param FS_Plugin_Plan $plan
406 *
407 * @return string
408 */
409 private function get_actions_dropdown( $api, $plan = null ) {
410 $this->actions = isset( $this->actions ) ?
411 $this->actions :
412 $this->get_plugin_actions( $api );
413
414 $actions = $this->actions;
415
416 $checkout_cta = $this->get_checkout_cta( $api, $plan );
417 if ( ! empty( $checkout_cta ) ) {
418 /**
419 * If there's no license yet, make the checkout button the main CTA. Otherwise, make it the last item in
420 * the actions dropdown.
421 *
422 * @author Leo Fajardo (@leorw)
423 * @since 2.3.0
424 */
425 if ( ! $api->has_purchased_license ) {
426 array_unshift( $actions, $checkout_cta );
427 } else {
428 $actions[] = $checkout_cta;
429 }
430 }
431
432 if ( empty( $actions ) ) {
433 return '';
434 }
435
436 $total_actions = count( $actions );
437 if ( 1 === $total_actions ) {
438 return $actions[0];
439 }
440
441 ob_start();
442
443 ?>
444 <div class="fs-cta fs-dropdown">
445 <div class="button-group">
446 <?php
447 // This should NOT be sanitized as the $actions are HTML buttons already.
448 echo $actions[0] ?>
449 <div class="button button-primary fs-dropdown-arrow-button">
450 <span class="fs-dropdown-arrow"></span>
451 <ul class="fs-dropdown-list" style="display: none">
452 <?php for ( $i = 1; $i < $total_actions; $i ++ ) : ?>
453 <li><?php echo str_replace( 'button button-primary', '', $actions[ $i ] ) ?></li>
454 <?php endfor ?>
455 </ul>
456 </div>
457 </div>
458 </div>
459 <?php
460
461 return ob_get_clean();
462 }
463
464 /**
465 * @author Vova Feldman (@svovaf)
466 * @since 1.1.7
467 *
468 * @param object $api
469 * @param FS_Plugin_Plan $plan
470 *
471 * @return string
472 */
473 private function get_checkout_cta( $api, $plan = null ) {
474 if ( empty( $api->checkout_link ) ||
475 ! isset( $api->plans ) ||
476 ! is_array( $api->plans ) ||
477 0 == count( $api->plans )
478 ) {
479 return '';
480 }
481
482 if ( is_null( $plan ) ) {
483 foreach ( $api->plans as $p ) {
484 if ( ! empty( $p->pricing ) ) {
485 $plan = $p;
486 break;
487 }
488 }
489 }
490
491 $blog_id = fs_request_get( 'fs_blog_id' );
492 $has_valid_blog_id = is_numeric( $blog_id );
493
494 if ( $has_valid_blog_id ) {
495 switch_to_blog( $blog_id );
496 }
497
498 $addon_checkout_url = $this->_fs->addon_checkout_url(
499 $plan->plugin_id,
500 $plan->pricing[0]->id,
501 $this->get_billing_cycle( $plan ),
502 $plan->has_trial(),
503 ( $has_valid_blog_id ? false : null )
504 );
505
506 if ( $has_valid_blog_id ) {
507 restore_current_blog();
508 }
509
510 return '<a class="button button-primary fs-checkout-button right" href="' . $addon_checkout_url . '" target="_parent">' .
511 esc_html( ! $plan->has_trial() ?
512 (
513 $api->has_purchased_license ?
514 fs_text_inline( 'Purchase More', 'purchase-more', $api->slug ) :
515 fs_text_x_inline( 'Purchase', 'verb', 'purchase', $api->slug )
516 ) :
517 sprintf(
518 /* translators: %s: N-days trial */
519 fs_text_inline( 'Start my free %s', 'start-free-x', $api->slug ),
520 $this->get_trial_period( $plan )
521 )
522 ) .
523 '</a>';
524 }
525
526 /**
527 * @author Leo Fajardo (@leorw)
528 * @since 2.3.0
529 *
530 * @param object $api
531 *
532 * @return string[]
533 */
534 private function get_plugin_actions( $api ) {
535 $this->status = isset( $this->status ) ?
536 $this->status :
537 install_plugin_install_status( $api );
538
539 $is_update_available = ( 'update_available' === $this->status['status'] );
540
541 if ( $is_update_available && empty( $this->status['url'] ) ) {
542 return array();
543 }
544
545 $blog_id = fs_request_get( 'fs_blog_id' );
546
547 $active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $blog_id );
548
549 $actions = array();
550
551 $is_addon_activated = $this->_fs->is_addon_activated( $api->slug );
552 $fs_addon = null;
553
554 $is_free_installed = null;
555 $is_premium_installed = null;
556
557 $has_installed_version = ( 'install' !== $this->status['status'] );
558
559 if ( ! $api->has_paid_plan ) {
560 /**
561 * Free-only add-on.
562 *
563 * @author Leo Fajardo (@leorw)
564 * @since 2.3.0
565 */
566 $is_free_installed = $has_installed_version;
567 $is_premium_installed = false;
568 } else if ( ! $api->has_free_plan ) {
569 /**
570 * Premium-only add-on.
571 *
572 * @author Leo Fajardo (@leorw)
573 * @since 2.3.0
574 */
575 $is_free_installed = false;
576 $is_premium_installed = $has_installed_version;
577 } else {
578 /**
579 * Freemium add-on.
580 *
581 * @author Leo Fajardo (@leorw)
582 * @since 2.3.0
583 */
584 if ( ! $has_installed_version ) {
585 $is_free_installed = false;
586 $is_premium_installed = false;
587 } else {
588 $fs_addon = $is_addon_activated ?
589 $this->_fs->get_addon_instance( $api->slug ) :
590 null;
591
592 if ( is_object( $fs_addon ) ) {
593 if ( $fs_addon->is_premium() ) {
594 $is_premium_installed = true;
595 } else {
596 $is_free_installed = true;
597 }
598 }
599
600 if ( is_null( $is_free_installed ) ) {
601 $is_free_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->slug}/{$api->slug}.php" ) );
602 if ( ! $is_free_installed ) {
603 /**
604 * Check if there's a plugin installed in a directory named `$api->slug`.
605 *
606 * @author Leo Fajardo (@leorw)
607 * @since 2.3.0
608 */
609 $installed_plugins = get_plugins( '/' . $api->slug );
610 $is_free_installed = ( ! empty( $installed_plugins ) );
611 }
612 }
613
614 if ( is_null( $is_premium_installed ) ) {
615 $is_premium_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->premium_slug}/{$api->slug}.php" ) );
616 if ( ! $is_premium_installed ) {
617 /**
618 * Check if there's a plugin installed in a directory named `$api->premium_slug`.
619 *
620 * @author Leo Fajardo (@leorw)
621 * @since 2.3.0
622 */
623 $installed_plugins = get_plugins( '/' . $api->premium_slug );
624 $is_premium_installed = ( ! empty( $installed_plugins ) );
625 }
626 }
627 }
628
629 $has_installed_version = ( $is_free_installed || $is_premium_installed );
630 }
631
632 $this->status['is_free_installed'] = $is_free_installed;
633 $this->status['is_premium_installed'] = $is_premium_installed;
634
635 $can_install_free_version = false;
636 $can_install_free_version_update = false;
637 $can_download_free_version = false;
638 $can_activate_free_version = false;
639 $can_install_premium_version = false;
640 $can_install_premium_version_update = false;
641 $can_download_premium_version = false;
642 $can_activate_premium_version = false;
643
644 if ( ! $api->has_purchased_license ) {
645 if ( $api->has_free_plan ) {
646 if ( $has_installed_version ) {
647 if ( $is_update_available ) {
648 $can_install_free_version_update = true;
649 } else if ( ! $is_premium_installed && ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) {
650 $can_activate_free_version = true;
651 }
652 } else {
653 if (
654 $this->_fs->is_premium() ||
655 ! $this->_fs->is_org_repo_compliant() ||
656 $api->is_wp_org_compliant
657 ) {
658 $can_install_free_version = true;
659 } else {
660 $can_download_free_version = true;
661 }
662 }
663 }
664 } else {
665 if ( ! is_object( $fs_addon ) && $is_addon_activated ) {
666 $fs_addon = $this->_fs->get_addon_instance( $api->slug );
667 }
668
669 $can_download_premium_version = true;
670
671 if ( ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) {
672 if ( $is_premium_installed ) {
673 $can_activate_premium_version = ( ! $is_addon_activated || ! $fs_addon->is_premium() );
674 } else if ( $is_free_installed ) {
675 $can_activate_free_version = ( ! $is_addon_activated );
676 }
677 }
678
679 if ( $this->_fs->is_premium() || ! $this->_fs->is_org_repo_compliant() ) {
680 if ( $is_update_available ) {
681 $can_install_premium_version_update = true;
682 } else if ( ! $is_premium_installed ) {
683 $can_install_premium_version = true;
684 }
685 }
686 }
687
688 if (
689 $can_install_premium_version ||
690 $can_install_premium_version_update
691 ) {
692 if ( is_numeric( $blog_id ) ) {
693 /**
694 * Replace the network status URL with a blog admin–based status URL if the `Add-Ons` page is loaded
695 * from a specific blog admin page (when `fs_blog_id` is valid) in order for plugin installation/update
696 * to work.
697 *
698 * @author Leo Fajardo (@leorw)
699 * @since 2.3.0
700 */
701 $this->status['url'] = self::get_blog_status_url( $blog_id, $this->status['url'], $this->status['status'] );
702 }
703
704 /**
705 * Add the `fs_allow_updater_and_dialog` param to the install/update URL so that the add-on can be
706 * installed/updated.
707 *
708 * @author Leo Fajardo (@leorw)
709 * @since 2.3.0
710 */
711 $this->status['url'] = str_replace( '?', '?fs_allow_updater_and_dialog=true&amp;', $this->status['url'] );
712 }
713
714 if ( $can_install_free_version_update || $can_install_premium_version_update ) {
715 $actions[] = $this->get_cta(
716 ( $can_install_free_version_update ?
717 fs_esc_html_inline( 'Install Free Version Update Now', 'install-free-version-update-now', $api->slug ) :
718 fs_esc_html_inline( 'Install Update Now', 'install-update-now', $api->slug ) ),
719 true,
720 false,
721 $this->status['url'],
722 '_parent'
723 );
724 } else if ( $can_install_free_version || $can_install_premium_version ) {
725 $actions[] = $this->get_cta(
726 ( $can_install_free_version ?
727 fs_esc_html_inline( 'Install Free Version Now', 'install-free-version-now', $api->slug ) :
728 fs_esc_html_inline( 'Install Now', 'install-now', $api->slug ) ),
729 true,
730 false,
731 $this->status['url'],
732 '_parent'
733 );
734 }
735
736 $download_latest_action = '';
737
738 if (
739 ! empty( $api->download_link ) &&
740 ( $can_download_free_version || $can_download_premium_version )
741 ) {
742 $download_latest_action = $this->get_cta(
743 ( $can_download_free_version ?
744 fs_esc_html_x_inline( 'Download Latest Free Version', 'as download latest version', 'download-latest-free-version', $api->slug ) :
745 fs_esc_html_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $api->slug ) ),
746 true,
747 false,
748 esc_url( $api->download_link )
749 );
750 }
751
752 if ( ! $can_activate_free_version && ! $can_activate_premium_version ) {
753 if ( ! empty( $download_latest_action ) ) {
754 $actions[] = $download_latest_action;
755 }
756 } else {
757 $activate_action = sprintf(
758 '<a class="button button-primary edit" href="%s" title="%s" target="_parent">%s</a>',
759 wp_nonce_url( ( is_numeric( $blog_id ) ? trailingslashit( get_admin_url( $blog_id ) ) : '' ) . 'plugins.php?action=activate&amp;plugin=' . $this->status['file'], 'activate-plugin_' . $this->status['file'] ),
760 fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $api->slug ),
761 $can_activate_free_version ?
762 fs_text_inline( 'Activate Free Version', 'activate-free', $api->slug ) :
763 fs_text_inline( 'Activate', 'activate', $api->slug )
764 );
765
766 if ( ! $can_download_premium_version && ! empty( $download_latest_action ) ) {
767 $actions[] = $download_latest_action;
768
769 $download_latest_action = '';
770 }
771
772 if ( $can_install_premium_version || $can_install_premium_version_update ) {
773 if ( $can_download_premium_version && ! empty( $download_latest_action ) ) {
774 $actions[] = $download_latest_action;
775
776 $download_latest_action = '';
777 }
778
779 $actions[] = $activate_action;
780 } else {
781 array_unshift( $actions, $activate_action );
782 }
783
784 if ( ! empty ($download_latest_action ) ) {
785 $actions[] = $download_latest_action;
786 }
787 }
788
789 return $actions;
790 }
791
792 /**
793 * Rebuilds the status URL based on the admin URL.
794 *
795 * @author Leo Fajardo (@leorw)
796 * @since 2.3.0
797 *
798 * @param int $blog_id
799 * @param string $network_status_url
800 * @param string $status
801 *
802 * @return string
803 */
804 private static function get_blog_status_url( $blog_id, $network_status_url, $status ) {
805 if ( ! in_array( $status, array( 'install', 'update_available' ) ) ) {
806 return $network_status_url;
807 }
808
809 $action = ( 'install' === $status ) ?
810 'install-plugin' :
811 'upgrade-plugin';
812
813 $query = parse_url( $network_status_url, PHP_URL_QUERY );
814 if ( empty( $query ) ) {
815 return $network_status_url;
816 }
817
818 parse_str( html_entity_decode( $query ), $url_params );
819 if ( empty( $url_params ) || ! isset( $url_params['plugin'] ) ) {
820 return $network_status_url;
821 }
822
823 $plugin = $url_params['plugin'];
824
825 return wp_nonce_url( get_admin_url( $blog_id,"update.php?action={$action}&plugin={$plugin}"), "{$action}_{$plugin}");
826 }
827
828 /**
829 * Helper method to get a CTA button HTML.
830 *
831 * @author Vova Feldman (@svovaf)
832 * @since 2.0.0
833 *
834 * @param string $label
835 * @param bool $is_primary
836 * @param bool $is_disabled
837 * @param string $href
838 * @param string $target
839 *
840 * @return string
841 */
842 private function get_cta(
843 $label,
844 $is_primary = true,
845 $is_disabled = false,
846 $href = '',
847 $target = '_blank'
848 ) {
849 $classes = array();
850
851 if ( ! $is_primary ) {
852 $classes[] = 'left';
853 } else {
854 $classes[] = 'button-primary';
855 $classes[] = 'right';
856 }
857
858 if ( $is_disabled ) {
859 $classes[] = 'disabled';
860 }
861
862 return sprintf(
863 '<a %s class="button %s">%s</a>',
864 empty( $href ) ? '' : 'href="' . $href . '" target="' . $target . '"',
865 implode( ' ', $classes ),
866 $label
867 );
868 }
869
870 /**
871 * @author Vova Feldman (@svovaf)
872 * @since 1.1.7
873 *
874 * @param FS_Plugin_Plan $plan
875 *
876 * @return string
877 */
878 private function get_trial_period( $plan ) {
879 $trial_period = (int) $plan->trial_period;
880
881 switch ( $trial_period ) {
882 case 30:
883 return 'month';
884 case 60:
885 return '2 months';
886 default:
887 return "{$plan->trial_period} days";
888 }
889 }
890
891 /**
892 * Display plugin information in dialog box form.
893 *
894 * Based on core install_plugin_information() function.
895 *
896 * @author Vova Feldman (@svovaf)
897 * @since 1.0.6
898 */
899 function install_plugin_information() {
900 global $tab;
901
902 if ( empty( $_REQUEST['plugin'] ) ) {
903 return;
904 }
905
906 $args = array(
907 'slug' => wp_unslash( $_REQUEST['plugin'] ),
908 'is_ssl' => is_ssl(),
909 'fields' => array(
910 'banners' => true,
911 'reviews' => true,
912 'downloaded' => false,
913 'active_installs' => true
914 )
915 );
916
917 if ( is_array( $args ) ) {
918 $args = (object) $args;
919 }
920
921 if ( ! isset( $args->per_page ) ) {
922 $args->per_page = 24;
923 }
924
925 if ( ! isset( $args->locale ) ) {
926 $args->locale = get_locale();
927 }
928
929 $api = apply_filters( 'fs_plugins_api', false, 'plugin_information', $args );
930
931 if ( is_wp_error( $api ) ) {
932 wp_die( $api );
933 }
934
935 $plugins_allowedtags = array(
936 'a' => array(
937 'href' => array(),
938 'title' => array(),
939 'target' => array(),
940 // Add image style for screenshots.
941 'class' => array()
942 ),
943 'style' => array(),
944 'abbr' => array( 'title' => array() ),
945 'acronym' => array( 'title' => array() ),
946 'code' => array(),
947 'pre' => array(),
948 'em' => array(),
949 'strong' => array(),
950 'div' => array( 'class' => array() ),
951 'span' => array( 'class' => array() ),
952 'p' => array(),
953 'ul' => array(),
954 'ol' => array(),
955 'li' => array( 'class' => array() ),
956 'i' => array( 'class' => array() ),
957 'h1' => array(),
958 'h2' => array(),
959 'h3' => array(),
960 'h4' => array(),
961 'h5' => array(),
962 'h6' => array(),
963 'img' => array( 'src' => array(), 'class' => array(), 'alt' => array() ),
964 // 'table' => array(),
965 // 'td' => array(),
966 // 'tr' => array(),
967 // 'th' => array(),
968 // 'thead' => array(),
969 // 'tbody' => array(),
970 );
971
972 $plugins_section_titles = array(
973 'description' => fs_text_x_inline( 'Description', 'Plugin installer section title', 'description', $api->slug ),
974 'installation' => fs_text_x_inline( 'Installation', 'Plugin installer section title', 'installation', $api->slug ),
975 'faq' => fs_text_x_inline( 'FAQ', 'Plugin installer section title', 'faq', $api->slug ),
976 'screenshots' => fs_text_inline( 'Screenshots', 'screenshots', $api->slug ),
977 'changelog' => fs_text_x_inline( 'Changelog', 'Plugin installer section title', 'changelog', $api->slug ),
978 'reviews' => fs_text_x_inline( 'Reviews', 'Plugin installer section title', 'reviews', $api->slug ),
979 'other_notes' => fs_text_x_inline( 'Other Notes', 'Plugin installer section title', 'other-notes', $api->slug ),
980 );
981
982 // Sanitize HTML
983 // foreach ( (array) $api->sections as $section_name => $content ) {
984 // $api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags );
985 // }
986
987 foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) {
988 if ( isset( $api->$key ) ) {
989 $api->$key = wp_kses( $api->$key, $plugins_allowedtags );
990 }
991 }
992
993 // Add after $api->slug is ready.
994 $plugins_section_titles['features'] = fs_text_x_inline( 'Features & Pricing', 'Plugin installer section title', 'features-and-pricing', $api->slug );
995
996 $_tab = esc_attr( $tab );
997
998 $section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English.
999 if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) {
1000 $section_titles = array_keys( (array) $api->sections );
1001 $section = array_shift( $section_titles );
1002 }
1003
1004 iframe_header( fs_text_inline( 'Plugin Install', 'plugin-install', $api->slug ) );
1005
1006 $_with_banner = '';
1007
1008 // var_dump($api->banners);
1009 if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) {
1010 $_with_banner = 'with-banner';
1011 $low = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low'];
1012 $high = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high'];
1013 ?>
1014 <style type="text/css">
1015 #plugin-information-title.with-banner
1016 {
1017 background-image: url( <?php echo esc_url( $low ); ?> );
1018 }
1019
1020 @media only screen and ( -webkit-min-device-pixel-ratio: 1.5 )
1021 {
1022 #plugin-information-title.with-banner
1023 {
1024 background-image: url( <?php echo esc_url( $high ); ?> );
1025 }
1026 }
1027 </style>
1028 <?php
1029 }
1030
1031 echo '<div id="plugin-information-scrollable">';
1032 echo "<div id='{$_tab}-title' class='{$_with_banner}'><div class='vignette'></div><h2>{$api->name}</h2></div>";
1033 echo "<div id='{$_tab}-tabs' class='{$_with_banner}'>\n";
1034
1035 foreach ( (array) $api->sections as $section_name => $content ) {
1036 if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) {
1037 continue;
1038 }
1039
1040 if ( isset( $plugins_section_titles[ $section_name ] ) ) {
1041 $title = $plugins_section_titles[ $section_name ];
1042 } else {
1043 $title = ucwords( str_replace( '_', ' ', $section_name ) );
1044 }
1045
1046 $class = ( $section_name === $section ) ? ' class="current"' : '';
1047 $href = add_query_arg( array( 'tab' => $tab, 'section' => $section_name ) );
1048 $href = esc_url( $href );
1049 $san_section = esc_attr( $section_name );
1050 echo "\t<a name='$san_section' href='$href' $class>$title</a>\n";
1051 }
1052
1053 echo "</div>\n";
1054
1055 ?>
1056 <div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'>
1057 <div class="fyi">
1058 <?php if ( $api->is_paid ) : ?>
1059 <?php if ( isset( $api->plans ) ) : ?>
1060 <div class="plugin-information-pricing">
1061 <?php foreach ( $api->plans as $plan ) : ?>
1062 <?php
1063 if ( empty( $plan->pricing ) ) {
1064 continue;
1065 }
1066
1067 /**
1068 * @var FS_Plugin_Plan $plan
1069 */
1070 ?>
1071 <?php $first_pricing = $plan->pricing[0] ?>
1072 <?php $is_multi_cycle = $first_pricing->is_multi_cycle() ?>
1073 <div class="fs-plan<?php if ( ! $is_multi_cycle ) {
1074 echo ' fs-single-cycle';
1075 } ?>" data-plan-id="<?php echo $plan->id ?>">
1076 <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>
1077 <?php $has_annual = $first_pricing->has_annual() ?>
1078 <?php $has_monthly = $first_pricing->has_monthly() ?>
1079 <div class="nav-tab-wrapper">
1080 <?php $billing_cycles = array( 'monthly', 'annual', 'lifetime' ) ?>
1081 <?php $i = 0;
1082 foreach ( $billing_cycles as $cycle ) : ?>
1083 <?php $prop = "{$cycle}_price";
1084 if ( isset( $first_pricing->{$prop} ) ) : ?>
1085 <?php $is_featured = ( 'annual' === $cycle && $is_multi_cycle ) ?>
1086 <?php
1087 $prices = array();
1088 foreach ( $plan->pricing as $pricing ) {
1089 if ( isset( $pricing->{$prop} ) ) {
1090 $prices[] = array(
1091 'id' => $pricing->id,
1092 'licenses' => $pricing->licenses,
1093 'price' => $pricing->{$prop}
1094 );
1095 }
1096 }
1097 ?>
1098 <a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>"
1099 data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>">
1100 <?php if ( $is_featured ) : ?>
1101 <label>
1102 &#9733; <?php fs_esc_html_echo_x_inline( 'Best', 'e.g. the best product', 'best', $api->slug ) ?>
1103 &#9733;</label>
1104 <?php endif ?>
1105 <?php
1106 switch ( $cycle ) {
1107 case 'monthly':
1108 fs_esc_html_echo_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug );
1109 break;
1110 case 'annual':
1111 fs_esc_html_echo_x_inline( 'Annual', 'as once a year', 'annual', $api->slug );
1112 break;
1113 case 'lifetime':
1114 fs_esc_html_echo_inline( 'Lifetime', 'lifetime', $api->slug );
1115 break;
1116 }
1117 ?>
1118 </a>
1119 <?php endif ?>
1120 <?php $i ++; endforeach ?>
1121 <?php wp_enqueue_script( 'jquery' ) ?>
1122 <script type="text/javascript">
1123 (function ($, undef) {
1124 var
1125 _formatBillingFrequency = function (cycle) {
1126 switch (cycle) {
1127 case 'monthly':
1128 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 ) ) ?>';
1129 case 'annual':
1130 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 ) ) ?>';
1131 case 'lifetime':
1132 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 ) ) ?>';
1133 }
1134 },
1135 _formatLicensesTitle = function (pricing) {
1136 switch (pricing.licenses) {
1137 case 1:
1138 return '<?php fs_esc_attr_echo_inline( 'Single Site License', 'license-single-site', $api->slug ) ?>';
1139 case null:
1140 return '<?php fs_esc_attr_echo_inline( 'Unlimited Licenses', 'license-unlimited', $api->slug ) ?>';
1141 default:
1142 return '<?php fs_esc_attr_echo_inline( 'Up to %s Sites', 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses);
1143 }
1144 },
1145 _formatPrice = function (pricing, cycle, multipleLicenses) {
1146 if (undef === multipleLicenses)
1147 multipleLicenses = true;
1148
1149 var priceCycle;
1150 switch (cycle) {
1151 case 'monthly':
1152 priceCycle = ' / <?php fs_echo_x_inline( 'mo', 'as monthly period', 'mo', $api->slug ) ?>';
1153 break;
1154 case 'lifetime':
1155 priceCycle = '';
1156 break;
1157 case 'annual':
1158 default:
1159 priceCycle = ' / <?php fs_echo_x_inline( 'year', 'as annual period', 'year', $api->slug ) ?>';
1160 break;
1161 }
1162
1163 if (!multipleLicenses && 1 == pricing.licenses) {
1164 return '$' + pricing.price + priceCycle;
1165 }
1166
1167 return _formatLicensesTitle(pricing) + ' - <var class="fs-price">$' + pricing.price + priceCycle + '</var>';
1168 },
1169 _checkoutUrl = function (plan, pricing, cycle) {
1170 return '<?php echo esc_url_raw( remove_query_arg( 'billing_cycle', add_query_arg( array( 'plugin_id' => $plan->plugin_id ), $api->checkout_link ) ) ) ?>' +
1171 '&plan_id=' + plan +
1172 '&pricing_id=' + pricing +
1173 '&billing_cycle=' + cycle<?php if ( $plan->has_trial() ) {
1174 echo " + '&trial=true'";
1175 }?>;
1176 },
1177 _updateCtaUrl = function (plan, pricing, cycle) {
1178 $('.plugin-information-pricing .fs-checkout-button, #plugin-information-footer .fs-checkout-button').attr('href', _checkoutUrl(plan, pricing, cycle));
1179 };
1180
1181 $(document).ready(function () {
1182 var $plan = $('.plugin-information-pricing .fs-plan[data-plan-id=<?php echo $plan->id ?>]');
1183 $plan.find('input[type=radio]').live('click', function () {
1184 _updateCtaUrl(
1185 $plan.attr('data-plan-id'),
1186 $(this).val(),
1187 $plan.find('.nav-tab-active').attr('data-billing-cycle')
1188 );
1189
1190 $plan.find('.fs-trial-terms .fs-price').html(
1191 $(this).parents('label').find('.fs-price').html()
1192 );
1193 });
1194
1195 $plan.find('.nav-tab').click(function () {
1196 if ($(this).hasClass('nav-tab-active'))
1197 return;
1198
1199 var $this = $(this),
1200 billingCycle = $this.attr('data-billing-cycle'),
1201 pricing = JSON.parse($this.attr('data-pricing')),
1202 $pricesList = $this.parents('.fs-plan').find('.fs-pricing-body .fs-licenses'),
1203 html = '';
1204
1205 // Un-select previously selected tab.
1206 $plan.find('.nav-tab').removeClass('nav-tab-active');
1207
1208 // Select current tab.
1209 $this.addClass('nav-tab-active');
1210
1211 // Render licenses prices.
1212 if (1 == pricing.length) {
1213 html = '<li><label><?php echo fs_esc_attr_x_inline( 'Price', 'noun', 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>';
1214 } else {
1215 for (var i = 0; i < pricing.length; i++) {
1216 html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>';
1217 }
1218 }
1219 $pricesList.html(html);
1220
1221 if (1 < pricing.length) {
1222 // Select first license option.
1223 $pricesList.find('li:first input').click();
1224 }
1225 else {
1226 _updateCtaUrl(
1227 $plan.attr('data-plan-id'),
1228 pricing[0].id,
1229 billingCycle
1230 );
1231 }
1232
1233 // Update billing frequency.
1234 $plan.find('.fs-billing-frequency').html(_formatBillingFrequency(billingCycle));
1235
1236 if ('annual' === billingCycle) {
1237 $plan.find('.fs-annual-discount').show();
1238 } else {
1239 $plan.find('.fs-annual-discount').hide();
1240 }
1241 });
1242
1243 <?php if ( $has_annual ) : ?>
1244 // Select annual by default.
1245 $plan.find('.nav-tab[data-billing-cycle=annual]').click();
1246 <?php else : ?>
1247 // Select first tab.
1248 $plan.find('.nav-tab:first').click();
1249 <?php endif ?>
1250 });
1251 }(jQuery));
1252 </script>
1253 </div>
1254 <div class="fs-pricing-body">
1255 <span class="fs-billing-frequency"></span>
1256 <?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?>
1257 <?php if ( $annual_discount > 0 ) : ?>
1258 <span
1259 class="fs-annual-discount"><?php printf(
1260 /* translators: %s: Discount (e.g. discount of $5 or 10%) */
1261 fs_esc_html_inline( 'Save %s', 'save-x', $api->slug ), $annual_discount . '%' ) ?></span>
1262 <?php endif ?>
1263 <ul class="fs-licenses">
1264 </ul>
1265 <?php echo $this->get_actions_dropdown( $api, $plan ) ?>
1266 <div style="clear:both"></div>
1267 <?php if ( $plan->has_trial() ) : ?>
1268 <?php $trial_period = $this->get_trial_period( $plan ) ?>
1269 <ul class="fs-trial-terms">
1270 <li>
1271 <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 ) ) ?>
1272 </li>
1273 <li>
1274 <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>' ) ?>
1275 </li>
1276 </ul>
1277 <?php endif ?>
1278 </div>
1279 </div>
1280 </div>
1281 <?php endforeach ?>
1282 <?php endif ?>
1283 <?php endif ?>
1284 <div>
1285 <h3><?php fs_echo_inline( 'Details', 'details', $api->slug ) ?></h3>
1286 <ul>
1287 <?php if ( ! empty( $api->version ) ) { ?>
1288 <li>
1289 <strong><?php fs_esc_html_echo_x_inline( 'Version', 'product version', 'version', $api->slug ); ?>
1290 :</strong> <?php echo $api->version; ?></li>
1291 <?php
1292 }
1293 if ( ! empty( $api->author ) ) {
1294 ?>
1295 <li>
1296 <strong><?php fs_echo_x_inline( 'Author', 'as the plugin author', 'author', $api->slug ); ?>
1297 :</strong> <?php echo links_add_target( $api->author, '_blank' ); ?>
1298 </li>
1299 <?php
1300 }
1301 if ( ! empty( $api->last_updated ) ) {
1302 ?>
1303 <li><strong><?php fs_echo_inline( 'Last Updated', 'last-updated', $api->slug ); ?>
1304 :</strong> <span
1305 title="<?php echo $api->last_updated; ?>">
1306 <?php echo esc_html( sprintf(
1307 /* translators: %s: time period (e.g. "2 hours" ago) */
1308 fs_text_x_inline( '%s ago', 'x-ago', $api->slug ),
1309 human_time_diff( strtotime( $api->last_updated ) )
1310 ) ) ?>
1311 </span></li>
1312 <?php
1313 }
1314 if ( ! empty( $api->requires ) ) {
1315 ?>
1316 <li>
1317 <strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>
1318 :</strong> <?php echo esc_html( sprintf( fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires ) ) ?>
1319 </li>
1320 <?php
1321 }
1322 if ( ! empty( $api->tested ) ) {
1323 ?>
1324 <li>
1325 <strong><?php fs_esc_html_echo_inline( 'Compatible up to', 'compatible-up-to', $api->slug ); ?>
1326 :</strong> <?php echo $api->tested; ?>
1327 </li>
1328 <?php
1329 }
1330 if ( ! empty( $api->downloaded ) ) {
1331 ?>
1332 <li>
1333 <strong><?php fs_esc_html_echo_inline( 'Downloaded', 'downloaded', $api->slug ) ?>
1334 :</strong> <?php echo esc_html( sprintf(
1335 ( ( 1 == $api->downloaded ) ?
1336 /* translators: %s: 1 or One (Number of times downloaded) */
1337 fs_text_inline( '%s time', 'x-time', $api->slug ) :
1338 /* translators: %s: Number of times downloaded */
1339 fs_text_inline( '%s times', 'x-times', $api->slug )
1340 ),
1341 number_format_i18n( $api->downloaded )
1342 ) ); ?>
1343 </li>
1344 <?php
1345 }
1346 if ( ! empty( $api->slug ) && true == $api->is_wp_org_compliant ) {
1347 ?>
1348 <li><a target="_blank"
1349 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 ) ?>
1350 &#187;</a>
1351 </li>
1352 <?php
1353 }
1354 if ( ! empty( $api->homepage ) ) {
1355 ?>
1356 <li><a target="_blank"
1357 href="<?php echo esc_url( $api->homepage ); ?>"><?php fs_esc_html_echo_inline( 'Plugin Homepage', 'plugin-homepage', $api->slug ) ?>
1358 &#187;</a>
1359 </li>
1360 <?php
1361 }
1362 if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) {
1363 ?>
1364 <li><a target="_blank"
1365 href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_esc_html_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
1366 &#187;</a>
1367 </li>
1368 <?php } ?>
1369 </ul>
1370 </div>
1371 <?php if ( ! empty( $api->rating ) ) { ?>
1372 <h3><?php fs_echo_inline( 'Average Rating', 'average-rating', $api->slug ); ?></h3>
1373 <?php wp_star_rating( array(
1374 'rating' => $api->rating,
1375 'type' => 'percent',
1376 'number' => $api->num_ratings
1377 ) ); ?>
1378 <small>(<?php echo esc_html( sprintf(
1379 fs_text_inline( 'based on %s', 'based-on-x', $api->slug ),
1380 sprintf(
1381 ( ( 1 == $api->num_ratings ) ?
1382 /* translators: %s: 1 or One */
1383 fs_text_inline( '%s rating', 'x-rating', $api->slug ) :
1384 /* translators: %s: Number larger than 1 */
1385 fs_text_inline( '%s ratings', 'x-ratings', $api->slug )
1386 ),
1387 number_format_i18n( $api->num_ratings )
1388 ) ) ) ?>)
1389 </small>
1390 <?php
1391 }
1392
1393 if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) {
1394 foreach ( $api->ratings as $key => $ratecount ) {
1395 // Avoid div-by-zero.
1396 $_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
1397 $stars_label = sprintf(
1398 ( ( 1 == $key ) ?
1399 /* translators: %s: 1 or One */
1400 fs_text_inline( '%s star', 'x-star', $api->slug ) :
1401 /* translators: %s: Number larger than 1 */
1402 fs_text_inline( '%s stars', 'x-stars', $api->slug )
1403 ),
1404 number_format_i18n( $key )
1405 );
1406 ?>
1407 <div class="counter-container">
1408 <span class="counter-label"><a
1409 href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
1410 target="_blank"
1411 title="<?php echo esc_attr( sprintf(
1412 /* translators: %s: # of stars (e.g. 5 stars) */
1413 fs_text_inline( 'Click to see reviews that provided a rating of %s', 'click-to-reviews', $api->slug ),
1414 $stars_label
1415 ) ) ?>"><?php echo $stars_label ?></a></span>
1416 <span class="counter-back">
1417 <span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
1418 </span>
1419 <span class="counter-count"><?php echo number_format_i18n( $ratecount ); ?></span>
1420 </div>
1421 <?php
1422 }
1423 }
1424 if ( ! empty( $api->contributors ) ) {
1425 ?>
1426 <h3><?php fs_echo_inline( 'Contributors', 'contributors', $api->slug ); ?></h3>
1427 <ul class="contributors">
1428 <?php
1429 foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) {
1430 if ( empty( $contrib_username ) && empty( $contrib_profile ) ) {
1431 continue;
1432 }
1433 if ( empty( $contrib_username ) ) {
1434 $contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile );
1435 }
1436 $contrib_username = sanitize_user( $contrib_username );
1437 if ( empty( $contrib_profile ) ) {
1438 echo "<li><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&amp;s=36' width='18' height='18' />{$contrib_username}</li>";
1439 } else {
1440 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>";
1441 }
1442 }
1443 ?>
1444 </ul>
1445 <?php if ( ! empty( $api->donate_link ) ) { ?>
1446 <a target="_blank"
1447 href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
1448 &#187;</a>
1449 <?php } ?>
1450 <?php } ?>
1451 </div>
1452 <div id="section-holder" class="wrap">
1453 <?php
1454 if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
1455 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>';
1456 } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
1457 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>';
1458 }
1459
1460 foreach ( (array) $api->sections as $section_name => $content ) {
1461 $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' );
1462 $content = links_add_target( $content, '_blank' );
1463
1464 $san_section = esc_attr( $section_name );
1465
1466 $display = ( $section_name === $section ) ? 'block' : 'none';
1467
1468 if ( 'description' === $section_name &&
1469 ( ( $api->is_wp_org_compliant && $api->wp_org_missing ) ||
1470 ( ! $api->is_wp_org_compliant && $api->fs_missing ) )
1471 ) {
1472 $missing_notice = array(
1473 'type' => 'error',
1474 'id' => md5( microtime() ),
1475 'message' => $api->is_paid ?
1476 fs_text_inline( 'Paid add-on must be deployed to Freemius.', 'paid-addon-not-deployed', $api->slug ) :
1477 fs_text_inline( 'Add-on must be deployed to WordPress.org or Freemius.', 'free-addon-not-deployed', $api->slug ),
1478 );
1479 fs_require_template( 'admin-notice.php', $missing_notice );
1480 }
1481 echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n";
1482 echo $content;
1483 echo "\t</div>\n";
1484 }
1485 echo "</div>\n";
1486 echo "</div>\n";
1487 echo "</div>\n"; // #plugin-information-scrollable
1488 echo "<div id='$tab-footer'>\n";
1489
1490 if (
1491 ! empty( $api->download_link ) &&
1492 ! empty( $this->status ) &&
1493 in_array( $this->status['status'], array( 'newer_installed', 'latest_installed' ) )
1494 ) {
1495 if ( 'newer_installed' === $this->status['status'] ) {
1496 echo $this->get_cta(
1497 ( $this->status['is_premium_installed'] ?
1498 esc_html( sprintf( fs_text_inline( 'Newer Version (%s) Installed', 'newer-installed', $api->slug ), $this->status['version'] ) ) :
1499 esc_html( sprintf( fs_text_inline( 'Newer Free Version (%s) Installed', 'newer-free-installed', $api->slug ), $this->status['version'] ) ) ),
1500 false,
1501 true
1502 );
1503 } else {
1504 echo $this->get_cta(
1505 ( $this->status['is_premium_installed'] ?
1506 fs_esc_html_inline( 'Latest Version Installed', 'latest-installed', $api->slug ) :
1507 fs_esc_html_inline( 'Latest Free Version Installed', 'latest-free-installed', $api->slug ) ),
1508 false,
1509 true
1510 );
1511 }
1512 }
1513
1514 echo $this->get_actions_dropdown( $api, null );
1515
1516 echo "</div>\n";
1517 ?>
1518 <script type="text/javascript">
1519 ( function( $, undef ) {
1520 var $dropdowns = $( '.fs-dropdown' );
1521
1522 $( '#plugin-information' )
1523 .click( function( evt ) {
1524 var $target = $( evt.target );
1525
1526 if (
1527 $target.hasClass( 'fs-dropdown-arrow-button' ) ||
1528 ( 0 !== $target.parents( '.fs-dropdown-arrow-button' ).length )
1529 ) {
1530 var $dropdown = $target.parents( '.fs-dropdown' ),
1531 isActive = $dropdown.hasClass( 'active' );
1532
1533 if ( ! isActive ) {
1534 /**
1535 * Close the other dropdown if it's active.
1536 *
1537 * @author Leo Fajardo (@leorw)
1538 * @since 2.3.0
1539 */
1540 $( '.fs-dropdown.active' ).each( function() {
1541 toggleDropdown( $( this ), false );
1542 } );
1543 }
1544
1545 /**
1546 * Toggle the current dropdown.
1547 *
1548 * @author Leo Fajardo (@leorw)
1549 * @since 2.3.0
1550 */
1551 toggleDropdown( $dropdown, ! isActive );
1552
1553 return true;
1554 }
1555
1556 /**
1557 * Close all dropdowns.
1558 *
1559 * @author Leo Fajardo (@leorw)
1560 * @since 2.3.0
1561 */
1562 toggleDropdown( $( this ).find( '.fs-dropdown' ), false );
1563 });
1564
1565 if ( 0 !== $dropdowns.length ) {
1566 /**
1567 * Add the `up` class so that the bottom dropdown's content will be shown above its buttons.
1568 *
1569 * @author Leo Fajardo (@leorw)
1570 * @since 2.3.0
1571 */
1572 $( '#plugin-information-footer' ).find( '.fs-dropdown' ).addClass( 'up' );
1573 }
1574
1575 /**
1576 * Returns the default state of the dropdown arrow button and hides the dropdown list.
1577 *
1578 * @author Leo Fajardo (@leorw)
1579 * @since 2.3.0
1580 *
1581 * @param {Object} [$dropdown]
1582 * @param {Boolean} [state]
1583 */
1584 function toggleDropdown( $dropdown, state ) {
1585 if ( undef === $dropdown ) {
1586 var $activeDropdown = $dropdowns.find( '.active' );
1587 if ( 0 !== $activeDropdown.length ) {
1588 $dropdown = $activeDropdown;
1589 }
1590 }
1591
1592 if ( undef === $dropdown ) {
1593 return;
1594 }
1595
1596 if ( undef === state ) {
1597 state = false;
1598 }
1599
1600 $dropdown.toggleClass( 'active', state );
1601 $dropdown.find( '.fs-dropdown-list' ).toggle( state );
1602 $dropdown.find( '.fs-dropdown-arrow-button' ).toggleClass( 'active', state );
1603 }
1604 } )( jQuery );
1605 </script>
1606 <?php
1607 iframe_footer();
1608 exit;
1609 }
1610 }