addons.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. <?php
  2. /**
  3. * Addons class.
  4. *
  5. * @since 6.0.0
  6. *
  7. * @package MonsterInsights
  8. * @author Chris Christoff
  9. */
  10. // Exit if accessed directly
  11. if ( ! defined( 'ABSPATH' ) ) {
  12. exit;
  13. }
  14. function monsterinsights_is_addons_page() {
  15. $current_screen = get_current_screen();
  16. global $admin_page_hooks;
  17. if ( ! is_object( $current_screen ) || empty( $current_screen->id ) || empty( $admin_page_hooks ) ) {
  18. return false;
  19. }
  20. $settings_page = false;
  21. if ( ! empty( $admin_page_hooks['monsterinsights_dashboard'] ) && $current_screen->id === $admin_page_hooks['monsterinsights_dashboard'] . '_page_monsterinsights_addons' ) {
  22. $settings_page = true;
  23. }
  24. if ( ! empty( $admin_page_hooks['monsterinsights_settings'] ) && $current_screen->id === $admin_page_hooks['monsterinsights_settings'] . '_page_monsterinsights_addons' ) {
  25. $settings_page = true;
  26. }
  27. if ( ! empty( $admin_page_hooks['monsterinsights_network'] ) && $current_screen->id === $admin_page_hooks['monsterinsights_network'] . '_page_monsterinsights_addons-network' ) {
  28. $settings_page = true;
  29. }
  30. return $settings_page;
  31. }
  32. /**
  33. * Maybe refreshes the addons page.
  34. *
  35. * @since 6.0.0
  36. *
  37. * @return null Return early if not refreshing the addons.
  38. */
  39. function monsterinsights_maybe_refresh_addons() {
  40. if ( ! monsterinsights_is_addons_page() ) {
  41. return;
  42. }
  43. if ( empty( $_POST['google-analytics-for-wordpress-refresh-addons-submit'] ) ) {
  44. return;
  45. }
  46. if ( ! wp_verify_nonce( $_POST['google-analytics-for-wordpress-refresh-addons'], 'google-analytics-for-wordpress-refresh-addons' ) ) {
  47. return;
  48. }
  49. monsterinsights_get_addons_data( MonsterInsights()->license->get_valid_license_key() );
  50. }
  51. add_action( 'current_screen', 'monsterinsights_maybe_refresh_addons' );
  52. /**
  53. * Callback to output the MonsterInsights addons page.
  54. *
  55. * @since 6.0.0
  56. */
  57. function monsterinsights_addons_page() {
  58. /**
  59. * Developer Alert:
  60. *
  61. * Per the README, this is considered an internal hook and should
  62. * not be used by other developers. This hook's behavior may be modified
  63. * or the hook may be removed at any time, without warning.
  64. */
  65. do_action('monsterinsights_head');
  66. ?>
  67. <?php echo monsterinsights_ublock_notice(); ?>
  68. <div id="monsterinsights-addon-heading" class="monsterinsights-addons-subheading monsterinsights-clearfix-after">
  69. <h1><?php esc_html_e( 'MonsterInsights Addons', 'google-analytics-for-wordpress' ); ?></h1>
  70. <form id="add-on-search">
  71. <span class="spinner"></span>
  72. <input id="add-on-searchbox" name="monsterinsights-addon-search" value="" placeholder="<?php esc_attr_e( 'Search MI Addons', 'google-analytics-for-wordpress' ); ?>" />
  73. <select id="monsterinsights-filter-select">
  74. <option value="asc"><?php esc_html_e( 'Sort Ascending (A-Z)', 'google-analytics-for-wordpress' ); ?></option>
  75. <option value="desc"><?php esc_html_e( 'Sort Descending (Z-A)', 'google-analytics-for-wordpress' ); ?></option>
  76. </select>
  77. </form>
  78. </div>
  79. <div id="monsterinsights-addons" class="wrap">
  80. <div class="monsterinsights-clear">
  81. <?php
  82. /**
  83. * Developer Alert:
  84. *
  85. * Per the README, this is considered an internal hook and should
  86. * not be used by other developers. This hook's behavior may be modified
  87. * or the hook may be removed at any time, without warning.
  88. */
  89. ?>
  90. <?php do_action( 'monsterinsights_addons_section' ); ?>
  91. </div>
  92. </div>
  93. <?php
  94. }
  95. /**
  96. * Callback for displaying the UI for Addons.
  97. *
  98. * @since 6.0.0
  99. */
  100. function monsterinsights_addons_content() {
  101. // If error(s) occurred during license key verification, display them and exit now.
  102. if ( monsterinsights_is_pro_version() && ! MonsterInsights()->license->get_valid_license_key() ) {
  103. ?>
  104. <div class="error below-h2">
  105. <p>
  106. <?php esc_html_e( 'In order to get access to Addons, you need to resolve your license key errors.', 'google-analytics-for-wordpress' ); ?>
  107. </p>
  108. </div>
  109. <?php
  110. return;
  111. }
  112. // Get Addons
  113. $addons = monsterinsights_get_addons();
  114. // If no Addon(s) were returned, our API call returned an error.
  115. // Show an error message with a button to reload the page, which will trigger another API call.
  116. if ( ! $addons ) {
  117. ?>
  118. <form id="monsterinsights-addons-refresh-addons-form" method="post">
  119. <p>
  120. <?php esc_html_e( 'There was an issue retrieving the addons for this site. Please click on the button below the refresh the addons data.', 'google-analytics-for-wordpress' ); ?>
  121. </p>
  122. <p>
  123. <a href="<?php echo esc_url( $_SERVER['REQUEST_URI'] ); ?>" class="button button-primary"><?php esc_html_e( 'Refresh Addons', 'google-analytics-for-wordpress' ); ?></a>
  124. </p>
  125. </form>
  126. <?php
  127. return;
  128. }
  129. // If here, we have Addons to display, so let's output them now.
  130. // Get installed plugins and upgrade URL
  131. $installed_plugins = get_plugins();
  132. $upgrade_url = monsterinsights_get_upgrade_link();
  133. ?>
  134. <div id="monsterinsights-addons">
  135. <?php
  136. // Output Addons the User is licensed to use.
  137. if ( count( $addons['licensed'] )> 0 ) {
  138. ?>
  139. <div class="monsterinsights-addons-area licensed" class="monsterinsights-clear">
  140. <h3><?php esc_html_e( 'Available Addons:', 'google-analytics-for-wordpress' ); ?></h3>
  141. <div id="monsterinsights-addons-licensed" class="monsterinsights-addons">
  142. <!-- list container class required for list.js -->
  143. <div class="list">
  144. <?php
  145. foreach ( (array) $addons['licensed'] as $i => $addon ) {
  146. monsterinsights_get_addon_card( $addon, $i, true, $installed_plugins );
  147. }
  148. ?>
  149. </div>
  150. </div>
  151. </div>
  152. <?php
  153. } // Close licensed addons
  154. // Output Addons the User isn't licensed to use.
  155. if ( count( $addons['unlicensed'] ) > 0 ) {
  156. ?>
  157. <div class="monsterinsights-addons-area unlicensed" class="monsterinsights-clear">
  158. <h3><?php esc_html_e( 'Unlock More Addons', 'google-analytics-for-wordpress' ); ?></h3>
  159. <p><?php echo sprintf( esc_html__( '%1$sWant even more addons?%2$sUpgrade your MonsterInsights account%3$s and unlock the following addons:', 'google-analytics-for-wordpress' ), '<strong>', '</strong> <a href="' . $upgrade_url. '">', '</a>' ); ?></p>
  160. <div id="monsterinsights-addons-unlicensed" class="monsterinsights-addons">
  161. <!-- list container class required for list.js -->
  162. <div class="list">
  163. <?php
  164. foreach ( (array) $addons['unlicensed'] as $i => $addon ) {
  165. monsterinsights_get_addon_card( $addon, $i, false, $installed_plugins );
  166. }
  167. ?>
  168. </div>
  169. </div>
  170. </div>
  171. <?php
  172. } // Close unlicensed addons
  173. ?>
  174. </div>
  175. <?php
  176. }
  177. add_action( 'monsterinsights_addons_section', 'monsterinsights_addons_content' );
  178. /**
  179. * Retrieves addons from the stored transient or remote server.
  180. *
  181. * @since 6.0.0
  182. *
  183. * @return bool | array false | Array of licensed and unlicensed Addons.
  184. */
  185. function monsterinsights_get_addons() {
  186. // Get license key and type.
  187. $key = is_network_admin() ? MonsterInsights()->license->get_network_license_key() : MonsterInsights()->license->get_site_license_key();
  188. $type = is_network_admin() ? MonsterInsights()->license->get_network_license_type() : MonsterInsights()->license->get_site_license_type();
  189. // Get addons data from transient or perform API query if no transient.
  190. if ( false === ( $addons = get_transient( '_monsterinsights_addons' ) ) ) {
  191. $addons = monsterinsights_get_addons_data( $key );
  192. }
  193. // If no Addons exist, return false
  194. if ( ! $addons ) {
  195. return false;
  196. }
  197. // Iterate through Addons, to build two arrays:
  198. // - Addons the user is licensed to use,
  199. // - Addons the user isn't licensed to use.
  200. $results = array(
  201. 'licensed' => array(),
  202. 'unlicensed'=> array(),
  203. );
  204. foreach ( (array) $addons as $i => $addon ) {
  205. // Determine whether the user is licensed to use this Addon or not.
  206. if (
  207. empty( $type ) ||
  208. ( in_array( 'Pro', $addon->categories ) && ( $type != 'pro' && $type != 'master' ) ) ||
  209. ( in_array( 'Plus', $addon->categories ) && $type != 'plus' && $type != 'pro' && $type != 'master' ) ||
  210. ( in_array( 'Basic', $addon->categories ) && ( $type != 'basic' && $type != 'plus' && $type != 'pro' && $type != 'master' ) )
  211. ) {
  212. // Unlicensed
  213. $results['unlicensed'][] = $addon;
  214. continue;
  215. }
  216. // Licensed
  217. $results['licensed'][] = $addon;
  218. }
  219. // Return Addons, split by licensed and unlicensed.
  220. return $results;
  221. }
  222. /**
  223. * Pings the remote server for addons data.
  224. *
  225. * @since 6.0.0
  226. *
  227. * @param string $key The user license key.
  228. * @return array Array of addon data otherwise.
  229. */
  230. function monsterinsights_get_addons_data( $key ) {
  231. $type = is_network_admin() ? MonsterInsights()->license->get_network_license_type() : MonsterInsights()->license->get_site_license_type();
  232. // Get Addons
  233. // If the key is valid, we'll get personalised upgrade URLs for each Addon (if necessary) and plugin update information.
  234. if ( $key ) {
  235. $addons = MonsterInsights()->license_actions->perform_remote_request( 'get-addons-data-v600', array( 'tgm-updater-key' => $key ) );
  236. } else {
  237. $addons = MonsterInsights()->license_actions->perform_remote_request( 'get-all-addons-data', array() );
  238. }
  239. // If there was an API error, set transient for only 10 minutes.
  240. if ( ! $addons ) {
  241. set_transient( '_monsterinsights_addons', false, 10 * MINUTE_IN_SECONDS );
  242. return false;
  243. }
  244. // If there was an error retrieving the addons, set the error.
  245. if ( isset( $addons->error ) ) {
  246. set_transient( '_monsterinsights_addons', false, 10 * MINUTE_IN_SECONDS );
  247. return false;
  248. }
  249. // Otherwise, our request worked. Save the data and return it.
  250. set_transient( '_monsterinsights_addons', $addons, 4 * HOUR_IN_SECONDS );
  251. return $addons;
  252. }
  253. /**
  254. * Retrieve the plugin basename from the plugin slug.
  255. *
  256. * @since 6.0.0
  257. *
  258. * @param string $slug The plugin slug.
  259. * @return string The plugin basename if found, else the plugin slug.
  260. */
  261. function monsterinsights_get_plugin_basename_from_slug( $slug ) {
  262. $keys = array_keys( get_plugins() );
  263. foreach ( $keys as $key ) {
  264. if ( preg_match( '|^' . $slug . '|', $key ) ) {
  265. return $key;
  266. }
  267. }
  268. return $slug;
  269. }
  270. /**
  271. * Outputs the addon "box" on the addons page.
  272. *
  273. * @since 6.0.0
  274. *
  275. * @param object $addon Addon data from the API / transient call
  276. * @param int $counter Index of this Addon in the collection
  277. * @param bool $is_licensed Whether the Addon is licensed for use
  278. * @param array $installed_plugins Installed WordPress Plugins
  279. */
  280. function monsterinsights_get_addon_card( $addon, $counter = 0, $is_licensed = false, $installed_plugins = false ) {
  281. // Setup some vars
  282. $slug = str_replace( 'monsterinsights-', '', $addon->slug );
  283. $slug = 'monsterinsights-' . $addon->slug;
  284. if ( $slug === 'monsterinsights-ecommerce' ) {
  285. $slug = 'ga-ecommerce';
  286. }
  287. $plugin_basename = monsterinsights_get_plugin_basename_from_slug( $slug );
  288. $categories = implode( ',', $addon->categories );
  289. if ( ! $installed_plugins ) {
  290. $installed_plugins = get_plugins();
  291. }
  292. // If the Addon doesn't supply an upgrade_url key, it's because the user hasn't provided a license
  293. // get_upgrade_link() will return the Lite or Pro link as necessary for us.
  294. if ( ! isset( $addon->upgrade_url ) ) {
  295. $addon->upgrade_url = monsterinsights_get_upgrade_link();
  296. }
  297. // Link user to doc to install MI pro to install addons
  298. if ( ! monsterinsights_is_pro_version() && $is_licensed && ! isset( $installed_plugins[ $plugin_basename ] ) ) {
  299. $addon->url = monsterinsights_get_url( 'addons-page', 'install-addons-link', "https://www.monsterinsights.com/docs/install-monsterinsights-pro-to-use-addons" );
  300. }
  301. // Output the card
  302. ?>
  303. <div class="monsterinsights-addon">
  304. <h3 class="monsterinsights-addon-title"><?php echo esc_html( $addon->title ); ?></h3>
  305. <?php
  306. if ( ! empty( $addon->image ) ) {
  307. ?>
  308. <img class="monsterinsights-addon-thumb" src="<?php echo esc_attr( esc_url( $addon->image ) ); ?>" alt="<?php echo esc_attr( $addon->title ); ?>" />
  309. <?php
  310. }
  311. ?>
  312. <p class="monsterinsights-addon-excerpt"><?php echo esc_html( $addon->excerpt ); ?></p>
  313. <?php
  314. // If the Addon is unlicensed, show the upgrade button
  315. if ( ! $is_licensed ) {
  316. ?>
  317. <div class="monsterinsights-addon-active monsterinsights-addon-message">
  318. <div class="interior">
  319. <div class="monsterinsights-addon-upgrade">
  320. <a href="<?php echo esc_attr( esc_url( $addon->upgrade_url ) ); ?>" target="_blank" rel="noopener noreferrer" referrer="no-referrer" class="button button-primary monsterinsights-addon-upgrade-button" rel="<?php echo esc_attr( $plugin_basename ); ?>">
  321. <?php esc_html_e( 'Upgrade Now', 'google-analytics-for-wordpress' ); ?>
  322. </a>
  323. <span class="spinner monsterinsights-spinner"></span>
  324. </div>
  325. </div>
  326. </div>
  327. <?php
  328. } else {
  329. // Addon is licensed
  330. // If the plugin is not installed, display an install message and button.
  331. if ( ! isset( $installed_plugins[ $plugin_basename ] ) ) {
  332. if ( empty( $addon->url ) ) {
  333. $addon->url = '';
  334. }
  335. ?>
  336. <div class="monsterinsights-addon-not-installed monsterinsights-addon-message">
  337. <div class="interior">
  338. <?php if ( monsterinsights_is_pro_version() ) { ?>
  339. <span class="addon-status"><?php echo sprintf( esc_html__( 'Status: %1$sNot Installed%2$s', 'google-analytics-for-wordpress' ), '<span>', '</span>' ); ?></span>
  340. <?php } ?>
  341. <div class="monsterinsights-addon-action">
  342. <?php if ( monsterinsights_is_pro_version() ) { ?>
  343. <a class="button button-primary monsterinsights-addon-action-button monsterinsights-install-addon" href="#" rel="<?php echo esc_attr( esc_url( $addon->url ) ); ?>">
  344. <i class="monsterinsights-cloud-download"></i>
  345. <?php esc_html_e( 'Install', 'google-analytics-for-wordpress' ); ?>
  346. </a>
  347. <?php } else { ?>
  348. <a class="button button-primary monsterinsights-addon-action-button" href="<?php echo esc_url( $addon->url ); ?>" rel="noopener noreferrer" referrer="no-referrer" target="_blank">
  349. <i class="monsterinsights-cloud-download"></i>
  350. <?php esc_html_e( "Why can't I install addons?", 'google-analytics-for-wordpress' ); ?>
  351. </a>
  352. <?php } ?>
  353. <span class="spinner monsterinsights-spinner"></span>
  354. </div>
  355. </div>
  356. </div>
  357. <?php
  358. } else {
  359. // Plugin is installed.
  360. $active = false;
  361. $ms_active = is_plugin_active_for_network( $plugin_basename );
  362. $ss_active = is_plugin_active( $plugin_basename );
  363. if ( is_multisite() && is_network_admin() ) {
  364. $active = is_plugin_active_for_network( $plugin_basename );
  365. } else {
  366. $active = is_plugin_active( $plugin_basename );
  367. }
  368. if ( $active ) {
  369. // Plugin is active. Display the active message and deactivate button.
  370. ?>
  371. <div class="monsterinsights-addon-active monsterinsights-addon-message">
  372. <div class="interior">
  373. <?php if ( $ms_active ) { ?>
  374. <span class="addon-status"><?php echo sprintf( esc_html__( 'Status: %1$sNetwork Active%2$s', 'google-analytics-for-wordpress'), '<span>', '</span>' ); ?></span>
  375. <?php } else { ?>
  376. <span class="addon-status"><?php echo sprintf( esc_html__( 'Status: %1$sActive%2$s', 'google-analytics-for-wordpress'), '<span>', '</span>' ); ?></span>
  377. <?php } ?>
  378. <?php if ( ( is_multisite() && is_network_admin() && $ms_active ) || ! is_multisite() || ( is_multisite() && !is_network_admin() && !$ms_active && $ss_active ) ) { ?>
  379. <div class="monsterinsights-addon-action">
  380. <a class="button button-primary monsterinsights-addon-action-button monsterinsights-deactivate-addon" href="#" rel="<?php echo esc_attr( $plugin_basename ); ?>">
  381. <i class="monsterinsights-toggle-on"></i>
  382. <?php if ( is_multisite() && is_network_admin() && $ms_active ) { ?>
  383. <?php esc_html_e( 'Network deactivate', 'google-analytics-for-wordpress' ); ?>
  384. <?php } else if ( is_multisite() && !is_network_admin() && !$ms_active && $ss_active ) { ?>
  385. <?php esc_html_e( 'Deactivate', 'google-analytics-for-wordpress' ); ?>
  386. <?php } else { ?>
  387. <?php esc_html_e( 'Deactivate', 'google-analytics-for-wordpress' ); ?>
  388. <?php } ?>
  389. </a>
  390. <span class="spinner google-analytics-for-wordpress-spinner"></span>
  391. </div>
  392. <?php } ?>
  393. </div>
  394. </div>
  395. <?php
  396. } else {
  397. // Plugin is inactivate. Display the inactivate mesage and activate button.
  398. ?>
  399. <div class="monsterinsights-addon-inactive monsterinsights-addon-message">
  400. <div class="interior">
  401. <?php if ( $ms_active ) { ?>
  402. <span class="addon-status"><?php echo sprintf( esc_html__( 'Status: %1$sNetwork Inactive%2$s', 'google-analytics-for-wordpress'), '<span>', '</span>' ); ?></span>
  403. <?php } else { ?>
  404. <span class="addon-status"><?php echo sprintf( esc_html__( 'Status: %1$sInactive%2$s', 'google-analytics-for-wordpress'), '<span>', '</span>' ); ?></span>
  405. <?php } ?>
  406. <div class="monsterinsights-addon-action">
  407. <a class="button button-primary monsterinsights-addon-action-button monsterinsights-activate-addon" href="#" rel="<?php echo esc_attr( $plugin_basename ); ?>">
  408. <i class="monsterinsights-toggle-on"></i>
  409. <?php if ( is_multisite() && is_network_admin() && ! $ms_active ) { ?>
  410. <?php esc_html_e( 'Network activate', 'google-analytics-for-wordpress' ); ?>
  411. <?php } else { ?>
  412. <?php esc_html_e( 'Activate', 'google-analytics-for-wordpress' ); ?>
  413. <?php } ?>
  414. </a>
  415. <span class="spinner monsterinsights-spinner"></span>
  416. </div>
  417. </div>
  418. </div>
  419. <?php
  420. }
  421. }
  422. }
  423. ?>
  424. </div>
  425. <?php
  426. }