PluginProbe
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder / 1.4.4
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder v1.4.4
3.6.0 3.5.0 trunk 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.6.1 1.0.7 1.0.8 1.0.9 1.1 1.1.1 1.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 All 110 releases
buttonizer-multifunctional-button / freemius / includes / fs-plugin-info-dialog.php

fs-plugin-info-dialog.php in Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder 1.4.4, at freemius/includes/fs-plugin-info-dialog.php

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