MS_Model_Plugin

Main class for protection.

Defined (1)

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

/app/model/class-ms-model-plugin.php  
  1. class MS_Model_Plugin extends MS_Model { 
  2.  
  3. /** 
  4. * Current Member object. 
  5. * @since 1.0.0 
  6. * @var string $member 
  7. */ 
  8. private $member; 
  9.  
  10. /** 
  11. * Full admin menu, used by the Adminside rule. 
  12. * This property cannot be initialized in the rule-model itself because the 
  13. * rule is loaded long after the menu is rendered and therefore does not 
  14. * have access to the full list of menu items. 
  15. * @since 1.0.0 
  16. * @var array 
  17. */ 
  18. protected $admin_menu = array(); 
  19.  
  20. /** 
  21. * Prepare object. 
  22. * @since 1.0.0 
  23. */ 
  24. public function __construct() { 
  25. do_action( 'ms_model_plugin_constructor', $this ); 
  26.  
  27. // Upgrade membership database if needs to. 
  28. MS_Model_Upgrade::init(); 
  29.  
  30. /** 
  31. * Create our own copy of the full admin menu to be used in the 
  32. * Membership2 settings. 
  33. * These hooks are only executed in the admin side. 
  34. */ 
  35. $this->add_action( '_network_admin_menu', 'store_admin_menu', 1 ); 
  36. $this->add_action( '_user_admin_menu', 'store_admin_menu', 1 ); 
  37. $this->add_action( '_admin_menu', 'store_admin_menu', 1 ); 
  38.  
  39. $this->add_action( 'network_admin_menu', 'store_admin_menu', 99999 ); 
  40. $this->add_action( 'user_admin_menu', 'store_admin_menu', 99999 ); 
  41. $this->add_action( 'admin_menu', 'store_admin_menu', 99999 ); 
  42.  
  43. // Register all Add-ons and load rules BEFORE the user is initialized. 
  44. $this->add_action( 'ms_load_member', 'load_addons', 1 ); 
  45. $this->add_action( 'ms_load_member', 'load_rules', 1 ); 
  46.  
  47. // Setup the page protection AFTER the user was initialized. 
  48. $this->add_action( 'ms_init_done', 'setup_rules', 1 ); 
  49. $this->add_action( 'ms_init_done', 'setup_protection', 2 ); 
  50. $this->add_action( 'ms_init_done', 'setup_admin_protection', 3 ); 
  51.  
  52. /** 
  53. * Some plugins (such as MarketPress) can trigger the set_current_user 
  54. * action hook before this object is initialized. To ensure correct 
  55. * loading order we use the `init` hook, which is called directly after 
  56. * the correct set_current_user call. 
  57. * Most of the plugin logic requires the current user to be known,  
  58. * that's why we do a explicit check here to make sure we have a valid 
  59. * user. 
  60. */ 
  61.  
  62. // Initialize the current member 
  63. $this->run_action( 'init', 'init_member', 1 ); 
  64.  
  65. /** 
  66. * ******************************************************************* * 
  67. * Hooks below are only set up when Content Protection is enabled 
  68. * ******************************************************************* * 
  69. */ 
  70.  
  71. if ( ! MS_Plugin::is_enabled() ) { return; } 
  72.  
  73. // Setup the CRON hooks. 
  74. $this->run_action( 'init', 'setup_cron_services', 1 ); 
  75. $this->add_filter( 'cron_schedules', 'cron_time_period' ); 
  76. $this->add_filter( 'ms_run_cron_services', 'run_cron_services' ); 
  77. $this->add_action( 'ms_cron_check_membership_status', 'check_membership_status' ); 
  78.  
  79. $this->add_action( 'template_redirect', 'protect_current_page', 1 ); 
  80.  
  81. // Init gateways and communications to register actions/filters 
  82. $this->run_action( 'init', array( 'MS_Model_Gateway', 'get_gateways' ), 2 ); 
  83. $this->run_action( 'init', array( 'MS_Model_Communication', 'init' ), 2 ); 
  84.  
  85. /** 
  86. * Initialise current member. 
  87. * Get current member and membership relationships. 
  88. * If user is not logged in (visitor), assign a visitor membership. 
  89. * If user is logged in but has not any memberships, assign a default membership. 
  90. * Deactivated users (active == false) get visitor membership assigned. 
  91. * @since 1.0.0 
  92. */ 
  93. public function init_member() { 
  94. do_action( 'ms_load_member', $this ); 
  95.  
  96. $this->member = MS_Model_Member::get_current_member(); 
  97.  
  98. if ( MS_Plugin::is_enabled() ) { 
  99. if ( ! is_user_logged_in() ) { 
  100. // If a Guest-Membership exists we also assign it to the user. 
  101. $ms_guest = MS_Model_Membership::get_guest(); 
  102. if ( $ms_guest->is_valid() && $ms_guest->active ) { 
  103. $this->member->add_membership( $ms_guest->id ); 
  104. } elseif ( ! $this->member->has_membership() ) { 
  105. // Apply User-Membership to logged-in users without subscriptions. 
  106. $ms_user = MS_Model_Membership::get_user(); 
  107. if ( $ms_user->is_valid() && $ms_user->active ) { 
  108. $this->member->add_membership( $ms_user->id ); 
  109. } elseif ( ! $this->member->is_member ) { 
  110. $this->member->subscriptions = array(); 
  111.  
  112. // No subscription: Assign the base membership, which only denies access. 
  113. if ( ! $this->member->has_membership() ) { 
  114. $this->member->add_membership( 
  115. MS_Model_Membership::get_base()->id 
  116. ); 
  117.  
  118. /** 
  119. * At this point the plugin is initialized and we are here: 
  120. * - All Add-Ons are registered 
  121. * - All Rules are registered 
  122. * - Know the current User 
  123. * - All Subscriptions/Memberships of the user are loaded 
  124. * - System memberships are already assigned (guest/base) 
  125. * - Payment gateways are registered 
  126. * - Communication settings are loaded 
  127. * Next we tell everybody that we are ready to get serious! 
  128. * What happens next: 
  129. * 1. All Membership-Rules are initialized/merged 
  130. * 2. Front-End Protection is applied 
  131. * 3. Admin-Side Protection is applied 
  132. */ 
  133.  
  134. do_action( 'ms_init_done', $this ); 
  135.  
  136. /** 
  137. * Returns an array with access-information on the current page/user 
  138. * @since 1.0.0 
  139. * @return array { 
  140. * Access information 
  141. * @type bool $has_access If the current user can view the current page. 
  142. * @type array $memberships List of active membership-IDs the user has 
  143. * registered to. 
  144. * } 
  145. */ 
  146. public function get_access_info() { 
  147. static $Info = null; 
  148.  
  149. if ( null === $Info ) { 
  150. $Info = array( 
  151. 'has_access' => null,  
  152. 'is_admin' => false,  
  153. 'memberships' => array(),  
  154. 'url' => MS_Helper_Utility::get_current_url(),  
  155. ); 
  156.  
  157. // The ID of the main system membership. 
  158. $base_id = MS_Model_Membership::get_base()->id; 
  159.  
  160. $simulation = $this->member->is_simulated_user() || isset( $_GET['explain'] ) && 'access' == $_GET['explain']; 
  161. if ( $simulation ) { $Info['reason'] = array(); } 
  162.  
  163. if ( $this->member->is_normal_admin() ) { 
  164. // Admins have access to ALL memberships. 
  165. $Info['is_admin'] = true; 
  166. $Info['has_access'] = true; 
  167.  
  168. if ( $simulation ) { 
  169. $Info['reason'][] = __( 'Allow: Admin-User always has access', 'membership2' ); 
  170.  
  171. $memberships = MS_Model_Membership::get_memberships(); 
  172. foreach ( $memberships as $membership ) { 
  173. $Info['memberships'][] = $membership->id; 
  174. } else { 
  175. /** 
  176. * A non-admin visitor is only guaranteed access to special 
  177. * Membership2 pages: 
  178. * Registration, Login, etc. 
  179. */ 
  180. $ms_page = MS_Model_Pages::current_page(); 
  181. if ( $ms_page ) { 
  182. $Info['has_access'] = true; 
  183.  
  184. if ( $simulation ) { 
  185. $Info['reason'][] = __( 'Allow: This is a Membership Page', 'membership2' ); 
  186.  
  187. // Build a list of memberships the user belongs to and check permission. 
  188. foreach ( $this->member->subscriptions as $subscription ) { 
  189. // Verify status of the membership. 
  190. // Only active, trial or canceled (until it expires) status memberships. 
  191. if ( ! $this->member->has_membership( $subscription->membership_id ) ) { 
  192. if ( $simulation ) { 
  193. $Info['reason'][] = sprintf( 
  194. __( 'Skipped: Not a member of "%s"', 'membership2' ),  
  195. $subscription->get_membership()->name 
  196. ); 
  197.  
  198. continue; 
  199.  
  200. if ( $base_id !== $subscription->membership_id ) { 
  201. $Info['memberships'][] = $subscription->membership_id; 
  202.  
  203. // If permission is not clear yet then check current membership... 
  204. if ( true !== $Info['has_access'] ) { 
  205. $membership = $subscription->get_membership(); 
  206. $access = $membership->has_access_to_current_page(); 
  207.  
  208. if ( null === $access ) { 
  209. if ( $simulation ) { 
  210. $Info['reason'][] = sprintf( 
  211. __( 'Ignored: Membership "%s"', 'membership2' ),  
  212. $membership->name 
  213. ); 
  214. $Info['reason'][] = $membership->_access_reason; 
  215. continue; 
  216.  
  217. if ( $simulation ) { 
  218. $Info['reason'][] = sprintf( 
  219. __( '%s: Membership "%s"', 'membership2' ),  
  220. $access ? __( 'Allow', 'membership2' ) : __( 'Deny', 'membership2' ),  
  221. $membership->name 
  222. ); 
  223.  
  224. $Info['deciding_membership'] = $membership->id; 
  225. if ( $access ) { 
  226. $Info['deciding_rule'] = $membership->_allow_rule; 
  227. } else { 
  228. $Info['deciding_rule'] = $membership->_deny_rule; 
  229. $Info['reason'][] = $membership->_access_reason; 
  230.  
  231. $Info['has_access'] = $access; 
  232.  
  233. if ( null === $Info['has_access'] ) { 
  234. $Info['has_access'] = true; 
  235.  
  236. if ( $simulation ) { 
  237. $Info['reason'][] = __( 'Allow: Page is not protected', 'membership2' ); 
  238.  
  239. // "membership-id: 0" means: User does not belong to any membership. 
  240. if ( ! count( $Info['memberships'] ) ) { 
  241. $Info['memberships'][] = 0; 
  242.  
  243. $Info = apply_filters( 'ms_model_plugin_get_access_info', $Info ); 
  244.  
  245. if ( $simulation ) { 
  246. $access = lib3()->session->get_clear( 'ms-access' ); 
  247. lib3()->session->add( 'ms-access', $Info ); 
  248. for ( $i = 0; $i < 9; $i += 1 ) { 
  249. if ( isset( $access[$i] ) ) { 
  250. lib3()->session->add( 'ms-access', $access[$i] ); 
  251.  
  252. if ( WP_DEBUG && isset( $_GET['explain'] ) && 'access' == $_GET['explain'] ) { 
  253. echo '<style>code{background:#EEE;background:rgba(0, 0, 0, 0.1);padding:1px 4px;}</style>'; 
  254. echo '<h3>Note</h3>'; 
  255. echo '<p>To disable the URL param <code>?explain=access</code> you have to set <code>WP_DEBUG</code> to false.</p>'; 
  256. echo '<hr><h3>Recent Access checks</h3>'; 
  257.  
  258. lib3()->debug->stacktrace_off(); 
  259. foreach ( $access as $item ) { 
  260. printf( 
  261. '<a href="%1$s">%1$s</a>: <strong>%2$s</strong>',  
  262. $item['url'],  
  263. $item['has_access'] ? __( 'Allow', 'membership2' ) : __( 'Deny', 'membership2' ) 
  264. ); 
  265. // Intended debug output, leave it here. 
  266. lib3()->debug->dump( $item ); 
  267. wp_die( '' ); 
  268.  
  269. return $Info; 
  270.  
  271. /** 
  272. * Checks member permissions and protects current page. 
  273. * Related Action Hooks: 
  274. * - template_redirect 
  275. * @since 1.0.0 
  276. */ 
  277. public function protect_current_page() { 
  278. do_action( 'ms_model_plugin_protect_current_page_before', $this ); 
  279.  
  280. // Admin user has access to everything 
  281. if ( $this->member->is_normal_admin() ) { 
  282. return; 
  283.  
  284. $access = $this->get_access_info(); 
  285.  
  286. if ( ! $access['has_access'] ) { 
  287. MS_Model_Pages::create_missing_pages(); 
  288. $no_access_page_url = MS_Model_Pages::get_page_url( 
  289. MS_Model_Pages::MS_PAGE_PROTECTED_CONTENT,  
  290. false 
  291. ); 
  292. $current_page_url = MS_Helper_Utility::get_current_url(); 
  293.  
  294. // Don't (re-)redirect the protection page. 
  295. if ( ! MS_Model_Pages::is_membership_page( null, MS_Model_Pages::MS_PAGE_PROTECTED_CONTENT ) ) { 
  296. $no_access_page_url = esc_url_raw( 
  297. add_query_arg( 
  298. array( 'redirect_to' => urlencode( $current_page_url ) ),  
  299. $no_access_page_url 
  300. ); 
  301.  
  302. $no_access_page_url = apply_filters( 
  303. 'ms_model_plugin_protected_content_page',  
  304. $no_access_page_url 
  305. ); 
  306. wp_safe_redirect( $no_access_page_url ); 
  307.  
  308. exit; 
  309.  
  310. do_action( 'ms_model_plugin_protect_current_page_after', $this ); 
  311.  
  312. /** 
  313. * Load all the Add-ons. 
  314. * Related Action Hooks: 
  315. * - ms_load_member 
  316. * @since 1.0.0 
  317. */ 
  318. public function load_addons() { 
  319. do_action( 'ms_load_addons', $this ); 
  320.  
  321. // Initialize all Add-ons 
  322. MS_Model_Addon::get_addons(); 
  323.  
  324. /** 
  325. * Load all the rules that are used by the plugin. 
  326. * Related Action Hooks: 
  327. * - ms_load_member 
  328. * @since 1.0.0 
  329. */ 
  330. public function load_rules() { 
  331. do_action( 'ms_load_rules', $this ); 
  332.  
  333. $rule_types = MS_Model_Rule::get_rule_types(); 
  334. $base = MS_Model_Membership::get_base(); 
  335.  
  336. foreach ( $rule_types as $rule_type ) { 
  337. $rule = $base->get_rule( $rule_type ); 
  338.  
  339. /** 
  340. * Load all the rules that are used by the plugin. 
  341. * Related Action Hooks: 
  342. * - ms_init_done 
  343. * @since 1.0.0 
  344. */ 
  345. public function setup_rules() { 
  346. // Make sure we stick to the correct workflow. 
  347. if ( ! did_action( 'ms_init_done' ) ) { 
  348. throw new Exception( 'setup_rules() is called too early.', 1 ); 
  349. return; 
  350.  
  351. do_action( 'ms_initialize_rules', $this ); 
  352.  
  353. $rule_types = MS_Model_Rule::get_rule_types(); 
  354.  
  355. foreach ( $this->member->subscriptions as $subscription ) { 
  356. foreach ( $rule_types as $rule_type ) { 
  357. $rule = $subscription->get_membership()->get_rule( $rule_type ); 
  358.  
  359. /** 
  360. * Setup initial protection for the front-end. 
  361. * Hide menu and pages, protect media donwload and feeds. 
  362. * Protect feeds. 
  363. * Related Action Hooks: 
  364. * - ms_init_done 
  365. * @since 1.0.0 
  366. */ 
  367. public function setup_protection() { 
  368. if ( is_admin() ) { return; } 
  369.  
  370. // Make sure we stick to the correct workflow. 
  371. if ( ! did_action( 'ms_init_done' ) ) { 
  372. throw new Exception( 'setup_protection() is called too early.', 1 ); 
  373. return; 
  374.  
  375. do_action( 'ms_setup_protection', $this ); 
  376.  
  377. // Search permissions through all memberships joined. 
  378. foreach ( $this->member->subscriptions as $subscription ) { 
  379. // Verify status of the membership. 
  380. // Only active, trial or canceled (until it expires) status memberships. 
  381. if ( ! $this->member->has_membership( $subscription->membership_id ) ) { 
  382. continue; 
  383.  
  384. $membership = $subscription->get_membership(); 
  385. $membership->initialize( $subscription ); 
  386.  
  387. // Protection is not applied for Admin users 
  388. if ( ! $this->member->is_normal_admin() ) { 
  389. $membership->protect_content(); 
  390.  
  391. do_action( 'ms_setup_protection_done', $this ); 
  392.  
  393. /** 
  394. * Setup initial protection for the admin-side. 
  395. * Related Action Hooks: 
  396. * - ms_init_done 
  397. * @since 1.0.0 
  398. */ 
  399. public function setup_admin_protection() { 
  400. if ( ! is_admin() ) { return; } 
  401.  
  402. // Make sure we stick to the correct workflow. 
  403. if ( ! did_action( 'ms_init_done' ) ) { 
  404. throw new Exception( 'setup_admin_protection() is called too early.', 1 ); 
  405. return; 
  406.  
  407. do_action( 'ms_setup_admin_protection', $this ); 
  408.  
  409. // Search permissions through all memberships joined. 
  410. foreach ( $this->member->subscriptions as $subscription ) { 
  411. // Verify status of the membership. 
  412. // Only active, trial or canceled (until it expires) status memberships. 
  413. if ( ! $this->member->has_membership( $subscription->membership_id ) ) { 
  414. continue; 
  415.  
  416. $membership = $subscription->get_membership(); 
  417. $membership->initialize( $subscription ); 
  418.  
  419. // Protection is not applied for Admin users 
  420. if ( ! $this->member->is_normal_admin() ) { 
  421. $membership->protect_admin_content(); 
  422.  
  423. do_action( 'ms_setup_admin_protection_done', $this ); 
  424.  
  425. /** 
  426. * Config cron time period. This is actually not displayed anywhere but 
  427. * used only in function setup_cron_services() 
  428. * Related Action Hooks: 
  429. * - cron_schedules 
  430. * @since 1.0.0 
  431. */ 
  432. public function cron_time_period( $periods ) { 
  433. if ( ! is_array( $periods ) ) { 
  434. $periods = array(); 
  435.  
  436. $periods['6hours'] = array( 
  437. 'interval' => 6 * HOUR_IN_SECONDS,  
  438. 'display' => __( 'Every 6 Hours', 'membership2' ) 
  439. ); 
  440. $periods['30mins'] = array( 
  441. 'interval' => 30 * MINUTE_IN_SECONDS,  
  442. 'display' => __( 'Every 30 Mins', 'membership2' ) 
  443. ); 
  444.  
  445. return apply_filters( 
  446. 'ms_model_plugin_cron_time_period',  
  447. $periods 
  448. ); 
  449.  
  450. /** 
  451. * Runs a single Membership 2 cron service and then re-schedules it. 
  452. * This function is used to manually trigger the cron services. 
  453. * @since 1.0.0 
  454. */ 
  455. public function run_cron_services( $hook ) { 
  456. wp_clear_scheduled_hook( $hook ); 
  457. $this->setup_cron_services( $hook ); 
  458.  
  459. // Note that we only remove the cron job and add it again. 
  460. // As a result the job is re-scheduled with current timestamp and 
  461. // therefore it will be executed instantly. 
  462.  
  463. /** 
  464. * Setup cron plugin services. 
  465. * Setup cron to call actions. 
  466. * The action-hook is called via the WordPress Cron implementation on a 
  467. * regular basis - this hooks are set up only once. 
  468. * The Cron jobs can be manually executed by opening the admin page 
  469. * "Membership2 > Settings" and adding URL param "&run_cron=1" 
  470. * @since 1.0.0 
  471. */ 
  472. public function setup_cron_services( $reschedule = null ) { 
  473. do_action( 'ms_model_plugin_setup_cron_services_before', $this ); 
  474.  
  475. $jobs = array( 
  476. 'ms_cron_check_membership_status' => '6hours',  
  477. 'ms_cron_process_communications' => 'hourly',  
  478. ); 
  479.  
  480. foreach ( $jobs as $hook => $interval ) { 
  481. if ( ! wp_next_scheduled( $hook ) || $hook == $reschedule ) { 
  482. wp_schedule_event( time(), $interval, $hook ); 
  483.  
  484. do_action( 'ms_model_plugin_setup_cron_services_after', $this ); 
  485.  
  486. /** 
  487. * Check membership status. 
  488. * Execute actions when time/period condition are met. 
  489. * E.g. change membership status, add communication to queue, create invoices. 
  490. * @since 1.0.0 
  491. */ 
  492. public function check_membership_status() { 
  493. do_action( 'ms_model_plugin_check_membership_status_before', $this ); 
  494.  
  495. if ( $this->member->is_simulated_user() ) { 
  496. return; 
  497.  
  498. $args = apply_filters( 
  499. 'ms_model_plugin_check_membership_status_get_subscription_args',  
  500. array( 'status' => 'valid' ) 
  501. ); 
  502. $subscriptions = MS_Model_Relationship::get_subscriptions( $args ); 
  503.  
  504. foreach ( $subscriptions as $subscription ) { 
  505. $subscription->check_membership_status(); 
  506.  
  507. do_action( 'ms_model_plugin_check_membership_status_after', $this ); 
  508.  
  509. /** 
  510. * Copies the full WordPress Admin menu before any restriction is applied 
  511. * by WordPress or an Plugin. This menu-information is used on the 
  512. * Membership2/Accessible Content settings pages 
  513. * @since 1.0.0 
  514. * @global array $menu 
  515. * @global array $submenu 
  516. */ 
  517. public function store_admin_menu() { 
  518. global $menu, $submenu; 
  519.  
  520. if ( ! isset( $this->admin_menu['main'] ) ) { 
  521. $this->admin_menu = array( 
  522. 'main' => $menu,  
  523. 'sub' => $submenu,  
  524. ); 
  525. } else { 
  526. foreach ( $menu as $pos => $item ) { 
  527. $this->admin_menu['main'][$pos] = $item; 
  528. foreach ( $submenu as $parent => $item ) { 
  529. $this->admin_menu['sub'][$parent] = $item; 
  530. ksort( $this->admin_menu['main'] ); 
  531.  
  532. /** 
  533. * Returns the previously stored admin menu items. 
  534. * @since 1.0.0 
  535. * @return array 
  536. */ 
  537. public function get_admin_menu() { 
  538. return $this->admin_menu; 
  539.