/app/model/class-ms-model-addon.php

  1. <?php 
  2. /** 
  3. * Manage Add-ons. 
  4. * 
  5. * Add-ons are stored in the directory /app/addon/<addon_name>/ 
  6. * Each Add-on must provide a file called `addon-<addon_name>.php` 
  7. * This file must define class MS_Addon_<addon_name>. 
  8. * This object is reponsible to initialize the the add-on logic. 
  9. * 
  10. * @since 1.0.0 
  11. * 
  12. * @package Membership2 
  13. * @subpackage Model 
  14. */ 
  15. class MS_Model_Addon extends MS_Model_Option { 
  16.  
  17. /** 
  18. * Add-on name constants. 
  19. * 
  20. * @deprecated Since 1.1.0 the Add-On constants are deprecated. 
  21. * Use the appropriate hooks to register new addons! 
  22. * Example: See the "Taxamo" addon 
  23. * 
  24. * @since 1.0.0 
  25. * 
  26. * @var string 
  27. */ 
  28. const ADDON_MULTI_MEMBERSHIPS = 'multi_memberships'; 
  29. const ADDON_POST_BY_POST = 'post_by_post'; 
  30. const ADDON_URL_GROUPS = 'url_groups'; 
  31. const ADDON_CPT_POST_BY_POST = 'cpt_post_by_post'; 
  32. const ADDON_TRIAL = 'trial'; 
  33. const ADDON_MEDIA = 'media'; 
  34. const ADDON_SHORTCODE = 'shortcode'; 
  35. const ADDON_AUTO_MSGS_PLUS = 'auto_msgs_plus'; 
  36. const ADDON_SPECIAL_PAGES = 'special_pages'; 
  37. const ADDON_ADV_MENUS = 'adv_menus'; 
  38. const ADDON_ADMINSIDE = 'adminside'; 
  39. const ADDON_MEMBERCAPS = 'membercaps'; 
  40. const ADDON_MEMBERCAPS_ADV = 'membercaps_advanced'; 
  41.  
  42. /** 
  43. * List of all registered Add-ons 
  44. * 
  45. * Related hook: ms_model_addon_register 
  46. * 
  47. * @var array { 
  48. * @key <string> The add-on ID. 
  49. * @value object { 
  50. * The add-on data. 
  51. * 
  52. * $name <string> Display name 
  53. * $parent <string> Empty/The Add-on ID of the parent 
  54. * $description <string> Description 
  55. * $footer <string> For the Add-ons list 
  56. * $icon <string> For the Add-ons list 
  57. * $class <string> For the Add-ons list 
  58. * $details <array of HTML elements> For the Add-ons list 
  59. * } 
  60. * } 
  61. */ 
  62. static private $_registered = array(); 
  63.  
  64. /** 
  65. * Used by function `flush_list` 
  66. * 
  67. * @since 1.0.0 
  68. * 
  69. * @var bool 
  70. */ 
  71. static private $_reload_files = false; 
  72.  
  73. /** 
  74. * List of add-on files to load when plugin is initialized. 
  75. * 
  76. * @since 1.0.0 
  77. * 
  78. * @var array of file-paths 
  79. */ 
  80. protected $addon_files = array(); 
  81.  
  82. /** 
  83. * Add-ons array. 
  84. * 
  85. * @since 1.0.0 
  86. * 
  87. * @var array { 
  88. * @key <string> The add-on ID. 
  89. * @value <boolean> The add-on enbled status (always true). 
  90. * } 
  91. */ 
  92. protected $active = array(); 
  93.  
  94. /** 
  95. * Initalize Object Hooks 
  96. * 
  97. * @since 1.0.0 
  98. */ 
  99. public function __construct() { 
  100. parent::__construct(); 
  101.  
  102. $this->add_action( 'ms_model_addon_flush', 'flush_list' ); 
  103.  
  104. /** 
  105. * Returns a list of all registered Add-Ons 
  106. * 
  107. * @since 1.0.0 
  108. * @return array Add-on lisl 
  109. */ 
  110. static public function get_addons() { 
  111. static $Done = false; 
  112. $res = null; 
  113.  
  114. if ( ! $Done || self::$_reload_files ) { 
  115. self::$_registered = array(); 
  116. $addons = array(); 
  117. $Done = true; 
  118. self::load_core_addons(); 
  119.  
  120. // Register core add-ons 
  121. $addons = self::get_core_list(); 
  122.  
  123. /** 
  124. * Register new addons. 
  125. * 
  126. * @since 1.0.0 
  127. */ 
  128. $addons = apply_filters( 
  129. 'ms_model_addon_register',  
  130. $addons 
  131. ); 
  132.  
  133. // Sanitation and populate default fields. 
  134. foreach ( $addons as $key => $data ) { 
  135. self::$_registered[ $key ] = $data->name; 
  136.  
  137. $addons[ $key ]->id = $key; 
  138. $addons[ $key ]->active = self::is_enabled( $key ); 
  139. $addons[ $key ]->title = $data->name; 
  140.  
  141. if ( isset( $addons[ $key ]->icon ) ) { 
  142. $addons[ $key ]->icon = '<i class="' . $addons[ $key ]->icon . '"></i>'; 
  143. } else { 
  144. $addons[ $key ]->icon = '<i class="wpmui-fa wpmui-fa-puzzle-piece"></i>'; 
  145.  
  146. if ( empty( $addons[ $key ]->action ) ) { 
  147. $addons[ $key ]->action = array(); 
  148. $addons[ $key ]->action[] = array( 
  149. 'id' => 'ms-toggle-' . $key,  
  150. 'type' => MS_Helper_Html::INPUT_TYPE_RADIO_SLIDER,  
  151. 'value' => self::is_enabled( $key ),  
  152. 'class' => 'toggle-plugin',  
  153. 'ajax_data' => array( 
  154. 'action' => MS_Controller_Addon::AJAX_ACTION_TOGGLE_ADDON,  
  155. 'field' => 'active',  
  156. 'addon' => $key,  
  157. ),  
  158. ); 
  159. $addons[ $key ]->action[] = MS_Helper_Html::save_text( null, false, true ); 
  160.  
  161. /** 
  162. * Add custom Actions or remove default actions 
  163. * 
  164. * @since 1.0.0 
  165. */ 
  166. $addons[ $key ]->action = apply_filters( 
  167. 'ms_model_addon_action-' . $key,  
  168. $addons[ $key ]->action,  
  169. $addons[ $key ] 
  170. ); 
  171.  
  172. natcasesort( self::$_registered ); 
  173. foreach ( self::$_registered as $key => $dummy ) { 
  174. self::$_registered[ $key ] = $addons[ $key ]; 
  175.  
  176. /** 
  177. * The Add-on list is prepared. Initialize the addons now. 
  178. * 
  179. * @since 1.0.0 
  180. */ 
  181. do_action( 'ms_model_addon_initialize' ); 
  182.  
  183. return self::$_registered; 
  184.  
  185. /** 
  186. * Force to reload the add-on list 
  187. * 
  188. * Related action hooks: 
  189. * - ms_model_addon_flush 
  190. * 
  191. * @since 1.0.0 
  192. */ 
  193. public function flush_list() { 
  194. self::$_reload_files = true; 
  195. self::get_addons(); 
  196.  
  197. /** 
  198. * Checks the /app/addon directory for a list of all addons and loads these 
  199. * files. 
  200. * 
  201. * @since 1.0.0 
  202. */ 
  203. static protected function load_core_addons() { 
  204. $model = MS_Factory::load( 'MS_Model_Addon' ); 
  205. $content_dir = trailingslashit( dirname( dirname( MS_Plugin::instance()->dir ) ) ); 
  206. $plugin_dir = substr( MS_Plugin::instance()->dir, strlen( $content_dir ) ); 
  207.  
  208. $addon_dirs = array( 
  209. $plugin_dir . 'premium/addon/',  
  210. $plugin_dir . 'app/addon/',  
  211. ); 
  212.  
  213. if ( empty( $model->addon_files ) || self::$_reload_files ) { 
  214. // In Admin dashboard we always refresh the addon-list... 
  215. self::$_reload_files = false; 
  216. $model->addon_files = array(); 
  217.  
  218. foreach ( $addon_dirs as $addon_dir ) { 
  219. $mask = $content_dir . $addon_dir . '*/class-ms-addon-*.php'; 
  220. $addons = glob( $mask ); 
  221.  
  222. foreach ( $addons as $file ) { 
  223. $model->addon_files[] = substr( $file, strlen( $content_dir ) ); 
  224.  
  225. /** 
  226. * Allow other plugins/themes to register custom addons 
  227. * 
  228. * @since 1.0.0 
  229. * 
  230. * @var array 
  231. */ 
  232. $model->addon_files = apply_filters( 
  233. 'ms_model_addon_files',  
  234. $model->addon_files 
  235. ); 
  236.  
  237. $model->save(); 
  238.  
  239. // Loop all recignized Add-ons and initialize them. 
  240. foreach ( $model->addon_files as $file ) { 
  241. $addon = $content_dir . $file; 
  242.  
  243. // Get class-name from file-name 
  244. $class = basename( $file ); 
  245. $class = str_replace( '.php', '', $class ); 
  246. $class = implode( '_', array_map( 'ucfirst', explode( '-', $class ) ) ); 
  247. $class = substr( $class, 6 ); // remove 'Class_' prefix 
  248.  
  249. if ( file_exists( $addon ) ) { 
  250. if ( ! class_exists( $class ) ) { 
  251. try { 
  252. include_once $addon; 
  253. } catch ( Exception $ex ) { 
  254.  
  255. if ( class_exists( $class ) ) { 
  256. MS_Factory::load( $class ); 
  257.  
  258. /** 
  259. * Allow custom addon-initialization code to run 
  260. * 
  261. * @since 1.0.0 
  262. */ 
  263. do_action( 'ms_model_addon_load' ); 
  264.  
  265. /** 
  266. * Verify if an add-on is enabled 
  267. * 
  268. * @since 1.0.0 
  269. * 
  270. * @var string $addon The add-on type. 
  271. * @return boolean True if enabled. 
  272. */ 
  273. static public function is_enabled( $addon ) { 
  274. $model = MS_Factory::load( 'MS_Model_Addon' ); 
  275. $enabled = ! empty( $model->active[ $addon ] ); 
  276.  
  277. if ( $enabled ) { 
  278. // Sub-addons are considered enabled only when the parent add-on is enabled also. 
  279. switch ( $addon ) { 
  280. case self::ADDON_MEMBERCAPS_ADV: 
  281. $enabled = self::is_enabled( self::ADDON_MEMBERCAPS ); 
  282. break; 
  283.  
  284. return apply_filters( 
  285. 'ms_model_addon_is_enabled_' . $addon,  
  286. $enabled 
  287. ); 
  288.  
  289. /** 
  290. * Enable an add-on type in the plugin. 
  291. * 
  292. * @since 1.0.0 
  293. * 
  294. * @var string $addon The add-on type. 
  295. */ 
  296. static public function enable( $addon ) { 
  297. $model = MS_Factory::load( 'MS_Model_Addon' ); 
  298. $model->refresh(); 
  299. $model->active[ $addon ] = true; 
  300. $model->save(); 
  301.  
  302. do_action( 'ms_model_addon_enable', $addon, $model ); 
  303.  
  304. /** 
  305. * Disable an add-on type in the plugin. 
  306. * 
  307. * @since 1.0.0 
  308. * 
  309. * @var string $addon The add-on type. 
  310. */ 
  311. static public function disable( $addon ) { 
  312. $model = MS_Factory::load( 'MS_Model_Addon' ); 
  313. $model->refresh(); 
  314. unset( $model->active[ $addon ] ); 
  315. $model->save(); 
  316.  
  317. do_action( 'ms_model_addon_disable', $addon, $model ); 
  318.  
  319. /** 
  320. * Toggle add-on type status in the plugin. 
  321. * 
  322. * @since 1.0.0 
  323. * 
  324. * @var string $addon The add-on type. 
  325. */ 
  326. static public function toggle_activation( $addon, $value = null ) { 
  327. $model = MS_Factory::load( 'MS_Model_Addon' ); 
  328. if ( null === $value ) { 
  329. $value = self::is_enabled( $addon ); 
  330.  
  331. if ( $value ) { 
  332. $model->disable( $addon ); 
  333. } else { 
  334. $model->enable( $addon ); 
  335.  
  336. do_action( 'ms_model_addon_toggle_activation', $addon, $model ); 
  337.  
  338. /** 
  339. * Enable add-on necessary to membership. 
  340. * 
  341. * @since 1.0.0 
  342. * 
  343. * @var string $addon The add-on type. 
  344. */ 
  345. public function auto_config( $membership ) { 
  346. if ( $membership->trial_period_enabled ) { 
  347. $this->enable( self::ADDON_TRIAL ); 
  348.  
  349. do_action( 'ms_model_addon_auto_config', $membership, $this ); 
  350.  
  351. /** 
  352. * Returns a list of all registered Add-Ons. 
  353. * Alias for the `get_addons()` function. 
  354. * 
  355. * @since 1.0.0 
  356. * @return array List of all registered Add-ons. 
  357. */ 
  358. public function get_addon_list() { 
  359. return self::get_addons(); 
  360.  
  361. /** 
  362. * Returns Add-On details for the core add-ons in legacy format. 
  363. * New Add-ons are stored in the /app/addon folder and use the 
  364. * ms_model_addon_register hook to provide these informations. 
  365. * 
  366. * ** This function should not be extended ** 
  367. * ** Create new Add-ons in the app/addon/ directory ** 
  368. * 
  369. * @since 1.0.0 
  370. * @return array List of Add-ons 
  371. */ 
  372. static private function get_core_list() { 
  373. $settings = MS_Factory::load( 'MS_Model_Settings' ); 
  374.  
  375. $options_text = sprintf( 
  376. '<i class="dashicons dashicons dashicons-admin-settings"></i> %s',  
  377. __( 'Options available', 'membership2' ) 
  378. ); 
  379.  
  380. $list[ self::ADDON_MULTI_MEMBERSHIPS ] = (object) array( 
  381. 'name' => __( 'Multiple Memberships', 'membership2' ),  
  382. 'description' => __( 'Your members can join more than one membership at the same time.', 'membership2' ),  
  383. 'icon' => 'dashicons dashicons-forms',  
  384. ); 
  385.  
  386. $list[ self::ADDON_TRIAL ] = (object) array( 
  387. 'name' => __( 'Trial Period', 'membership2' ),  
  388. 'description' => __( 'Allow your members to sign up for a free membership trial. Trial details can be configured separately for each membership.', 'membership2' ),  
  389. ); 
  390.  
  391. $list[ self::ADDON_POST_BY_POST ] = (object) array( 
  392. 'name' => __( 'Individual Posts', 'membership2' ),  
  393. 'description' => __( 'Protect individual Posts instead of Categories.', 'membership2' ),  
  394. ); 
  395.  
  396. $list[ self::ADDON_CPT_POST_BY_POST ] = (object) array( 
  397. 'name' => __( 'Individual Custom Posts', 'membership2' ),  
  398. 'description' => __( 'Protect individual Posts of a Custom Post Type.', 'membership2' ),  
  399. ); 
  400.  
  401. $list[ self::ADDON_MEDIA ] = (object) array( 
  402. 'name' => __( 'Media Protection', 'membership2' ),  
  403. 'description' => __( 'Protect Images and other Media-Library content.', 'membership2' ),  
  404. 'footer' => $options_text,  
  405. 'icon' => 'dashicons dashicons-admin-media',  
  406. 'class' => 'ms-options',  
  407. 'details' => array( 
  408. array( 
  409. 'id' => 'masked_url',  
  410. 'before' => esc_html( trailingslashit( get_option( 'home' ) ) ),  
  411. 'type' => MS_Helper_Html::INPUT_TYPE_TEXT,  
  412. 'title' => __( 'Mask download URL:', 'membership2' ),  
  413. 'value' => $settings->downloads['masked_url'],  
  414. 'data_ms' => array( 
  415. 'field' => 'masked_url',  
  416. 'action' => MS_Controller_Settings::AJAX_ACTION_UPDATE_SETTING,  
  417. '_wpnonce' => true, // Nonce will be generated from 'action' 
  418. ),  
  419. ),  
  420. array( 
  421. 'id' => 'protection_type',  
  422. 'type' => MS_Helper_Html::INPUT_TYPE_RADIO,  
  423. 'title' => __( 'Protection method', 'membership2' ),  
  424. 'desc' => __( 'You can change the way that Membership2 changes the default URL to your WordPress media library files.<br>This is done for increased protection by hiding the real filename and path.', 'membership2' ),  
  425. 'value' => $settings->downloads['protection_type'],  
  426. 'field_options' => MS_Rule_Media_Model::get_protection_types(),  
  427. 'data_ms' => array( 
  428. 'field' => 'protection_type',  
  429. 'action' => MS_Controller_Settings::AJAX_ACTION_UPDATE_SETTING,  
  430. '_wpnonce' => true, // Nonce will be generated from 'action' 
  431. ),  
  432. ),  
  433. array( 
  434. 'id' => 'advanced',  
  435. 'type' => MS_Helper_Html::INPUT_TYPE_RADIO_SLIDER,  
  436. 'title' => __( 'Protect Individual Media files', 'membership2' ),  
  437. 'desc' => __( 'Enable this to display a new tab in "Membership2" where you can manually modify access to each media library item.<br>Default: When this option is disabled then the parent-post controls the access to the media file.', 'membership2' ),  
  438. 'value' => self::is_enabled( MS_Addon_Mediafiles::ID ),  
  439. 'data_ms' => array( 
  440. 'action' => MS_Controller_Addon::AJAX_ACTION_TOGGLE_ADDON,  
  441. 'field' => 'active',  
  442. 'addon' => MS_Addon_Mediafiles::ID,  
  443. '_wpnonce' => true, // Nonce will be generated from 'action' 
  444. ),  
  445. ),  
  446. ),  
  447. ); 
  448.  
  449. $list[ self::ADDON_SHORTCODE ] = (object) array( 
  450. 'name' => __( 'Shortcode Protection', 'membership2' ),  
  451. 'description' => __( 'Protect Shortcode-Output via Memberships.', 'membership2' ),  
  452. 'icon' => 'dashicons dashicons-editor-code',  
  453. ); 
  454.  
  455. $list[ self::ADDON_URL_GROUPS ] = (object) array( 
  456. 'name' => __( 'URL Protection', 'membership2' ),  
  457. 'description' => __( 'URL Protection will protect pages by the URL. This rule overrides all other rules, so use it carefully.', 'membership2' ),  
  458. 'icon' => 'dashicons dashicons-admin-links',  
  459. ); 
  460.  
  461. $list[ self::ADDON_AUTO_MSGS_PLUS ] = (object) array( 
  462. 'name' => __( 'Additional Automated Messages', 'membership2' ),  
  463. 'description' => __( 'Send your members automated Email responses for various additional events.', 'membership2' ),  
  464. 'icon' => 'dashicons dashicons-email',  
  465. ); 
  466.  
  467. $list[ self::ADDON_SPECIAL_PAGES ] = (object) array( 
  468. 'name' => __( 'Protect Special Pages', 'membership2' ),  
  469. 'description' => __( 'Change protection of special pages such as the search results.', 'membership2' ),  
  470. 'icon' => 'dashicons dashicons-admin-home',  
  471. ); 
  472.  
  473. $list[ self::ADDON_ADV_MENUS ] = (object) array( 
  474. 'name' => __( 'Advanced menu protection', 'membership2' ),  
  475. 'description' => __( 'Adds a new option to the General Settings that controls how WordPress menus are protected.<br />Protect individual Menu-Items, replace the contents of WordPress Menu-Locations or replace each Menu individually.', 'membership2' ),  
  476. 'footer' => $options_text,  
  477. 'class' => 'ms-options',  
  478. 'details' => array( 
  479. array( 
  480. 'id' => 'menu_protection',  
  481. 'type' => MS_Helper_Html::INPUT_TYPE_SELECT,  
  482. 'title' => __( 'Choose how you want to protect your WordPress menus.', 'membership2' ),  
  483. 'value' => $settings->menu_protection,  
  484. 'field_options' => array( 
  485. 'item' => __( 'Protect single Menu Items (Default)', 'membership2' ),  
  486. 'menu' => __( 'Replace individual Menus', 'membership2' ),  
  487. 'location' => __( 'Overwrite contents of Menu Locations', 'membership2' ),  
  488. ),  
  489. 'data_ms' => array( 
  490. 'action' => MS_Controller_Settings::AJAX_ACTION_UPDATE_SETTING,  
  491. 'field' => 'menu_protection',  
  492. ),  
  493. ),  
  494. ),  
  495. ); 
  496.  
  497. // New since 1.1 
  498. $list[ self::ADDON_ADMINSIDE ] = (object) array( 
  499. 'name' => __( 'Admin Side Protection', 'membership2' ),  
  500. 'description' => __( 'Control the pages and even Meta boxes that members can access on the admin side.', 'membership2' ),  
  501. 'icon' => 'dashicons dashicons-admin-network',  
  502. ); 
  503.  
  504. $list[ self::ADDON_MEMBERCAPS ] = (object) array( 
  505. 'name' => __( 'Member Capabilities', 'membership2' ),  
  506. 'description' => __( 'Manage user-capabilities on membership level.', 'membership2' ),  
  507. 'footer' => $options_text,  
  508. 'class' => 'ms-options',  
  509. 'icon' => 'dashicons dashicons-admin-users',  
  510. 'details' => array( 
  511. array( 
  512. 'id' => 'ms-toggle-' . self::ADDON_MEMBERCAPS_ADV,  
  513. 'title' => __( 'Advanced Capability protection', 'membership2' ),  
  514. 'desc' => __( 'Allows you to protect individual WordPress Capabilities. When activated then the "User Roles" tab is replaced by a "Member Capabilities" tab where you can protect and assign individual WordPress Capabilities instead of roles.', 'membership2' ),  
  515. 'type' => MS_Helper_Html::INPUT_TYPE_RADIO_SLIDER,  
  516. 'value' => self::is_enabled( self::ADDON_MEMBERCAPS_ADV ),  
  517. 'class' => 'toggle-plugin',  
  518. 'ajax_data' => array( 
  519. 'action' => MS_Controller_Addon::AJAX_ACTION_TOGGLE_ADDON,  
  520. 'field' => 'active',  
  521. 'addon' => self::ADDON_MEMBERCAPS_ADV,  
  522. ),  
  523. ),  
  524. ),  
  525. ); 
  526.  
  527. return $list; 
.