class-license-manager.php 22 KB

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