Yoast_License_Manager

Class Yoast_License_Manager.

Defined (1)

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

/vendor/yoast/license-manager/class-license-manager.php  
  1. abstract class Yoast_License_Manager implements iYoast_License_Manager { 
  2.  
  3. /** 
  4. * @const VERSION The version number of the License_Manager class 
  5. */ 
  6. const VERSION = 1; 
  7.  
  8. /** 
  9. * @var Yoast_License The license 
  10. */ 
  11. protected $product; 
  12.  
  13. /** 
  14. * @var string 
  15. */ 
  16. private $license_constant_name = ''; 
  17.  
  18. /** 
  19. * @var boolean True if license is defined with a constant 
  20. */ 
  21. private $license_constant_is_defined = false; 
  22.  
  23. /** 
  24. * @var boolean True if remote license activation just failed 
  25. */ 
  26. private $remote_license_activation_failed = false; 
  27.  
  28. /** 
  29. * @var array Array of license related options 
  30. */ 
  31. private $options = array(); 
  32.  
  33. /** 
  34. * @var string Used to prefix ID's, option names, etc.. 
  35. */ 
  36. protected $prefix; 
  37.  
  38. /** 
  39. * @var bool Boolean indicating whether this plugin is network activated 
  40. */ 
  41. protected $is_network_activated = false; 
  42.  
  43. /** 
  44. * Constructor 
  45. * @param Yoast_Product $product 
  46. */ 
  47. public function __construct( Yoast_Product $product ) { 
  48.  
  49. // Set the license 
  50. $this->product = $product; 
  51.  
  52. // set prefix 
  53. $this->prefix = sanitize_title_with_dashes( $this->product->get_item_name() . '_', null, 'save' ); 
  54.  
  55. // maybe set license key from constant 
  56. $this->maybe_set_license_key_from_constant(); 
  57.  
  58. /** 
  59. * Setup hooks 
  60. */ 
  61. public function setup_hooks() { 
  62.  
  63. // show admin notice if license is not active 
  64. add_action( 'admin_notices', array( $this, 'display_admin_notices' ) ); 
  65.  
  66. // catch POST requests from license form 
  67. add_action( 'admin_init', array( $this, 'catch_post_request' ) ); 
  68.  
  69. // setup item type (plugin|theme) specific hooks 
  70. $this->specific_hooks(); 
  71.  
  72. // setup the auto updater 
  73. $this->setup_auto_updater(); 
  74.  
  75.  
  76. /** 
  77. * Display license specific admin notices, namely: 
  78. * - License for the product isn't activated 
  79. * - External requests are blocked through WP_HTTP_BLOCK_EXTERNAL 
  80. */ 
  81. public function display_admin_notices() { 
  82.  
  83. if ( ! current_user_can( 'manage_options' ) ) { 
  84. return; 
  85.  
  86. // show notice if license is invalid 
  87. if ( ! $this->license_is_valid() ) { 
  88. if ( $this->get_license_key() == '' ) { 
  89. $message = __( '<b>Warning!</b> You didn\'t set your %s license key yet, which means you\'re missing out on updates and support! <a href="%s">Enter your license key</a> or <a href="%s" target="_blank">get a license here</a>.' ); 
  90. } else { 
  91. $message = __( '<b>Warning!</b> Your %s license is inactive which means you\'re missing out on updates and support! <a href="%s">Activate your license</a> or <a href="%s" target="_blank">get a license here</a>.' ); 
  92. ?> 
  93. <div class="notice notice-error yoast-notice-error"> 
  94. <p><?php printf( __( $message, $this->product->get_text_domain() ), $this->product->get_item_name(), $this->product->get_license_page_url(), $this->product->get_tracking_url( 'activate-license-notice' ) ); ?></p> 
  95. </div> 
  96. <?php 
  97.  
  98. // show notice if external requests are blocked through the WP_HTTP_BLOCK_EXTERNAL constant 
  99. if ( defined( "WP_HTTP_BLOCK_EXTERNAL" ) && WP_HTTP_BLOCK_EXTERNAL === true ) { 
  100.  
  101. // check if our API endpoint is in the allowed hosts 
  102. $host = parse_url( $this->product->get_api_url(), PHP_URL_HOST ); 
  103.  
  104. if ( ! defined( "WP_ACCESSIBLE_HOSTS" ) || stristr( WP_ACCESSIBLE_HOSTS, $host ) === false ) { 
  105. ?> 
  106. <div class="notice notice-error yoast-notice-error"> 
  107. <p><?php printf( __( '<b>Warning!</b> You\'re blocking external requests which means you won\'t be able to get %s updates. Please add %s to %s.', $this->product->get_text_domain() ), $this->product->get_item_name(), '<strong>' . $host . '</strong>', '<code>WP_ACCESSIBLE_HOSTS</code>' ); ?></p> 
  108. </div> 
  109. <?php 
  110.  
  111.  
  112. /** 
  113. * Set a notice to display in the admin area 
  114. * @param string $type error|updated 
  115. * @param string $message The message to display 
  116. */ 
  117. protected function set_notice( $message, $success = true ) { 
  118. $css_class = ( $success ) ? 'notice-success yoast-notice-success' : 'notice-error yoast-notice-error'; 
  119. add_settings_error( $this->prefix . 'license', 'license-notice', $message, $css_class ); 
  120.  
  121. /** 
  122. * Remotely activate License 
  123. * @return boolean True if the license is now activated, false if not 
  124. */ 
  125. public function activate_license() { 
  126.  
  127. $result = $this->call_license_api( 'activate' ); 
  128.  
  129. if ( $result ) { 
  130.  
  131. // show success notice if license is valid 
  132. if ( $result->license === 'valid' ) { 
  133. $success = true; 
  134. $message = $this->get_successful_activation_message( $result ); 
  135. } else { 
  136. $this->remote_license_activation_failed = true; 
  137.  
  138. $success = false; 
  139. $message = $this->get_unsuccessful_activation_message( $result ); 
  140.  
  141. // Append custom HTML message to default message. 
  142. $message .= $this->get_custom_message( $result ); 
  143.  
  144. $this->set_notice( $message, $success ); 
  145.  
  146. $this->set_license_status( $result->license ); 
  147.  
  148. return $this->license_is_valid(); 
  149.  
  150. /** 
  151. * Remotely deactivate License 
  152. * @return boolean True if the license is now deactivated, false if not 
  153. */ 
  154. public function deactivate_license() { 
  155.  
  156. $result = $this->call_license_api( 'deactivate' ); 
  157.  
  158. if ( $result ) { 
  159.  
  160. // show notice if license is deactivated 
  161. if ( $result->license === 'deactivated' ) { 
  162. $success = true; 
  163. $message = sprintf( __( "Your %s license has been deactivated.", $this->product->get_text_domain() ), $this->product->get_item_name() ); 
  164. } else { 
  165. $success = false; 
  166. $message = sprintf( __( "Failed to deactivate your %s license.", $this->product->get_text_domain() ), $this->product->get_item_name() ); 
  167.  
  168. $message .= $this->get_custom_message( $result ); 
  169.  
  170. // Append custom HTML message to default message. 
  171. $this->set_notice( $message, $success ); 
  172.  
  173. $this->set_license_status( $result->license ); 
  174.  
  175. return ( $this->get_license_status() === 'deactivated' ); 
  176.  
  177. /** 
  178. * @param string $action activate|deactivate 
  179. * @return mixed 
  180. */ 
  181. protected function call_license_api( $action ) { 
  182.  
  183. // don't make a request if license key is empty 
  184. if ( $this->get_license_key() === '' ) { 
  185. return false; 
  186.  
  187. // data to send in our API request 
  188. $api_params = array( 
  189. 'edd_action' => $action . '_license',  
  190. 'license' => $this->get_license_key(),  
  191. 'item_name' => urlencode( trim( $this->product->get_item_name() ) ),  
  192. 'url' => get_option( 'home' ) 
  193. // grab the URL straight from the option to prevent filters from breaking it. 
  194. ); 
  195.  
  196. // create api request url 
  197. $url = add_query_arg( $api_params, $this->product->get_api_url() ); 
  198.  
  199. require_once dirname( __FILE__ ) . '/class-api-request.php'; 
  200. $request = new Yoast_API_Request( $url ); 
  201.  
  202. if ( $request->is_valid() !== true ) { 
  203. $this->set_notice( sprintf( __( "Request error: \"%s\" (%scommon license notices%s)", $this->product->get_text_domain() ), $request->get_error_message(), '<a href="http://kb.yoast.com/article/13-license-activation-notices">', '</a>' ), false ); 
  204.  
  205. // get response 
  206. return $request->get_response(); 
  207.  
  208.  
  209. /** 
  210. * Set the license status 
  211. * @param string $license_status 
  212. */ 
  213. public function set_license_status( $license_status ) { 
  214. $this->set_option( 'status', $license_status ); 
  215.  
  216. /** 
  217. * Get the license status 
  218. * @return string $license_status; 
  219. */ 
  220. public function get_license_status() { 
  221. $license_status = $this->get_option( 'status' ); 
  222.  
  223. return trim( $license_status ); 
  224.  
  225. /** 
  226. * Set the license key 
  227. * @param string $license_key 
  228. */ 
  229. public function set_license_key( $license_key ) { 
  230. $this->set_option( 'key', $license_key ); 
  231.  
  232. /** 
  233. * Gets the license key from constant or option 
  234. * @return string $license_key 
  235. */ 
  236. public function get_license_key() { 
  237. $license_key = $this->get_option( 'key' ); 
  238.  
  239. return trim( $license_key ); 
  240.  
  241. /** 
  242. * Gets the license expiry date 
  243. * @return string 
  244. */ 
  245. public function get_license_expiry_date() { 
  246. return $this->get_option( 'expiry_date' ); 
  247.  
  248. /** 
  249. * Stores the license expiry date 
  250. */ 
  251. public function set_license_expiry_date( $expiry_date ) { 
  252. $this->set_option( 'expiry_date', $expiry_date ); 
  253.  
  254. /** 
  255. * Checks whether the license status is active 
  256. * @return boolean True if license is active 
  257. */ 
  258. public function license_is_valid() { 
  259. return ( $this->get_license_status() === 'valid' ); 
  260.  
  261. /** 
  262. * Get all license related options 
  263. * @return array Array of license options 
  264. */ 
  265. protected function get_options() { 
  266.  
  267. // create option name 
  268. $option_name = $this->prefix . 'license'; 
  269.  
  270. // get array of options from db 
  271. if ( $this->is_network_activated ) { 
  272. $options = get_site_option( $option_name, array() ); 
  273. } else { 
  274. $options = get_option( $option_name, array() ); 
  275.  
  276. // setup array of defaults 
  277. $defaults = array( 
  278. 'key' => '',  
  279. 'status' => '',  
  280. 'expiry_date' => '' 
  281. ); 
  282.  
  283. // merge options with defaults 
  284. $this->options = wp_parse_args( $options, $defaults ); 
  285.  
  286. return $this->options; 
  287.  
  288. /** 
  289. * Set license related options 
  290. * @param array $options Array of new license options 
  291. */ 
  292. protected function set_options( array $options ) { 
  293. // create option name 
  294. $option_name = $this->prefix . 'license'; 
  295.  
  296. // update db 
  297. if ( $this->is_network_activated ) { 
  298. update_site_option( $option_name, $options ); 
  299. } else { 
  300. update_option( $option_name, $options ); 
  301.  
  302.  
  303. /** 
  304. * Gets a license related option 
  305. * @param string $name The option name 
  306. * @return mixed The option value 
  307. */ 
  308. protected function get_option( $name ) { 
  309. $options = $this->get_options(); 
  310.  
  311. return $options[ $name ]; 
  312.  
  313. /** 
  314. * Set a license related option 
  315. * @param string $name The option name 
  316. * @param mixed $value The option value 
  317. */ 
  318. protected function set_option( $name, $value ) { 
  319. // get options 
  320. $options = $this->get_options(); 
  321.  
  322. // update option 
  323. $options[ $name ] = $value; 
  324.  
  325. // save options 
  326. $this->set_options( $options ); 
  327.  
  328. public function show_license_form_heading() { 
  329. ?> 
  330. <h3> 
  331. <?php printf( __( "%s: License Settings", $this->product->get_text_domain() ), $this->product->get_item_name() ); ?> 
  332.     
  333. </h3> 
  334. <?php 
  335.  
  336. /** 
  337. * Show a form where users can enter their license key 
  338. * @param boolean $embedded Boolean indicating whether this form is embedded in another form? 
  339. */ 
  340. public function show_license_form( $embedded = true ) { 
  341. $key_name = $this->prefix . 'license_key'; 
  342. $nonce_name = $this->prefix . 'license_nonce'; 
  343. $action_name = $this->prefix . 'license_action'; 
  344.  
  345. $api_host_available = $this->get_api_availability(); 
  346.  
  347. $visible_license_key = $this->get_license_key(); 
  348.  
  349. // obfuscate license key 
  350. $obfuscate = ( strlen( $this->get_license_key() ) > 5 && ( $this->license_is_valid() || ! $this->remote_license_activation_failed ) ); 
  351.  
  352. if ( $obfuscate ) { 
  353. $visible_license_key = str_repeat( '*', strlen( $this->get_license_key() ) - 4 ) . substr( $this->get_license_key(), - 4 ); 
  354.  
  355. // make license key readonly when license key is valid or license is defined with a constant 
  356. $readonly = ( $this->license_is_valid() || $this->license_constant_is_defined ); 
  357.  
  358. require dirname( __FILE__ ) . '/views/form.php'; 
  359.  
  360. // enqueue script in the footer 
  361. add_action( 'admin_footer', array( $this, 'output_script' ), 99 ); 
  362.  
  363. /** 
  364. * Check if the license form has been submitted 
  365. */ 
  366. public function catch_post_request() { 
  367.  
  368. $name = $this->prefix . 'license_key'; 
  369.  
  370. // check if license key was posted and not empty 
  371. if ( ! isset( $_POST[ $name ] ) ) { 
  372. return; 
  373.  
  374. // run a quick security check 
  375. $nonce_name = $this->prefix . 'license_nonce'; 
  376.  
  377. if ( ! check_admin_referer( $nonce_name, $nonce_name ) ) { 
  378. return; 
  379.  
  380. // @TODO: check for user cap? 
  381.  
  382. // get key from posted value 
  383. $license_key = $_POST[ $name ]; 
  384.  
  385. // check if license key doesn't accidentally contain asterisks 
  386. if ( strstr( $license_key, '*' ) === false ) { 
  387.  
  388. // sanitize key 
  389. $license_key = trim( sanitize_key( $_POST[ $name ] ) ); 
  390.  
  391. // save license key 
  392. $this->set_license_key( $license_key ); 
  393.  
  394. // does user have an activated valid license 
  395. if ( ! $this->license_is_valid() ) { 
  396.  
  397. // try to auto-activate license 
  398. return $this->activate_license(); 
  399.  
  400.  
  401. $action_name = $this->prefix . 'license_action'; 
  402.  
  403. // was one of the action buttons clicked? 
  404. if ( isset( $_POST[ $action_name ] ) ) { 
  405.  
  406. $action = trim( $_POST[ $action_name ] ); 
  407.  
  408. switch ( $action ) { 
  409. case 'activate': 
  410. return $this->activate_license(); 
  411.  
  412. case 'deactivate': 
  413. return $this->deactivate_license(); 
  414.  
  415.  
  416.  
  417. /** 
  418. * Output the script containing the YoastLicenseManager JS Object 
  419. * This takes care of disabling the 'activate' and 'deactivate' buttons 
  420. */ 
  421. public function output_script() { 
  422. require_once dirname( __FILE__ ) . '/views/script.php'; 
  423.  
  424. /** 
  425. * Set the constant used to define the license 
  426. * @param string $license_constant_name The license constant name 
  427. */ 
  428. public function set_license_constant_name( $license_constant_name ) { 
  429. $this->license_constant_name = trim( $license_constant_name ); 
  430. $this->maybe_set_license_key_from_constant(); 
  431.  
  432. /** 
  433. * Get the API availability information 
  434. * @return array 
  435. */ 
  436. protected function get_api_availability() { 
  437. return array( 
  438. 'url' => $this->product->get_api_url(),  
  439. 'availability' => $this->check_api_host_availability(),  
  440. 'curl_version' => $this->get_curl_version(),  
  441. ); 
  442.  
  443. /** 
  444. * Check if the API host address is available from this server 
  445. * @return bool 
  446. */ 
  447. private function check_api_host_availability() { 
  448. $wp_http = new WP_Http(); 
  449. if ( $wp_http->block_request( $this->product->get_api_url() ) === false ) { 
  450. return true; 
  451.  
  452. return false; 
  453.  
  454. /** 
  455. * Get the current curl version, or false 
  456. * @return mixed 
  457. */ 
  458. protected function get_curl_version() { 
  459. if ( function_exists( 'curl_version' ) ) { 
  460. $curl_version = curl_version(); 
  461.  
  462. if ( isset( $curl_version['version'] ) ) { 
  463. return $curl_version['version']; 
  464.  
  465. return false; 
  466.  
  467. /** 
  468. * Maybe set license key from a defined constant 
  469. */ 
  470. private function maybe_set_license_key_from_constant() { 
  471.  
  472. if ( empty( $this->license_constant_name ) ) { 
  473. // generate license constant name 
  474. $this->set_license_constant_name( strtoupper( str_replace( array( 
  475. ' ',  
  476. '-' 
  477. ), '', sanitize_key( $this->product->get_item_name() ) ) ) . '_LICENSE' ); 
  478.  
  479. // set license key from constant 
  480. if ( defined( $this->license_constant_name ) ) { 
  481.  
  482. $license_constant_value = constant( $this->license_constant_name ); 
  483.  
  484. // update license key value with value of constant 
  485. if ( $this->get_license_key() !== $license_constant_value ) { 
  486. $this->set_license_key( $license_constant_value ); 
  487.  
  488. $this->license_constant_is_defined = true; 
  489.  
  490. /** 
  491. * Determine what message should be shown for a successful license activation 
  492. * @param Object $result Result of a request. 
  493. * @return string 
  494. */ 
  495. protected function get_successful_activation_message( $result ) { 
  496. // Get expiry date. 
  497. if ( isset( $result->expires ) ) { 
  498. $this->set_license_expiry_date( $result->expires ); 
  499. $expiry_date = strtotime( $result->expires ); 
  500. } else { 
  501. $expiry_date = false; 
  502.  
  503. // Always show that it was successful. 
  504. $message = sprintf( __( "Your %s license has been activated. ", $this->product->get_text_domain() ), $this->product->get_item_name() ); 
  505.  
  506. // Show a custom notice it is an unlimited license. 
  507. if ( $result->license_limit == 0 ) { 
  508. $message .= __( "You have an unlimited license. ", $this->product->get_text_domain() ); 
  509. } else { 
  510. $message .= sprintf( _n( "You have used %d/%d activation. ", "You have used %d/%d activations. ", $result->license_limit, $this->product->get_text_domain() ), $result->site_count, $result->license_limit ); 
  511.  
  512. // add upgrade notice if user has less than 3 activations left 
  513. if ( $result->license_limit > 0 && ( $result->license_limit - $result->site_count ) <= 3 ) { 
  514. $message .= sprintf( __( '<a href="%s">Did you know you can upgrade your license?</a> ', $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-nearing-limit-notice' ) ); 
  515.  
  516. if ( $expiry_date !== false && $expiry_date < strtotime( "+1 month" ) ) { 
  517. // Add extend notice if license is expiring in less than 1 month. 
  518. $days_left = round( ( $expiry_date - time() ) / 86400 ); 
  519. $message .= sprintf( _n( '<a href="%s">Your license is expiring in %d day, would you like to extend it?</a> ', '<a href="%s">Your license is expiring in %d days, would you like to extend it?</a> ', $days_left, $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-expiring-notice' ), $days_left ); 
  520.  
  521. return $message; 
  522.  
  523. /** 
  524. * Determine what message should be shown for an unsuccessful activation 
  525. * @param Object $result Result of a request. 
  526. * @return string 
  527. */ 
  528. protected function get_unsuccessful_activation_message( $result ) { 
  529. // Default message if we cannot detect anything more specific. 
  530. $message = __( 'Failed to activate your license, your license key seems to be invalid.', $this->product->get_text_domain() ); 
  531.  
  532. if ( ! empty( $result->error ) ) { 
  533. switch ( $result->error ) { 
  534. // Show notice if user is at their activation limit. 
  535. case 'no_activations_left': 
  536. $message = sprintf( __( 'You\'ve reached your activation limit. You must <a href="%s">upgrade your license</a> to use it on this site.', $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-at-limit-notice' ) ); 
  537. break; 
  538.  
  539. // Show notice if the license is expired. 
  540. case 'expired': 
  541. $message = sprintf( __( 'Your license has expired. You must <a href="%s">extend your license</a> in order to use it again.', $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-expired-notice' ) ); 
  542. break; 
  543.  
  544. return $message; 
  545.  
  546. /** 
  547. * Get the locale for the current user 
  548. * @return string 
  549. */ 
  550. protected function get_user_locale() { 
  551. if ( function_exists( 'get_user_locale' ) ) { 
  552. return get_user_locale(); 
  553.  
  554. return get_locale(); 
  555.  
  556. /** 
  557. * Parse custom HTML message from response 
  558. * @param Object $result Result of the request. 
  559. * @return string 
  560. */ 
  561. protected function get_custom_message( $result ) { 
  562. $message = ''; 
  563.  
  564. // Allow for translated messages to be used. 
  565. $localizedDescription = 'custom_message_' . $this->get_user_locale(); 
  566. if ( ! empty( $result->{$localizedDescription} ) ) { 
  567. $message = $result->{$localizedDescription}; 
  568.  
  569. // Fall back to non-localized custom message if no locale has been provided. 
  570. if ( empty( $message ) && ! empty( $result->custom_message ) ) { 
  571. $message = $result->custom_message; 
  572.  
  573. // Make sure we limit the type of HTML elements to be displayed. 
  574. if ( ! empty( $message ) ) { 
  575. $message = wp_kses( $message, array( 
  576. 'a' => array( 
  577. 'href' => array(),  
  578. 'target' => array(),  
  579. 'title' => array() 
  580. ),  
  581. 'br' => array(),  
  582. ) ); 
  583.  
  584. // Make sure we are on a new line. 
  585. $message = '<br />' . $message; 
  586.  
  587. return $message;