FS_Plugin_Info_Dialog

Class FS_Plugin_Info_Dialog.

Defined (1)

The class is defined in the following location(s).

/freemius/includes/fs-plugin-info-dialog.php  
  1. class FS_Plugin_Info_Dialog { 
  2. /** 
  3. * @since 1.1.7 
  4. * @var FS_Logger 
  5. */ 
  6. private $_logger; 
  7.  
  8. /** 
  9. * @since 1.1.7 
  10. * @var Freemius 
  11. */ 
  12. private $_fs; 
  13.  
  14. function __construct( Freemius $fs ) { 
  15. $this->_fs = $fs; 
  16.  
  17. $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $fs->get_slug() . '_info', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); 
  18.  
  19. // Remove default plugin information action. 
  20. remove_all_actions( 'install_plugins_pre_plugin-information' ); 
  21.  
  22. // Override action with custom plugins function for add-ons. 
  23. add_action( 'install_plugins_pre_plugin-information', array( &$this, 'install_plugin_information' ) ); 
  24.  
  25. // Override request for plugin information for Add-ons. 
  26. add_filter( 
  27. 'fs_plugins_api',  
  28. array( &$this, '_get_addon_info_filter' ),  
  29. WP_FS__DEFAULT_PRIORITY, 3 ); 
  30.  
  31. /** 
  32. * Generate add-on plugin information. 
  33. * @author Vova Feldman (@svovaf) 
  34. * @since 1.0.6 
  35. * @param array $data 
  36. * @param string $action 
  37. * @param object|null $args 
  38. * @return array|null 
  39. */ 
  40. function _get_addon_info_filter( $data, $action = '', $args = null ) { 
  41. $this->_logger->entrance(); 
  42.  
  43. $parent_plugin_id = fs_request_get( 'parent_plugin_id', false ); 
  44.  
  45. if ( $this->_fs->get_id() != $parent_plugin_id || 
  46. ( 'plugin_information' !== $action ) || 
  47. ! isset( $args->slug ) 
  48. ) { 
  49. return $data; 
  50.  
  51. // Find add-on by slug. 
  52. $selected_addon = $this->_fs->get_addon_by_slug($args->slug, WP_FS__DEV_MODE); 
  53.  
  54. if ( false === $selected_addon ) { 
  55. return $data; 
  56.  
  57. if ( ! isset( $selected_addon->info ) ) { 
  58. // Setup some default info. 
  59. $selected_addon->info = new stdClass(); 
  60. $selected_addon->info->selling_point_0 = 'Selling Point 1'; 
  61. $selected_addon->info->selling_point_1 = 'Selling Point 2'; 
  62. $selected_addon->info->selling_point_2 = 'Selling Point 3'; 
  63. $selected_addon->info->description = '<p>Tell your users all about your add-on</p>'; 
  64.  
  65. fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' ); 
  66.  
  67. $data = $args; 
  68.  
  69. $is_free = true; 
  70.  
  71. // Load add-on pricing. 
  72. $has_pricing = false; 
  73. $has_features = false; 
  74. $plans = false; 
  75. $plans_result = $this->_fs->get_api_site_or_plugin_scope()->get( "/addons/{$selected_addon->id}/plans.json?type=visible" ); 
  76. if ( ! isset( $plans_result->error ) ) { 
  77. $plans = $plans_result->plans; 
  78. if ( is_array( $plans ) ) { 
  79. for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) { 
  80. $plans[ $i ] = new FS_Plugin_Plan( $plans[ $i ] ); 
  81. $plan = $plans[ $i ]; 
  82.  
  83. $pricing_result = $this->_fs->get_api_site_or_plugin_scope()->get( "/addons/{$selected_addon->id}/plans/{$plan->id}/pricing.json" ); 
  84. if ( ! isset( $pricing_result->error ) ) { 
  85. // Update plan's pricing. 
  86. $plan->pricing = $pricing_result->pricing; 
  87.  
  88. if ( is_array( $plan->pricing ) && ! empty( $plan->pricing ) ) { 
  89. $is_free = false; 
  90.  
  91. foreach ( $plan->pricing as &$pricing ) { 
  92. $pricing = new FS_Pricing( $pricing ); 
  93.  
  94. $has_pricing = true; 
  95.  
  96. $features_result = $this->_fs->get_api_site_or_plugin_scope()->get( "/addons/{$selected_addon->id}/plans/{$plan->id}/features.json" ); 
  97. if ( ! isset( $features_result->error ) && 
  98. is_array( $features_result->features ) && 
  99. 0 < count( $features_result->features ) 
  100. ) { 
  101. // Update plan's pricing. 
  102. $plan->features = $features_result->features; 
  103.  
  104. $has_features = true; 
  105.  
  106. // Fetch latest version from Freemius. 
  107. $latest = $this->_fs->_fetch_latest_version( $selected_addon->id ); 
  108.  
  109. if ( ! $is_free ) { 
  110. // If paid add-on, then it's not on wordpress.org 
  111. $is_wordpress_org = false; 
  112. } else { 
  113. // If no versions found, then assume it's a .org plugin. 
  114. $is_wordpress_org = ( false === $latest ); 
  115.  
  116. if ( $is_wordpress_org ) { 
  117. $repo_data = FS_Plugin_Updater::_fetch_plugin_info_from_repository( 
  118. 'plugin_information', (object) array( 
  119. 'slug' => $selected_addon->slug,  
  120. 'is_ssl' => is_ssl(),  
  121. 'fields' => array( 
  122. 'banners' => true,  
  123. 'reviews' => true,  
  124. 'downloaded' => false,  
  125. 'active_installs' => true 
  126. ) ); 
  127.  
  128. if ( ! empty( $repo_data ) ) { 
  129. $data = $repo_data; 
  130. $data->wp_org_missing = false; 
  131. } else { 
  132. // Couldn't find plugin on .org. 
  133. $is_wordpress_org = false; 
  134.  
  135. // Plugin is missing, not on Freemius nor WP.org. 
  136. $data->wp_org_missing = true; 
  137.  
  138. if ( ! $is_wordpress_org ) { 
  139. $data->checkout_link = $this->_fs->checkout_url(); 
  140. $data->fs_missing = ( false === $latest ); 
  141.  
  142. if ( $is_free ) { 
  143. $data->download_link = $this->_fs->_get_latest_download_local_url( $selected_addon->id ); 
  144.  
  145. if ( ! $is_wordpress_org ) { 
  146. // Fetch as much as possible info from local files. 
  147. $plugin_local_data = $this->_fs->get_plugin_data(); 
  148. $data->name = $selected_addon->title; 
  149. $data->author = $plugin_local_data['Author']; 
  150. $view_vars = array( 'plugin' => $selected_addon ); 
  151. $data->sections = array( 
  152. 'description' => fs_get_template( '/plugin-info/description.php', $view_vars ),  
  153. ); 
  154.  
  155. if ( ! empty( $selected_addon->info->banner_url ) ) { 
  156. $data->banners = array( 
  157. 'low' => $selected_addon->info->banner_url,  
  158. ); 
  159.  
  160. if ( ! empty( $selected_addon->info->screenshots ) ) { 
  161. $view_vars = array( 
  162. 'screenshots' => $selected_addon->info->screenshots,  
  163. 'plugin' => $selected_addon,  
  164. ); 
  165. $data->sections['screenshots'] = fs_get_template( '/plugin-info/screenshots.php', $view_vars ); 
  166.  
  167. if ( is_object( $latest ) ) { 
  168. $data->version = $latest->version; 
  169. $data->last_updated = ! is_null( $latest->updated ) ? $latest->updated : $latest->created; 
  170. $data->requires = $latest->requires_platform_version; 
  171. $data->tested = $latest->tested_up_to_version; 
  172. } else { 
  173. // Add dummy version. 
  174. $data->version = '1.0.0'; 
  175.  
  176. // Add message to developer to deploy the plugin through Freemius. 
  177.  
  178. if ( $has_pricing ) { 
  179. // Add plans to data. 
  180. $data->plans = $plans; 
  181.  
  182. if ( $has_features ) { 
  183. $view_vars = array( 
  184. 'plans' => $plans,  
  185. 'plugin' => $selected_addon,  
  186. ); 
  187. $data->sections['features'] = fs_get_template( '/plugin-info/features.php', $view_vars ); 
  188.  
  189. $data->is_paid = ! $is_free; 
  190. $data->external = ! $is_wordpress_org; 
  191.  
  192. return $data; 
  193.  
  194. /** 
  195. * @author Vova Feldman (@svovaf) 
  196. * @since 1.1.7 
  197. * @param FS_Plugin_Plan $plan 
  198. * @return string 
  199. */ 
  200. private function get_billing_cycle( FS_Plugin_Plan $plan ) { 
  201. $billing_cycle = null; 
  202.  
  203. if ( 1 === count( $plan->pricing ) && 1 == $plan->pricing[0]->licenses ) { 
  204. $pricing = $plan->pricing[0]; 
  205. if ( isset( $pricing->annual_price ) ) { 
  206. $billing_cycle = 'annual'; 
  207. } else if ( isset( $pricing->monthly_price ) ) { 
  208. $billing_cycle = 'monthly'; 
  209. } else if ( isset( $pricing->lifetime_price ) ) { 
  210. $billing_cycle = 'lifetime'; 
  211. } else { 
  212. foreach ( $plan->pricing as $pricing ) { 
  213. if ( isset( $pricing->annual_price ) ) { 
  214. $billing_cycle = 'annual'; 
  215. } else if ( isset( $pricing->monthly_price ) ) { 
  216. $billing_cycle = 'monthly'; 
  217. } else if ( isset( $pricing->lifetime_price ) ) { 
  218. $billing_cycle = 'lifetime'; 
  219.  
  220. if ( ! is_null( $billing_cycle ) ) { 
  221. break; 
  222.  
  223. return $billing_cycle; 
  224.  
  225. /** 
  226. * @author Vova Feldman (@svovaf) 
  227. * @since 1.1.7 
  228. * @param FS_Plugin_Plan $plan 
  229. * @param FS_Pricing $pricing 
  230. * @return float|null|string 
  231. */ 
  232. private function get_price_tag( FS_Plugin_Plan $plan, FS_Pricing $pricing ) { 
  233. $price_tag = ''; 
  234. if ( isset( $pricing->annual_price ) ) { 
  235. $price_tag = $pricing->annual_price . ( $plan->is_block_features ? ' / year' : '' ); 
  236. } else if ( isset( $pricing->monthly_price ) ) { 
  237. $price_tag = $pricing->monthly_price . ' / mo'; 
  238. } else if ( isset( $pricing->lifetime_price ) ) { 
  239. $price_tag = $pricing->lifetime_price; 
  240.  
  241. return '$' . $price_tag; 
  242.  
  243. /** 
  244. * @author Vova Feldman (@svovaf) 
  245. * @since 1.1.7 
  246. * @param object $api 
  247. * @param FS_Plugin_Plan|null $plan 
  248. * @return string 
  249. */ 
  250. private function get_plugin_cta( $api, $plan = null ) { 
  251. if ( ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) ) { 
  252.  
  253. if ( ! empty( $api->checkout_link ) && isset( $api->plans ) && 0 < is_array( $api->plans ) ) { 
  254. if ( is_null( $plan ) ) { 
  255. $plan = $api->plans[0]; 
  256.  
  257. return ' <a class="button button-primary right" href="' . $this->_fs->addon_checkout_url( 
  258. $plan->plugin_id,  
  259. $plan->pricing[0]->id,  
  260. $this->get_billing_cycle( $plan ),  
  261. $plan->has_trial() 
  262. ) . '" target="_parent">' . 
  263. ( ! $plan->has_trial() ? 
  264. __fs( 'purchase', $api->slug ) : 
  265. sprintf( __fs( 'start-free-x', $api->slug ), $this->get_trial_period( $plan ) ) 
  266. ) . 
  267. '</a>'; 
  268.  
  269. // @todo Add Cart concept. 
  270. // echo ' <a class="button right" href="' . $status['url'] . '" target="_parent">' . __( 'Add to Cart' ) . '</a>'; 
  271.  
  272. } else if ( ! empty( $api->download_link ) ) { 
  273. $status = install_plugin_install_status( $api ); 
  274.  
  275. // Hosted on WordPress.org. 
  276. switch ( $status['status'] ) { 
  277. case 'install': 
  278. if ( $api->external && 
  279. $this->_fs->is_org_repo_compliant() || 
  280. ! $this->_fs->is_premium() 
  281. ) { 
  282. /** 
  283. * Add-on hosted on Freemius, not yet installed, and core 
  284. * plugin is wordpress.org compliant. Therefore, require a download 
  285. * since installing external plugins is not allowed by the wp.org guidelines. 
  286. */ 
  287. return ' <a class="button button-primary right" href="' . esc_url( $api->download_link ) . '" target="_blank">' . __fs( 'download-latest', $api->slug ) . '</a>'; 
  288. } else { 
  289. if ( $status['url'] ) { 
  290. return '<a class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . __( 'Install Now' ) . '</a>'; 
  291. break; 
  292. case 'update_available': 
  293. if ( $status['url'] ) { 
  294. return '<a class="button button-primary right" href="' . $status['url'] . '" target="_parent">' . __( 'Install Update Now' ) . '</a>'; 
  295. break; 
  296. case 'newer_installed': 
  297. return '<a class="button button-primary right disabled">' . sprintf( __( 'Newer Version (%s) Installed' ), $status['version'] ) . '</a>'; 
  298. break; 
  299. case 'latest_installed': 
  300. return '<a class="button button-primary right disabled">' . __( 'Latest Version Installed' ) . '</a>'; 
  301. break; 
  302.  
  303.  
  304. return ''; 
  305.  
  306. /** 
  307. * @author Vova Feldman (@svovaf) 
  308. * @since 1.1.7 
  309. * @param FS_Plugin_Plan $plan 
  310. * @return string 
  311. */ 
  312. private function get_trial_period( $plan ) { 
  313. $trial_period = (int) $plan->trial_period; 
  314.  
  315. switch ( $trial_period ) { 
  316. case 30: 
  317. return 'month'; 
  318. case 60: 
  319. return '2 months'; 
  320. default: 
  321. return "{$plan->trial_period} days"; 
  322.  
  323. /** 
  324. * Display plugin information in dialog box form. 
  325. * Based on core install_plugin_information() function. 
  326. * @author Vova Feldman (@svovaf) 
  327. * @since 1.0.6 
  328. */ 
  329. function install_plugin_information() { 
  330. global $tab; 
  331.  
  332. if ( empty( $_REQUEST['plugin'] ) ) { 
  333. return; 
  334.  
  335. $args = array( 
  336. 'slug' => wp_unslash( $_REQUEST['plugin'] ),  
  337. 'is_ssl' => is_ssl(),  
  338. 'fields' => array( 
  339. 'banners' => true,  
  340. 'reviews' => true,  
  341. 'downloaded' => false,  
  342. 'active_installs' => true 
  343. ); 
  344.  
  345. if ( is_array( $args ) ) { 
  346. $args = (object) $args; 
  347.  
  348. if ( ! isset( $args->per_page ) ) { 
  349. $args->per_page = 24; 
  350.  
  351. if ( ! isset( $args->locale ) ) { 
  352. $args->locale = get_locale(); 
  353.  
  354. $api = apply_filters( 'fs_plugins_api', false, 'plugin_information', $args ); 
  355.  
  356. if ( is_wp_error( $api ) ) { 
  357. wp_die( $api ); 
  358.  
  359. $plugins_allowedtags = array( 
  360. 'a' => array( 
  361. 'href' => array(),  
  362. 'title' => array(),  
  363. 'target' => array(),  
  364. // Add image style for screenshots. 
  365. 'class' => array() 
  366. ),  
  367. 'style' => array(),  
  368. 'abbr' => array( 'title' => array() ),  
  369. 'acronym' => array( 'title' => array() ),  
  370. 'code' => array(),  
  371. 'pre' => array(),  
  372. 'em' => array(),  
  373. 'strong' => array(),  
  374. 'div' => array( 'class' => array() ),  
  375. 'span' => array( 'class' => array() ),  
  376. 'p' => array(),  
  377. 'ul' => array(),  
  378. 'ol' => array(),  
  379. 'li' => array( 'class' => array() ),  
  380. 'i' => array( 'class' => array() ),  
  381. 'h1' => array(),  
  382. 'h2' => array(),  
  383. 'h3' => array(),  
  384. 'h4' => array(),  
  385. 'h5' => array(),  
  386. 'h6' => array(),  
  387. 'img' => array( 'src' => array(), 'class' => array(), 'alt' => array() ),  
  388. // 'table' => array(),  
  389. // 'td' => array(),  
  390. // 'tr' => array(),  
  391. // 'th' => array(),  
  392. // 'thead' => array(),  
  393. // 'tbody' => array(),  
  394. ); 
  395.  
  396. $plugins_section_titles = array( 
  397. 'description' => _x( 'Description', 'Plugin installer section title' ),  
  398. 'installation' => _x( 'Installation', 'Plugin installer section title' ),  
  399. 'faq' => _x( 'FAQ', 'Plugin installer section title' ),  
  400. 'screenshots' => _x( 'Screenshots', 'Plugin installer section title' ),  
  401. 'changelog' => _x( 'Changelog', 'Plugin installer section title' ),  
  402. 'reviews' => _x( 'Reviews', 'Plugin installer section title' ),  
  403. 'other_notes' => _x( 'Other Notes', 'Plugin installer section title' ),  
  404. ); 
  405.  
  406. // Sanitize HTML 
  407. // foreach ( (array) $api->sections as $section_name => $content ) { 
  408. // $api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags ); 
  409. // } 
  410.  
  411. foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) { 
  412. if ( isset( $api->$key ) ) { 
  413. $api->$key = wp_kses( $api->$key, $plugins_allowedtags ); 
  414.  
  415. // Add after $api->slug is ready. 
  416. $plugins_section_titles['features'] = __fs( 'features-and-pricing', $api->slug ); 
  417.  
  418. $_tab = esc_attr( $tab ); 
  419.  
  420. $section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English. 
  421. if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) { 
  422. $section_titles = array_keys( (array) $api->sections ); 
  423. $section = array_shift( $section_titles ); 
  424.  
  425. iframe_header( __( 'Plugin Install' ) ); 
  426.  
  427. $_with_banner = ''; 
  428.  
  429. // var_dump($api->banners); 
  430. if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) { 
  431. $_with_banner = 'with-banner'; 
  432. $low = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low']; 
  433. $high = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high']; 
  434. ?> 
  435. <style type="text/css"> 
  436. #plugin-information-title.with-banner 
  437. background-image: url( <?php echo esc_url( $low ); ?> ); 
  438.  
  439. @media only screen and ( -webkit-min-device-pixel-ratio: 1.5 ) 
  440. #plugin-information-title.with-banner 
  441. background-image: url( <?php echo esc_url( $high ); ?> ); 
  442. </style> 
  443. <?php 
  444.  
  445. echo '<div id="plugin-information-scrollable">'; 
  446. echo "<div id='{$_tab}-title' class='{$_with_banner}'><div class='vignette'></div><h2>{$api->name}</h2></div>"; 
  447. echo "<div id='{$_tab}-tabs' class='{$_with_banner}'>\n"; 
  448.  
  449. foreach ( (array) $api->sections as $section_name => $content ) { 
  450. if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) { 
  451. continue; 
  452.  
  453. if ( isset( $plugins_section_titles[ $section_name ] ) ) { 
  454. $title = $plugins_section_titles[ $section_name ]; 
  455. } else { 
  456. $title = ucwords( str_replace( '_', ' ', $section_name ) ); 
  457.  
  458. $class = ( $section_name === $section ) ? ' class="current"' : ''; 
  459. $href = add_query_arg( array( 'tab' => $tab, 'section' => $section_name ) ); 
  460. $href = esc_url( $href ); 
  461. $san_section = esc_attr( $section_name ); 
  462. echo "\t<a name='$san_section' href='$href' $class>$title</a>\n"; 
  463.  
  464. echo "</div>\n"; 
  465.  
  466. ?> 
  467. <div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'> 
  468. <div class="fyi"> 
  469. <?php if ( $api->is_paid ) : ?> 
  470. <?php if ( isset( $api->plans ) ) : ?> 
  471. <div class="plugin-information-pricing"> 
  472. <?php foreach ( $api->plans as $plan ) : ?> 
  473. <?php 
  474. /** 
  475. * @var FS_Plugin_Plan $plan 
  476. */ 
  477. ?> 
  478. <?php $first_pricing = $plan->pricing[0] ?> 
  479. <?php $is_multi_cycle = $first_pricing->is_multi_cycle() ?> 
  480. <div class="fs-plan<?php if ( ! $is_multi_cycle ) { 
  481. echo ' fs-single-cycle'; 
  482. } ?>" data-plan-id="<?php echo $plan->id ?>"> 
  483. <h3 data-plan="<?php echo $plan->id ?>"><?php printf( __fs( 'x-plan', $api->slug ), $plan->title ) ?></h3> 
  484. <?php $has_annual = $first_pricing->has_annual() ?> 
  485. <?php $has_monthly = $first_pricing->has_monthly() ?> 
  486. <div class="nav-tab-wrapper"> 
  487. <?php $billing_cycles = array( 'monthly', 'annual', 'lifetime' ) ?> 
  488. <?php $i = 0; 
  489. foreach ( $billing_cycles as $cycle ) : ?> 
  490. <?php $prop = "{$cycle}_price"; 
  491. if ( isset( $first_pricing->{$prop} ) ) : ?> 
  492. <?php $is_featured = ( 'annual' === $cycle && $is_multi_cycle ) ?> 
  493. <?php 
  494. $prices = array(); 
  495. foreach ( $plan->pricing as $pricing ) { 
  496. if ( isset( $pricing->{$prop} ) ) { 
  497. $prices[] = array( 
  498. 'id' => $pricing->id,  
  499. 'licenses' => $pricing->licenses,  
  500. 'price' => $pricing->{$prop} 
  501. ); 
  502. ?> 
  503. <a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>" 
  504. data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>"> 
  505. <?php if ( $is_featured ) : ?> 
  506. <label>★ <?php _efs( 'best', $api->slug ) ?> ★</label> 
  507. <?php endif ?> 
  508. <?php _efs( $cycle, $api->slug ) ?> 
  509. </a> 
  510. <?php endif ?> 
  511. <?php $i ++; endforeach ?> 
  512. <?php wp_enqueue_script( 'jquery' ) ?> 
  513. <script type="text/javascript"> 
  514. (function ($, undef) { 
  515. var 
  516. _formatBillingFrequency = function (cycle) { 
  517. switch (cycle) { 
  518. case 'monthly': 
  519. return '<?php printf(__fs('billed-x', $api->slug), __fs('monthly', $api->slug)) ?>'; 
  520. case 'annual': 
  521. return '<?php printf(__fs('billed-x', $api->slug), __fs('annually', $api->slug)) ?>'; 
  522. case 'lifetime': 
  523. return '<?php printf(__fs('billed-x', $api->slug), __fs('once', $api->slug)) ?>'; 
  524. },  
  525. _formatLicensesTitle = function (pricing) { 
  526. switch (pricing.licenses) { 
  527. case 1: 
  528. return '<?php _efs( 'license-single-site', $api->slug ) ?>'; 
  529. case null: 
  530. return '<?php _efs( 'license-unlimited', $api->slug ) ?>'; 
  531. default: 
  532. return '<?php _efs( 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses); 
  533. },  
  534. _formatPrice = function (pricing, cycle, multipleLicenses) { 
  535. if (undef === multipleLicenses) 
  536. multipleLicenses = true; 
  537.  
  538. var priceCycle; 
  539. switch (cycle) { 
  540. case 'monthly': 
  541. priceCycle = ' / <?php _efs('mo', $api->slug) ?>'; 
  542. break; 
  543. case 'lifetime': 
  544. priceCycle = ''; 
  545. break; 
  546. case 'annual': 
  547. default: 
  548. priceCycle = ' / <?php _efs('year', $api->slug) ?>'; 
  549. break; 
  550.  
  551. if (!multipleLicenses && 1 == pricing.licenses) { 
  552. return '$' + pricing.price + priceCycle; 
  553.  
  554. return _formatLicensesTitle(pricing) + ' - <var class="fs-price">$' + pricing.price + priceCycle + '</var>'; 
  555. },  
  556. _checkoutUrl = function (plan, pricing, cycle) { 
  557. return '<?php echo esc_url_raw(remove_query_arg('billing_cycle', add_query_arg(array('plugin_id' => $plan->plugin_id), $api->checkout_link))) ?>' + 
  558. '&plan_id=' + plan + 
  559. '&pricing_id=' + pricing + 
  560. '&billing_cycle=' + cycle<?php if ($plan->has_trial()) { echo " + '&trial=true'"; }?>; 
  561. },  
  562. _updateCtaUrl = function (plan, pricing, cycle) { 
  563. $('.plugin-information-pricing .button, #plugin-information-footer .button').attr('href', _checkoutUrl(plan, pricing, cycle)); 
  564. }; 
  565.  
  566. $(document).ready(function () { 
  567. var $plan = $('.plugin-information-pricing .fs-plan[data-plan-id=<?php echo $plan->id ?>]'); 
  568. $plan.find('input[type=radio]').live('click', function () { 
  569. _updateCtaUrl( 
  570. $plan.attr('data-plan-id'),  
  571. $(this).val(),  
  572. $plan.find('.nav-tab-active').attr('data-billing-cycle') 
  573. ); 
  574.  
  575. $plan.find('.fs-trial-terms .fs-price').html( 
  576. $(this).parents('label').find('.fs-price').html() 
  577. ); 
  578. }); 
  579.  
  580. $plan.find('.nav-tab').click(function () { 
  581. if ($(this).hasClass('nav-tab-active')) 
  582. return; 
  583.  
  584. var $this = $(this),  
  585. billingCycle = $this.attr('data-billing-cycle'),  
  586. pricing = JSON.parse($this.attr('data-pricing')),  
  587. $pricesList = $this.parents('.fs-plan').find('.fs-pricing-body .fs-licenses'),  
  588. html = ''; 
  589.  
  590. // Un-select previously selected tab. 
  591. $plan.find('.nav-tab').removeClass('nav-tab-active'); 
  592.  
  593. // Select current tab. 
  594. $this.addClass('nav-tab-active'); 
  595.  
  596. // Render licenses prices. 
  597. if (1 == pricing.length) { 
  598. html = '<li><label><?php _efs( 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>'; 
  599. } else { 
  600. for (var i = 0; i < pricing.length; i++) { 
  601. html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>'; 
  602. $pricesList.html(html); 
  603.  
  604. if (1 < pricing.length) { 
  605. // Select first license option. 
  606. $pricesList.find('li:first input').click(); 
  607. else { 
  608. _updateCtaUrl( 
  609. $plan.attr('data-plan-id'),  
  610. pricing[0].id,  
  611. billingCycle 
  612. ); 
  613.  
  614. // Update billing frequency. 
  615. $plan.find('.fs-billing-frequency').html(_formatBillingFrequency(billingCycle)); 
  616.  
  617. if ('annual' === billingCycle) { 
  618. $plan.find('.fs-annual-discount').show(); 
  619. } else { 
  620. $plan.find('.fs-annual-discount').hide(); 
  621. }); 
  622.  
  623. <?php if ( $has_annual ) : ?> 
  624. // Select annual by default. 
  625. $plan.find('.nav-tab[data-billing-cycle=annual]').click(); 
  626. <?php else : ?> 
  627. // Select first tab. 
  628. $plan.find('.nav-tab:first').click(); 
  629. <?php endif ?> 
  630. }); 
  631. }(jQuery)); 
  632. </script> 
  633. </div> 
  634. <div class="fs-pricing-body"> 
  635. <span class="fs-billing-frequency"></span> 
  636. <?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?> 
  637. <?php if ( $annual_discount > 0 ) : ?> 
  638. <span 
  639. class="fs-annual-discount"><?php printf( __fs( 'save-x', $api->slug ), $annual_discount . '%' ) ?></span> 
  640. <?php endif ?> 
  641. <ul class="fs-licenses"> 
  642. </ul> 
  643. <?php echo $this->get_plugin_cta( $api, $plan ) ?> 
  644. <div style="clear:both"></div> 
  645. <?php if ( $plan->has_trial() ) : ?> 
  646. <?php $trial_period = $this->get_trial_period( $plan ) ?> 
  647. <ul class="fs-trial-terms"> 
  648. <li> 
  649. <i class="dashicons dashicons-yes"></i><?php printf( __fs( 'no-commitment-x', $api->slug ), $trial_period ) ?> 
  650. </li> 
  651. <li> 
  652. <i class="dashicons dashicons-yes"></i><?php printf( __fs( 'after-x-pay-as-little-y', $api->slug ), $trial_period, '<var class="fs-price">' . $this->get_price_tag( $plan, $plan->pricing[0] ) . '</var>' ) ?> 
  653. </li> 
  654. </ul> 
  655. <?php endif ?> 
  656. </div> 
  657. </div> 
  658. </div> 
  659. <?php endforeach ?> 
  660. <?php endif ?> 
  661. <?php endif ?> 
  662. <div> 
  663. <h3><?php _efs( 'details', $api->slug ) ?></h3> 
  664. <ul> 
  665. <?php if ( ! empty( $api->version ) ) { ?> 
  666. <li><strong><?php _e( 'Version:' ); ?></strong> <?php echo $api->version; ?></li> 
  667. <?php 
  668. if ( ! empty( $api->author ) ) { 
  669. ?> 
  670. <li> 
  671. <strong><?php _e( 'Author:' ); ?></strong> <?php echo links_add_target( $api->author, '_blank' ); ?> 
  672. </li> 
  673. <?php 
  674. if ( ! empty( $api->last_updated ) ) { 
  675. ?> 
  676. <li><strong><?php _e( 'Last Updated:' ); ?></strong> <span 
  677. title="<?php echo $api->last_updated; ?>"> 
  678. <?php printf( __( '%s ago' ), human_time_diff( strtotime( $api->last_updated ) ) ); ?> 
  679. </span></li> 
  680. <?php 
  681. if ( ! empty( $api->requires ) ) { 
  682. ?> 
  683. <li> 
  684. <strong><?php _e( 'Requires WordPress Version:' ); ?></strong> <?php printf( __( '%s or higher' ), $api->requires ); ?> 
  685. </li> 
  686. <?php 
  687. if ( ! empty( $api->tested ) ) { 
  688. ?> 
  689. <li><strong><?php _e( 'Compatible up to:' ); ?></strong> <?php echo $api->tested; ?> 
  690. </li> 
  691. <?php 
  692. if ( ! empty( $api->downloaded ) ) { 
  693. ?> 
  694. <li> 
  695. <strong><?php _e( 'Downloaded:' ); ?></strong> <?php printf( _n( '%s time', '%s times', $api->downloaded ), number_format_i18n( $api->downloaded ) ); ?> 
  696. </li> 
  697. <?php 
  698. if ( ! empty( $api->slug ) && empty( $api->external ) ) { 
  699. ?> 
  700. <li><a target="_blank" 
  701. href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php _e( 'WordPress.org Plugin Page »' ); ?></a> 
  702. </li> 
  703. <?php 
  704. if ( ! empty( $api->homepage ) ) { 
  705. ?> 
  706. <li><a target="_blank" 
  707. href="<?php echo esc_url( $api->homepage ); ?>"><?php _e( 'Plugin Homepage »' ); ?></a> 
  708. </li> 
  709. <?php 
  710. if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) { 
  711. ?> 
  712. <li><a target="_blank" 
  713. href="<?php echo esc_url( $api->donate_link ); ?>"><?php _e( 'Donate to this plugin »' ); ?></a> 
  714. </li> 
  715. <?php } ?> 
  716. </ul> 
  717. </div> 
  718. <?php if ( ! empty( $api->rating ) ) { ?> 
  719. <h3><?php _e( 'Average Rating' ); ?></h3> 
  720. <?php wp_star_rating( array( 
  721. 'rating' => $api->rating,  
  722. 'type' => 'percent',  
  723. 'number' => $api->num_ratings 
  724. ) ); ?> 
  725. <small><?php printf( _n( '(based on %s rating)', '(based on %s ratings)', $api->num_ratings ), number_format_i18n( $api->num_ratings ) ); ?></small> 
  726. <?php 
  727.  
  728. if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) { 
  729. foreach ( $api->ratings as $key => $ratecount ) { 
  730. // Avoid div-by-zero. 
  731. $_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0; 
  732. ?> 
  733. <div class="counter-container"> 
  734. <span class="counter-label"><a 
  735. href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>" 
  736. target="_blank" 
  737. title="<?php echo esc_attr( sprintf( _n( 'Click to see reviews that provided a rating of %d star', 'Click to see reviews that provided a rating of %d stars', $key ), $key ) ); ?>"><?php printf( _n( '%d star', '%d stars', $key ), $key ); ?></a></span> 
  738. <span class="counter-back"> 
  739. <span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span> 
  740. </span> 
  741. <span class="counter-count"><?php echo number_format_i18n( $ratecount ); ?></span> 
  742. </div> 
  743. <?php 
  744. if ( ! empty( $api->contributors ) ) { 
  745. ?> 
  746. <h3><?php _e( 'Contributors' ); ?></h3> 
  747. <ul class="contributors"> 
  748. <?php 
  749. foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) { 
  750. if ( empty( $contrib_username ) && empty( $contrib_profile ) ) { 
  751. continue; 
  752. if ( empty( $contrib_username ) ) { 
  753. $contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile ); 
  754. $contrib_username = sanitize_user( $contrib_username ); 
  755. if ( empty( $contrib_profile ) ) { 
  756. echo "<li><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&s=36' width='18' height='18' />{$contrib_username}</li>"; 
  757. } else { 
  758. echo "<li><a href='{$contrib_profile}' target='_blank'><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&s=36' width='18' height='18' />{$contrib_username}</a></li>"; 
  759. ?> 
  760. </ul> 
  761. <?php if ( ! empty( $api->donate_link ) ) { ?> 
  762. <a target="_blank" 
  763. href="<?php echo esc_url( $api->donate_link ); ?>"><?php _e( 'Donate to this plugin »' ); ?></a> 
  764. <?php } ?> 
  765. <?php } ?> 
  766. </div> 
  767. <div id="section-holder" class="wrap"> 
  768. <?php 
  769. if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) { 
  770. echo '<div class="notice notice-warning"><p>' . '<strong>' . __( 'Warning:' ) . '</strong> ' . __( 'This plugin has not been tested with your current version of WordPress.' ) . '</p></div>'; 
  771. } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) { 
  772. echo '<div class="notice notice-warning"><p>' . '<strong>' . __( 'Warning:' ) . '</strong> ' . __( 'This plugin has not been marked as compatible with your version of WordPress.' ) . '</p></div>'; 
  773.  
  774. foreach ( (array) $api->sections as $section_name => $content ) { 
  775. $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' ); 
  776. $content = links_add_target( $content, '_blank' ); 
  777.  
  778. $san_section = esc_attr( $section_name ); 
  779.  
  780. $display = ( $section_name === $section ) ? 'block' : 'none'; 
  781.  
  782. if ( 'description' === $section_name && 
  783. ( ( ! $api->external && $api->wp_org_missing ) || 
  784. ( $api->external && $api->fs_missing ) ) 
  785. ) { 
  786. $missing_notice = array( 
  787. 'type' => 'error',  
  788. 'id' => md5( microtime() ),  
  789. 'message' => __fs( ( $api->is_paid ? 'paid-addon-not-deployed' : 'free-addon-not-deployed' ), $api->slug ),  
  790. ); 
  791. fs_require_template( 'admin-notice.php', $missing_notice ); 
  792. echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n"; 
  793. echo $content; 
  794. echo "\t</div>\n"; 
  795. echo "</div>\n"; 
  796. echo "</div>\n"; 
  797. echo "</div>\n"; // #plugin-information-scrollable 
  798. echo "<div id='$tab-footer'>\n"; 
  799.  
  800. echo $this->get_plugin_cta( $api ); 
  801.  
  802. echo "</div>\n"; 
  803.  
  804. iframe_footer(); 
  805. exit;