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