PluginProbe
WPComplete / 2.3
WPComplete v2.3
2.9.5.7 2.9.5.6 trunk 1.0 1.1 1.2 1.4 1.4.6 2.3 2.9.1 2.9.2 2.9.5 2.9.5.1 2.9.5.3 2.9.5.4 2.9.5.5
wpcomplete / admin / class-wpcomplete-admin.php

class-wpcomplete-admin.php in WPComplete 2.3, at admin/class-wpcomplete-admin.php

1,799 lines 62.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The admin-specific functionality of the plugin.
5 *
6 * @link https://wpcomplete.co
7 * @since 1.0.0
8 * @last 2.0.0
9 *
10 * @package WPComplete
11 * @subpackage wpcomplete/admin
12 */
13
14 /**
15 * The admin-specific functionality of the plugin.
16 *
17 * Defines the plugin name, version, and two examples hooks for how to
18 * enqueue the admin-specific stylesheet and JavaScript.
19 *
20 * @package WPComplete
21 * @subpackage wpcomplete/admin
22 * @author Zack Gilbert <zack@zackgilbert.com>
23 */
24 class WPComplete_Admin extends WPComplete_Common {
25
26 /**
27 * The ID of this plugin.
28 *
29 * @since 1.0.0
30 * @access protected
31 * @var string $plugin_name The ID of this plugin.
32 */
33 protected $plugin_name;
34
35 /**
36 * The version of this plugin.
37 *
38 * @since 1.0.0
39 * @access protected
40 * @var string $version The current version of this plugin.
41 */
42 protected $version;
43
44 /**
45 * Register the stylesheets for the admin area.
46 *
47 * @since 1.0.0
48 */
49 public function enqueue_styles() {
50
51 /**
52 * This function is provided for demonstration purposes only.
53 *
54 * An instance of this class should be passed to the run() function
55 * defined in Plugin_Name_Loader as all of the hooks are defined
56 * in that particular class.
57 *
58 * The WPComplete_Loader will then create the relationship
59 * between the defined hooks and the functions defined in this
60 * class.
61 */
62
63 wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wpcomplete-admin.css', array('wp-color-picker'), $this->version, 'all' );
64
65 }
66
67 /**
68 * Register the JavaScript for the admin area.
69 *
70 * @since 1.0.0
71 */
72 public function enqueue_scripts() {
73
74 /**
75 * This function is provided for demonstration purposes only.
76 *
77 * An instance of this class should be passed to the run() function
78 * defined in Plugin_Name_Loader as all of the hooks are defined
79 * in that particular class.
80 *
81 * The WPComplete_Loader will then create the relationship
82 * between the defined hooks and the functions defined in this
83 * class.
84 */
85 wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wpcomplete-admin.js', array( 'jquery', 'jquery-ui-autocomplete', 'wp-color-picker', 'inline-edit-post' ), $this->version, true );
86
87 wp_localize_script( $this->plugin_name, 'WPComplete', array( 'url' => admin_url( 'admin-ajax.php' ) ) );
88 }
89
90 /**
91 * Add WPComplete specific dashboard widget
92 *
93 * @since 2.0.0
94 * @last 2.0.0
95 */
96 public function add_dashboard_widget() {
97 if ( get_option( $this->plugin_name . '_show_widget', 'true' ) === 'true' ) {
98 wp_add_dashboard_widget( $this->plugin_name . '-course-statistics', 'WPComplete Course Statistics', array( $this, 'add_dashboard_widget_cb' ) );
99 }
100 }
101
102 /**
103 * Callback for WPComplete dashboard widget. Adds actual content.
104 *
105 * @since 2.0.0
106 * @last 2.0.4
107 */
108 public function add_dashboard_widget_cb() {
109 $posts = $this->get_completable_posts();
110 if ( count( $posts ) <= 0 ) {
111 include_once 'partials/wpcomplete-admin-widget-empty.php';
112 return;
113 }
114
115 $courses = array();
116 foreach ($this->get_course_names($posts) as $course) {
117 // buttons count, number of users that started, number of users that finished
118 $courses[$course] = array('buttons' => count($this->get_course_buttons($course, $posts)), 'started' => 0, 'finished' => 0);
119 }
120 $courses[''] = array('buttons' => count($this->get_course_buttons('', $posts)), 'started' => 0, 'finished' => 0);
121
122 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
123 // Get all users that are able to complete the post:
124 $args = array('fields' => 'id');
125 if ($selected_role != 'all') $args['role'] = $selected_role;
126 $total_users = get_users($args);
127
128 foreach ($total_users as $user_id) {
129 foreach ($courses as $course => $course_info) {
130 if ( $this->user_has_started_course( $user_id, $course, $posts ) ) {
131 $courses[$course]['started']++;
132 }
133 if ( $this->user_has_completed_course( $user_id, $course, $posts ) ) {
134 $courses[$course]['finished']++;
135 }
136 }
137 }
138
139 if ( $courses['']['buttons'] > 0 ) {
140 $courses[get_bloginfo( 'name' )] = $courses[''];
141 }
142 unset($courses['']);
143
144 include_once 'partials/wpcomplete-admin-widget.php';
145 }
146
147 /**
148 * Add WPComplete specific page under the Settings submenu.
149 *
150 * @since 1.0.0
151 */
152 public function add_options_page() {
153
154 $this->plugin_screen_hook_suffix = add_options_page(
155 __( 'WPComplete Settings', $this->plugin_name ),
156 __( 'WPComplete', $this->plugin_name ),
157 'manage_options',
158 $this->plugin_name,
159 array( $this, 'display_settings_page' )
160 );
161
162 }
163
164 /**
165 * Render the WPComplete specific settings page for plugin.
166 *
167 * @since 1.0.0
168 */
169 public function display_settings_page() {
170 include_once 'partials/wpcomplete-admin-display.php';
171 }
172
173 /**
174 * Build all the settings for plugin on the WPComplete settings page.
175 *
176 * @since 1.0.0
177 */
178 public function register_settings() {
179 // PREMIUM:
180 register_setting( $this->plugin_name, $this->plugin_name . '_license_key', array( $this, 'sanitize_license' ) );
181
182 // Section related to students:
183 add_settings_section(
184 $this->plugin_name . '_students',
185 __( 'General Settings', $this->plugin_name ),
186 array( $this, 'settings_section_cb' ),
187 $this->plugin_name
188 );
189
190 add_settings_field(
191 $this->plugin_name . '_role',
192 __( 'Student Role Type', $this->plugin_name ),
193 array( $this, 'settings_role_cb' ),
194 $this->plugin_name,
195 $this->plugin_name . '_students',
196 array( 'label_for' => $this->plugin_name . '_role' )
197 );
198 if (WPCOMPLETE_IS_ACTIVATED)
199 register_setting( $this->plugin_name, $this->plugin_name . '_role', 'sanitize_text_field' );
200
201 add_settings_field(
202 $this->plugin_name . '_post_type',
203 __( 'Lesson Content Type', $this->plugin_name ),
204 array( $this, 'settings_post_type_cb' ),
205 $this->plugin_name,
206 $this->plugin_name . '_students',
207 array( 'label_for' => $this->plugin_name . '_post_type' )
208 );
209 if (WPCOMPLETE_IS_ACTIVATED)
210 register_setting( $this->plugin_name, $this->plugin_name . '_post_type', 'sanitize_text_field' );
211
212 add_settings_field(
213 $this->plugin_name . '_auto_append',
214 '',
215 array( $this, 'settings_auto_append_cb' ),
216 $this->plugin_name,
217 $this->plugin_name . '_students',
218 array()
219 );
220 register_setting( $this->plugin_name, $this->plugin_name . '_auto_append', 'sanitize_text_field' );
221
222 // Section related to the Mark as Complete button:
223 add_settings_section(
224 $this->plugin_name . '_incomplete_button',
225 __( 'Mark Complete Button', $this->plugin_name ),
226 array( $this, 'settings_section_cb' ),
227 $this->plugin_name
228 );
229
230 add_settings_field(
231 $this->plugin_name . '_incomplete_text',
232 __( 'Button Text', $this->plugin_name ),
233 array( $this, 'settings_incomplete_text_cb' ),
234 $this->plugin_name,
235 $this->plugin_name . '_incomplete_button',
236 array( 'label_for' => $this->plugin_name . '_incomplete_text' )
237 );
238 register_setting( $this->plugin_name, $this->plugin_name . '_incomplete_text', 'sanitize_text_field' );
239
240 add_settings_field(
241 $this->plugin_name . '_incomplete_active_text',
242 __( 'Saving Text', $this->plugin_name ),
243 array( $this, 'settings_incomplete_active_text_cb' ),
244 $this->plugin_name,
245 $this->plugin_name . '_incomplete_button',
246 array( 'label_for' => $this->plugin_name . '_incomplete_active_text' )
247 );
248 if (WPCOMPLETE_IS_ACTIVATED)
249 register_setting( $this->plugin_name, $this->plugin_name . '_incomplete_active_text', 'sanitize_text_field' );
250
251 add_settings_field(
252 $this->plugin_name . '_incomplete_background',
253 __( 'Button Color', $this->plugin_name ),
254 array( $this, 'settings_incomplete_background_cb' ),
255 $this->plugin_name,
256 $this->plugin_name . '_incomplete_button',
257 array( 'label_for' => $this->plugin_name . '_incomplete_background' )
258 );
259 if (WPCOMPLETE_IS_ACTIVATED)
260 register_setting( $this->plugin_name, $this->plugin_name . '_incomplete_background', 'sanitize_text_field' );
261
262 add_settings_field(
263 $this->plugin_name . '_incomplete_color',
264 __( 'Button Text Color', $this->plugin_name ),
265 array( $this, 'settings_incomplete_color_cb' ),
266 $this->plugin_name,
267 $this->plugin_name . '_incomplete_button',
268 array( 'label_for' => $this->plugin_name . '_incomplete_color' )
269 );
270 if (WPCOMPLETE_IS_ACTIVATED)
271 register_setting( $this->plugin_name, $this->plugin_name . '_incomplete_color', 'sanitize_text_field' );
272
273 // Section related to the Completed! button:
274 add_settings_section(
275 $this->plugin_name . '_completed_button',
276 __( 'Completed Button', $this->plugin_name ),
277 array( $this, 'settings_section_cb' ),
278 $this->plugin_name
279 );
280
281 add_settings_field(
282 $this->plugin_name . '_completed_text',
283 __( 'Button Text', $this->plugin_name ),
284 array( $this, 'settings_completed_text_cb' ),
285 $this->plugin_name,
286 $this->plugin_name . '_completed_button',
287 array( 'label_for' => $this->plugin_name . '_completed_text' )
288 );
289 register_setting( $this->plugin_name, $this->plugin_name . '_completed_text', 'sanitize_text_field' );
290
291 add_settings_field(
292 $this->plugin_name . '_completed_active_text',
293 __( 'Saving Text', $this->plugin_name ),
294 array( $this, 'settings_completed_active_text_cb' ),
295 $this->plugin_name,
296 $this->plugin_name . '_completed_button',
297 array( 'label_for' => $this->plugin_name . '_completed_active_text' )
298 );
299 if (WPCOMPLETE_IS_ACTIVATED)
300 register_setting( $this->plugin_name, $this->plugin_name . '_completed_active_text', 'sanitize_text_field' );
301
302 add_settings_field(
303 $this->plugin_name . '_completed_background',
304 __( 'Button Color', $this->plugin_name ),
305 array( $this, 'settings_completed_background_cb' ),
306 $this->plugin_name,
307 $this->plugin_name . '_completed_button',
308 array( 'label_for' => $this->plugin_name . '_completed_background' )
309 );
310 if (WPCOMPLETE_IS_ACTIVATED)
311 register_setting( $this->plugin_name, $this->plugin_name . '_completed_background', 'sanitize_text_field' );
312
313 add_settings_field(
314 $this->plugin_name . '_completed_color',
315 __( 'Button Text Color', $this->plugin_name ),
316 array( $this, 'settings_completed_color_cb' ),
317 $this->plugin_name,
318 $this->plugin_name . '_completed_button',
319 array( 'label_for' => $this->plugin_name . '_completed_color' )
320 );
321 if (WPCOMPLETE_IS_ACTIVATED)
322 register_setting( $this->plugin_name, $this->plugin_name . '_completed_color', 'sanitize_text_field' );
323
324 // PREMIUM: Section related to the graphs:
325 add_settings_section(
326 $this->plugin_name . '_graphs',
327 __( 'Graph Settings', $this->plugin_name ),
328 array( $this, 'settings_section_cb' ),
329 $this->plugin_name
330 );
331
332 add_settings_field(
333 $this->plugin_name . '_graph_primary',
334 __( 'Primary Color', $this->plugin_name ),
335 array( $this, 'settings_graph_primary_cb' ),
336 $this->plugin_name,
337 $this->plugin_name . '_graphs',
338 array( 'label_for' => $this->plugin_name . '_graph_primary' )
339 );
340 if (WPCOMPLETE_IS_ACTIVATED)
341 register_setting( $this->plugin_name, $this->plugin_name . '_graph_primary', 'sanitize_text_field' );
342
343 add_settings_field(
344 $this->plugin_name . '_graph_secondary',
345 __( 'Secondary Color', $this->plugin_name ),
346 array( $this, 'settings_graph_secondary_cb' ),
347 $this->plugin_name,
348 $this->plugin_name . '_graphs',
349 array( 'label_for' => $this->plugin_name . '_graph_secondary' )
350 );
351 if (WPCOMPLETE_IS_ACTIVATED)
352 register_setting( $this->plugin_name, $this->plugin_name . '_graph_secondary', 'sanitize_text_field' );
353
354 // PREMIUM: Section related to advanced features:
355 add_settings_section(
356 $this->plugin_name . '_advanced',
357 __( 'Advanced Settings', $this->plugin_name ),
358 array( $this, 'settings_section_cb' ),
359 $this->plugin_name
360 );
361
362 add_settings_field(
363 $this->plugin_name . '_custom_styles',
364 __( 'Custom Styles (CSS)', $this->plugin_name ),
365 array( $this, 'settings_custom_styles_cb' ),
366 $this->plugin_name,
367 $this->plugin_name . '_advanced',
368 array( 'label_for' => $this->plugin_name . '_custom_styles' )
369 );
370 if (WPCOMPLETE_IS_ACTIVATED)
371 register_setting( $this->plugin_name, $this->plugin_name . '_custom_styles', 'sanitize_text_field' );
372
373 add_settings_field(
374 $this->plugin_name . '_show_widget',
375 '',
376 array( $this, 'settings_show_widget_cb' ),
377 $this->plugin_name,
378 $this->plugin_name . '_advanced',
379 array()
380 );
381 if (WPCOMPLETE_IS_ACTIVATED)
382 register_setting( $this->plugin_name, $this->plugin_name . '_show_widget', 'sanitize_text_field' );
383
384 }
385
386 /**
387 * Render extra text for sections.
388 *
389 * @since 1.0.0
390 */
391 public function settings_section_cb() {
392 }
393
394 /**
395 * Sanitation helper for license field.
396 *
397 * @since 1.0.0
398 */
399 public function sanitize_license( $new ) {
400 $old = get_option( $this->plugin_name . '_license_key' );
401 if ( $old && $old != $new ) {
402 delete_option( $this->plugin_name . '_license_status' ); // new license has been entered, so must reactivate
403 wp_cache_delete( $this->plugin_name . '_license_status' );
404 }
405 return $new;
406 }
407
408 /**
409 * Render select menu for assigning which type of user roles should be tracked as students.
410 *
411 * @since 1.0.0
412 */
413 public function settings_role_cb() {
414 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
415 $disabled = !WPCOMPLETE_IS_ACTIVATED;
416
417 include 'partials/wpcomplete-admin-settings-role.php';
418 }
419
420 /**
421 * Render select menu for assigning which type of user roles should be tracked as students.
422 *
423 * @since 1.0.3
424 */
425 public function settings_post_type_cb() {
426 $selected_type = get_option( $this->plugin_name . '_post_type', 'page_post' );
427 $disabled = !WPCOMPLETE_IS_ACTIVATED;
428
429 include 'partials/wpcomplete-admin-settings-post-type.php';
430 }
431
432 /**
433 * Render checkbox for if should attempt to append [complete_button] shortcode if not found
434 *
435 * @since 1.3.0
436 */
437 public function settings_auto_append_cb() {
438 $is_enabled = get_option( $this->plugin_name . '_auto_append', 'true' );
439 $disabled = false;
440 include 'partials/wpcomplete-admin-settings-auto-append.php';
441 }
442
443 /**
444 * Render the Mark as Complete button text setting field.
445 *
446 * @since 1.0.0
447 */
448 public function settings_incomplete_text_cb() {
449 $name = $this->plugin_name . '_incomplete_text';
450 $text = get_option( $name, 'Mark as complete' );
451 $class = '';
452 $disabled = false;
453
454 include 'partials/wpcomplete-admin-settings-input.php';
455 }
456
457 /**
458 * Render the Mark as Complete button active text setting field.
459 *
460 * @since 1.4.7
461 */
462 public function settings_incomplete_active_text_cb() {
463 $name = $this->plugin_name . '_incomplete_active_text';
464 $text = get_option( $name, 'Saving...' );
465 $class = '';
466 $disabled = false;
467
468 include 'partials/wpcomplete-admin-settings-input.php';
469 }
470
471 /**
472 * Render the Mark as Complete button background color setting field.
473 *
474 * @since 1.0.0
475 */
476 public function settings_incomplete_background_cb() {
477 $name = $this->plugin_name . '_incomplete_background';
478 $text = get_option( $name, '#ff0000' );
479 $class = 'color-picker';
480 $disabled = !WPCOMPLETE_IS_ACTIVATED;
481
482 include 'partials/wpcomplete-admin-settings-input.php';
483 }
484
485 /**
486 * Render the Mark as Complete button text color setting field.
487 *
488 * @since 1.0.0
489 */
490 public function settings_incomplete_color_cb() {
491 $name = $this->plugin_name . '_incomplete_color';
492 $text = get_option( $name, '#ffffff' );
493 $class = 'color-picker';
494 $disabled = !WPCOMPLETE_IS_ACTIVATED;
495
496 include 'partials/wpcomplete-admin-settings-input.php';
497 }
498
499 /**
500 * Render the Completed! button text setting field.
501 *
502 * @since 1.0.0
503 */
504 public function settings_completed_text_cb() {
505 $name = $this->plugin_name . '_completed_text';
506 $text = get_option( $name, 'COMPLETED!' );
507 $class = '';
508 $disabled = false;
509
510 include 'partials/wpcomplete-admin-settings-input.php';
511 }
512
513 /**
514 * Render the Completed! button active text setting field.
515 *
516 * @since 1.4.7
517 */
518 public function settings_completed_active_text_cb() {
519 $name = $this->plugin_name . '_completed_active_text';
520 $text = get_option( $name, 'Saving...' );
521 $class = '';
522 $disabled = false;
523
524 include 'partials/wpcomplete-admin-settings-input.php';
525 }
526
527 /**
528 * Render the Completed! button background color setting field.
529 *
530 * @since 1.0.0
531 */
532 public function settings_completed_background_cb() {
533 $name = $this->plugin_name . '_completed_background';
534 $text = get_option( $name, '#666666' );
535 $class = 'color-picker';
536 $disabled = !WPCOMPLETE_IS_ACTIVATED;
537
538 include 'partials/wpcomplete-admin-settings-input.php';
539 }
540
541 /**
542 * Render the Completed! button text color setting field.
543 *
544 * @since 1.0.0
545 */
546 public function settings_completed_color_cb() {
547 $name = $this->plugin_name . '_completed_color';
548 $text = get_option( $name, '#ffffff' );
549 $class = 'color-picker';
550 $disabled = !WPCOMPLETE_IS_ACTIVATED;
551
552 include 'partials/wpcomplete-admin-settings-input.php';
553 }
554
555 /**
556 * PREMIUM:
557 * Render graph primary color setting field.
558 *
559 * @since 1.0.0
560 */
561 public function settings_graph_primary_cb() {
562 $name = $this->plugin_name . '_graph_primary';
563 $text = get_option( $name, '#97a71d' );
564 $class = 'color-picker';
565 $disabled = !WPCOMPLETE_IS_ACTIVATED;
566
567 include 'partials/wpcomplete-admin-settings-input.php';
568 }
569
570 /**
571 * PREMIUM:
572 * Render graph secondary color setting field.
573 *
574 * @since 1.0.0
575 */
576 public function settings_graph_secondary_cb() {
577 $name = $this->plugin_name . '_graph_secondary';
578 $text = get_option( $name, '#ebebeb' );
579 $class = 'color-picker';
580 $disabled = !WPCOMPLETE_IS_ACTIVATED;
581
582 include 'partials/wpcomplete-admin-settings-input.php';
583 }
584
585 /**
586 * PREMIUM:
587 * Render textarea for custom styles.
588 *
589 * @since 1.2.0
590 */
591 public function settings_custom_styles_cb() {
592 $name = $this->plugin_name . '_custom_styles';
593 $default = '
594 li .wpc-lesson {} li .wpc-lesson-complete {} li .wpc-lesson-completed { opacity: .5; } li .wpc-lesson-completed:after { content: "✔"; margin-left: 5px; }
595 ';
596 $text = get_option( $name, $default );
597 if ( empty( $text ) ) {
598 $text = '
599 .wpc-lesson {} li .wpc-lesson-complete {} li .wpc-lesson-completed {}
600 ';
601 }
602 $text = str_replace("} ", "}\n", $text);
603 $disabled = !WPCOMPLETE_IS_ACTIVATED;
604
605 include 'partials/wpcomplete-admin-settings-textarea.php';
606 }
607
608 /**
609 * Render checkbox for if should show dashboard widget
610 *
611 * @since 2.1.0
612 */
613 public function settings_show_widget_cb() {
614 $is_enabled = get_option( $this->plugin_name . '_show_widget', 'true' );
615 $disabled = !WPCOMPLETE_IS_ACTIVATED;
616 include 'partials/wpcomplete-admin-settings-widget.php';
617 }
618
619 /**
620 * PREMIUM:
621 * Script used to activate license keys.
622 *
623 * @since 1.0.0
624 */
625 public function activate_license() {
626 // Clear cache...
627 delete_transient( WPCOMPLETE_PREFIX . '_license_status' );
628 // listen for our activate button to be clicked
629 if ( isset( $_POST[$this->plugin_name . '_license_activate'] ) ) {
630 // run a quick security check
631 //if ( ! check_admin_referer( $this->plugin_name . '_license_nonce', $this->plugin_name . '_license_nonce' ) )
632 // return; // get out if we didn't click the Activate button
633
634 // retrieve the license from the database
635 $license = trim( $_POST[ $this->plugin_name . '_license_key'] );
636
637 // If posted license isn't the same as what's stored, store it.
638 $current = get_option( $this->plugin_name . '_license_key');
639 if ( $current != $license ) {
640 update_option( $this->plugin_name . '_license_key', $license);
641 }
642
643 // data to send in our API request
644 $api_params = array(
645 'edd_action' => 'activate_license',
646 'license' => $license,
647 'item_name' => urlencode( WPCOMPLETE_PRODUCT_NAME ),
648 'url' => home_url()
649 );
650
651 // Call the custom API.
652 $response = wp_remote_post( WPCOMPLETE_STORE_URL, array(
653 'timeout' => 15,
654 'sslverify' => false,
655 'body' => $api_params
656 ) );
657
658 $message = '';
659 // make sure the response came back okay
660 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
661 $message = ( is_wp_error( $response ) && $response->get_error_message() ) ? $response->get_error_message() : __( 'An error occurred, please try again.' );
662 } else {
663 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
664 if ( false === $license_data->success ) {
665 switch( $license_data->error ) {
666 case 'expired' :
667 $message = sprintf(
668 __( 'Your license key expired on %s.' ),
669 date_i18n( get_option( 'date_format' ), strtotime( $license_data->expires, current_time( 'timestamp' ) ) )
670 );
671 break;
672 case 'revoked' :
673 $message = __( 'Your license key has been disabled.' );
674 break;
675 case 'missing' :
676 $message = __( 'Invalid license.' );
677 break;
678 case 'invalid' :
679 case 'site_inactive' :
680 $message = __( 'Your license is not active for this URL.' );
681 break;
682 case 'item_name_mismatch' :
683 $message = sprintf( __( 'This appears to be an invalid license key for %s.' ), WPCOMPLETE_PRODUCT_NAME );
684 break;
685 case 'no_activations_left':
686 $message = __( 'Your license key has reached its activation limit.' );
687 break;
688 default :
689 $message = __( 'An error occurred, please try again.' );
690 break;
691 }
692 }
693 }
694 // Check if anything passed on a message constituting a failure
695 if ( ! empty( $message ) ) {
696 $base_url = admin_url( 'options-general.php?page=' . $this->plugin_name );
697 $redirect = add_query_arg( array( 'sl_activation' => 'false', 'message' => urlencode( $message ) ), $base_url );
698 wp_redirect( $redirect );
699 exit();
700 }
701
702 update_option( $this->plugin_name . '_license_status', $license_data->expires );
703 wp_redirect( admin_url( 'options-general.php?page=' . $this->plugin_name ) );
704 exit();
705
706 }
707 }
708
709 /* END SETTINGS PAGE HELPERS */
710
711 /**
712 * Render the meta box for this plugin enabling completion functionality
713 *
714 * @since 1.0.0
715 */
716 public function add_completable_metabox() {
717 $screens = $this->get_enabled_post_types();
718
719 foreach ( $screens as $screen ) {
720 add_meta_box(
721 'completable', // Unique ID
722 __( 'WPComplete', $this->plugin_name ), // Box title
723 array( $this, 'add_completable_metabox_cb' ), // Content callback
724 $screen // post type
725 );
726 }
727 }
728
729 /**
730 * Callback which renders the actual html for completable metabox. Includes enabling completability and redirect url.
731 *
732 * @since 1.0.0
733 * @last 2.0.0
734 */
735 public function add_completable_metabox_cb( $post ) {
736 // get the variables we need to build the form:
737 $completable = false;
738 $redirect = array('title' => '', 'url' => '');
739 $post_meta = get_post_meta( $post->ID, 'wpcomplete', true);
740 $post_course = false;
741
742 if ($post_meta) {
743 $completable = true;
744 $post_meta = json_decode($post_meta, true);
745 $post_course = ( isset( $post_meta['course'] ) ) ? $post_meta['course'] : false;
746
747 $redirect = ( isset( $post_meta['redirect'] ) ) ? $post_meta['redirect'] : array('title' => '', 'url' => '');
748 }
749 // include a nonce to ensure we can save:
750 wp_nonce_field( $this->plugin_name, 'completable_nonce' );
751
752 include 'partials/wpcomplete-admin-metabox.php';
753 }
754
755 /**
756 * Add options to the bulk menu for posts and pages.
757 *
758 * @since 1.0.0
759 */
760 public function add_bulk_actions() {
761 global $post_type;
762
763 if ( in_array( $post_type, $this->get_enabled_post_types() ) ) {
764 ?>
765 <script defer type="text/javascript">
766 jQuery(document).ready(function() {
767 jQuery('<option>').val('completable').text("<?php _e('Can Complete', $this->plugin_name)?>").appendTo("select[name='action'],select[name='action2']");
768 <?php
769 $courses = $this->get_course_names();
770 if ( count( $courses ) > 0 ) { ?>
771 jQuery('<option>').val('course::true').text("<?php _e('Assign to: ' . get_bloginfo( 'name' ), $this->plugin_name)?>").appendTo("select[name='action'],select[name='action2']");
772 <?php foreach ( $courses as $course_name ) { ?>
773 jQuery('<option>').val('course::<?php echo $course_name; ?>').text("Assign to: <?php _e($course_name, $this->plugin_name)?>").appendTo("select[name='action'],select[name='action2']");
774 <?php }
775 } ?>
776
777 });
778 </script>
779 <?php
780 }
781 }
782
783 /**
784 * Save script for saving an individual post/page, enabling it as completable
785 * PREMIUM: and custom redirect url.
786 *
787 * @since 1.0.0
788 * @last 2.0.3
789 */
790 public function save_completable( $post_id ) {
791 if ( isset( $_POST['completable_nonce'] ) && isset( $_POST['post_type'] ) && isset( $_POST['wpcomplete'] ) && isset( $_POST['wpcomplete']['completable'] ) ) {
792 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
793 //echo '<!-- Autosave -->';
794 return;
795 } // end if
796
797 // Verify that the input is coming from the proper form
798 if ( ! wp_verify_nonce( $_POST['completable_nonce'], $this->plugin_name ) ) {
799 //echo '<!-- NONCE FAILED -->';
800 return;
801 } // end if
802
803 // Make sure the user has permissions to posts and pages
804 if ( ! in_array( $_POST['post_type'], $this->get_enabled_post_types() ) ) {
805 // echo '<!-- Post type isn\'t allowed to be marked as completable -->';
806 return;
807 }
808
809 $is_completable = $_POST['wpcomplete']['completable'];
810 // PREMIUM:
811 $course_name = 'true';
812 if ( isset( $_POST['wpcomplete']['course-custom'] ) && !empty( $_POST['wpcomplete']['course-custom'] ) ) {
813 $course_name = $_POST['wpcomplete']['course-custom'];
814 } else if ( isset( $_POST['wpcomplete']['course'] ) && !empty( $_POST['wpcomplete']['course'] ) ) {
815 $course_name = $_POST['wpcomplete']['course'];
816 }
817
818 $redirect_to = ( isset( $_POST['wpcomplete']['completion-redirect-to'] ) ) ? $_POST['wpcomplete']['completion-redirect-to'] : '';
819 $redirect_url = ( isset( $_POST['wpcomplete']['completion-redirect-url'] ) ) ? $_POST['wpcomplete']['completion-redirect-url'] : '';
820 $redirect = array('title' => $redirect_to, 'url' => $redirect_url);
821
822 if ($is_completable == 'true') {
823
824 $post_meta = array();
825
826 if ( $course_name !== 'true' ) {
827 $post_meta['course'] = $course_name;
828 }
829
830 // TODO: Can we optimize this any?
831 $content = '';
832 if (!empty($_POST['post_content'])) {
833 $content = $_POST['post_content'];
834 } else {
835 foreach ($_POST as $key => $value) {
836 if (is_array($value)) {
837 foreach ($value as $key2 => $value2) {
838 if ( !is_array( $value2 ) && false !== strpos( $value2, '[wpc' ) ) {
839 $content = $value2;
840 break;
841 }
842 }
843 } else {
844 if ( false !== strpos( $value, '[wpc' ) ) {
845 $content = $value;
846 break;
847 }
848 }
849 }
850 }
851 $post_meta = $this->add_multiple_buttons_to_meta($post_id, $post_meta, $content);
852
853 if ( !empty( $redirect_to ) ) {
854 $post_meta['redirect'] = $redirect;
855 }
856
857 // Update it for this post.
858 update_post_meta( $post_id, 'wpcomplete', json_encode( $post_meta, JSON_UNESCAPED_UNICODE ) );
859
860 } else {
861
862 // If the value exists, delete it.
863 delete_post_meta( $post_id, 'wpcomplete' );
864
865 }
866
867 wp_cache_delete( "posts", 'wpcomplete' );
868
869 }
870 }
871
872 /**
873 * Save script for the bulk action that marks multiple pages/posts as completable.
874 *
875 * @since 1.0.0
876 * @last 2.0.3
877 */
878 public function save_bulk_completable() {
879 global $typenow;
880 $post_type = $typenow;
881
882 if ( in_array( $post_type, $this->get_enabled_post_types() ) && isset($_REQUEST['post']) ) {
883 if ( (($_REQUEST['action'] == 'completable') || ($_REQUEST['action2'] == 'completable')) ) {
884 // security check
885 check_admin_referer( 'bulk-posts' );
886
887 $action = ($_REQUEST['action'] == '-1') ? $_REQUEST['action2'] : $_REQUEST['action'];
888
889 // make sure ids are submitted. depending on the resource type, this may be 'media' or 'ids'
890 if ( isset($_REQUEST['post'] ) ) {
891 $post_ids = array_map( 'intval', $_REQUEST['post'] );
892 }
893
894 if ( empty( $post_ids ) ) return;
895
896 // this is based on wp-admin/edit.php
897 $sendback = remove_query_arg( array('exported', 'untrashed', 'deleted', 'ids'), wp_get_referer() );
898 if ( ! $sendback )
899 $sendback = admin_url( "edit.php?post_type=$post_type" );
900
901 // do the marking as complete!
902 $marked = 0;
903 foreach ( $post_ids as $post_id ) {
904 $post_meta = get_post_meta( $post_id, 'wpcomplete', true );
905
906 if ( ! $post_meta ) {
907 // Enable the post because it wasn't previously.
908 $post_meta = array();
909 // Check to see if we need to add multiple buttons to database meta info:
910 $post_content = get_post_field('post_content', $post_id);
911 $post_meta = $this->add_multiple_buttons_to_meta($post_id, $post_meta, $post_content);
912
913 update_post_meta( $post_id, 'wpcomplete', json_encode( $post_meta, JSON_UNESCAPED_UNICODE ) );
914 $marked++;
915 } else {
916 // Already enabled... no need to do anything...
917 }
918 }
919
920 $sendback = add_query_arg( array('completable' => $marked, 'ids' => join(',', $post_ids) ), $sendback );
921 $sendback = remove_query_arg( array('action', 'action2', 'tags_input', 'post_author', 'comment_status', 'ping_status', '_status', 'post', 'bulk_edit', 'post_view'), $sendback );
922
923 wp_cache_delete( "posts", 'wpcomplete' );
924
925 wp_redirect( $sendback );
926 exit();
927 } else if ( ( (substr($_REQUEST['action'], 0, strlen('course::')) == 'course::') || (substr($_REQUEST['action2'], 0, strlen('course::')) == 'course::') ) ) {
928 // security check
929 check_admin_referer( 'bulk-posts' );
930
931 $action = ($_REQUEST['action'] == '-1') ? $_REQUEST['action2'] : $_REQUEST['action'];
932 list($action, $course_name) = explode("::", $action);
933
934 // make sure ids are submitted. depending on the resource type, this may be 'media' or 'ids'
935 if ( isset($_REQUEST['post'] ) ) {
936 $post_ids = array_map( 'intval', $_REQUEST['post'] );
937 }
938
939 if ( empty( $post_ids ) ) return;
940
941 // this is based on wp-admin/edit.php
942 $sendback = remove_query_arg( array('exported', 'untrashed', 'deleted', 'ids'), wp_get_referer() );
943 if ( ! $sendback )
944 $sendback = admin_url( "edit.php?post_type=$post_type" );
945
946 // do the marking as complete!
947 $marked = 0;
948 foreach ( $post_ids as $post_id ) {
949 $post_meta = get_post_meta( $post_id, 'wpcomplete', true );
950
951 if ( ! $post_meta ) {
952 // Enable the post because it wasn't previously.
953 $post_meta = array('course' => $course_name);
954 // Check to see if we need to add multiple buttons to database meta info:
955 $post_content = get_post_field('post_content', $post_id);
956 $post_meta = $this->add_multiple_buttons_to_meta($post_id, $post_meta, $post_content);
957
958 update_post_meta( $post_id, 'wpcomplete', json_encode( $post_meta, JSON_UNESCAPED_UNICODE ) );
959 $marked++;
960 } else {
961 $post_meta = json_decode( $post_meta, true );
962 if ( !isset($post_meta['course']) || ( $post_meta['course'] != $course_name ) ) {
963 $post_meta['course'] = $course_name;
964 // Check to see if we need to add multiple buttons to database meta info:
965 $post_content = get_post_field('post_content', $post_id);
966 $post_meta = $this->add_multiple_buttons_to_meta($post_id, $post_meta, $post_content);
967
968 update_post_meta( $post_id, 'wpcomplete', json_encode( $post_meta, JSON_UNESCAPED_UNICODE ) );
969 $marked++;
970 } else {
971 // Already in this course... no need to do anything...
972 }
973 }
974 }
975
976 $sendback = add_query_arg( array('course' => $marked, 'ids' => join(',', $post_ids) ), $sendback );
977 $sendback = remove_query_arg( array('action', 'action2', 'tags_input', 'post_author', 'comment_status', 'ping_status', '_status', 'post', 'bulk_edit', 'post_view'), $sendback );
978
979 wp_cache_delete( "posts", 'wpcomplete' );
980
981 wp_redirect( $sendback );
982 exit();
983 }
984 }
985 }
986
987 /**
988 * Add a notice message for completed bulk actions.
989 *
990 * @since 1.0.0
991 */
992 public function show_bulk_action_notice() {
993 global $post_type, $pagenow;
994
995 if ( $pagenow == 'edit.php' && in_array( $post_type, $this->get_enabled_post_types() ) && isset($_REQUEST['completable']) && (int) $_REQUEST['completable']) {
996 $message = sprintf( _n( 'Post marked completable by students.', '%s posts marked as completable by students.', $_REQUEST['completable'] ), number_format_i18n( $_REQUEST['completable'] ) );
997 echo "<div class=\"updated\"><p>{$message}</p></div>";
998 } else if ( $pagenow == 'edit.php' && in_array( $post_type, $this->get_enabled_post_types() ) && isset($_REQUEST['course']) && (int) $_REQUEST['course']) {
999 $message = sprintf( _n( 'Post assigned to course.', '%s posts assigned to course.', $_REQUEST['course'] ), number_format_i18n( $_REQUEST['course'] ) );
1000 echo "<div class=\"updated\"><p>{$message}</p></div>";
1001 }
1002 }
1003
1004 /**
1005 * PREMIUM:
1006 * If the license has not been configured properly, display an admin notice.
1007 *
1008 * @since 1.0.0
1009 */
1010 public function show_license_notice() {
1011 global $pagenow;
1012
1013 if ( !WPCOMPLETE_IS_ACTIVATED ) {
1014 $msg = __( 'Please activate your license key to enable all WPComplete PRO features.', $this->plugin_name );
1015
1016 include 'partials/wpcomplete-admin-license-notice.php';
1017 }
1018 }
1019
1020 /**
1021 * Add the new custom column header, "User Completion" to pages and posts edit.php page.
1022 *
1023 * @since 1.0.0
1024 */
1025 public function add_custom_column_header( $columns ) {
1026 global $post_type;
1027
1028 if (!$post_type) $post_type = $_POST['post_type'];
1029
1030 if ( in_array( $post_type, $this->get_enabled_post_types() ) ) {
1031
1032 if ( count( $this->get_course_names() ) > 0 ) {
1033 $columns = array_merge( $columns, array( 'completable-course' => __( 'Course Name', $this->plugin_name ) ) );
1034 }
1035
1036 $columns = array_merge( $columns, array( 'completable' => __( 'Completion', $this->plugin_name ) ) );
1037 }
1038
1039 return $columns;
1040 }
1041
1042 /**
1043 * Add the values for each post/page of the new custom "Completion %" column.
1044 * If post/page isn't enabled to be completed, it shows — in column.
1045 * If wordpress install doesn't have any subscribers (students), it shows "0 Students".
1046 * Otherwise, it'll show the ratio and percentage of how many students have completed it.
1047 *
1048 * @since 1.0.0
1049 * @last 2.0.0
1050 */
1051 public function add_custom_column_value( $column_name, $post_id ) {
1052 if ( $column_name == 'completable-course' ) {
1053 $posts = $this->get_completable_posts();
1054
1055 if ( isset( $posts[$post_id] ) ) {
1056 $course_name = (!isset($posts[$post_id]['course'])) ? get_bloginfo( 'name' ) : $posts[$post_id]['course'];
1057 } else {
1058 $course_name = '';
1059 }
1060
1061 echo '<div id="completable-course-' . $post_id . '">' . $course_name . '</div>';
1062
1063 } else if ( $column_name == 'completable' ) {
1064 $posts = $this->get_completable_posts();
1065
1066 if ( isset( $posts[$post_id] ) ) {
1067 $users_of_blog = count_users();
1068 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
1069
1070 if ( $selected_role == 'all' ) {
1071 $avail_users = $users_of_blog['total_users'];
1072 } else {
1073 $avail_users = ( isset( $users_of_blog['avail_roles'][$selected_role] ) ) ? $users_of_blog['avail_roles'][$selected_role] : 0;
1074 }
1075
1076 if ($avail_users > 0) {
1077 $completion = '';
1078
1079 $args = array('fields' => 'id');
1080 if ($selected_role != 'all') $args['role'] = $selected_role;
1081 //$args['meta_key'] = 'wpcomplete';
1082 $users = get_users($args);
1083
1084 if ( isset( $posts[$post_id]['buttons'] ) ) {
1085 foreach ( $posts[$post_id]['buttons'] as $button) {
1086 // calculate how many of these users are completed...
1087 $completed_users = 0;
1088 foreach ($users as $user_id) {
1089 $user_completed = $this->get_user_completed($user_id);
1090 if ( isset( $user_completed[$button] ) ) {
1091 $completed_users++;
1092 }
1093 }
1094 list($button_post_id, $button_id) = $this->extract_button_info($button);
1095 $button_name = ($button === ''.$post_id) ? 'Default Button' : "Button '$button_id'";
1096 $completion .= ('<a href="edit.php?page=wpcomplete-buttons&post_id=' . $post_id . '&button=' . $button . '">' . "$button_name: $completed_users/$avail_users Users (" . round(100 * ($completed_users / $avail_users), 1) . '%)</a><br>');
1097 }
1098 } else {
1099 // calculate how many of these users are completed...
1100 $completed_users = 0;
1101 foreach ($users as $user_id) {
1102 $user_completed = $this->get_user_completed($user_id);
1103 if ( isset( $user_completed[$post_id] ) ) {
1104 $completed_users++;
1105 }
1106 }
1107 $completion = '<a href="edit.php?page=wpcomplete-posts&post_id=' . $post_id . '">' . ("$completed_users/$avail_users Users (" . round(100 * ($completed_users / $avail_users), 1) . '%)') . '</a>';
1108 }
1109 } else {
1110 $completion = "0 Users";
1111 }
1112 echo '<div id="completable-' . $post_id . '">' . $completion . '</div>';
1113 } else {
1114 echo '<div id="completable-' . $post_id . '">—</div>';
1115 }
1116 }
1117 }
1118
1119 /**
1120 *
1121 *
1122 * @since 2.3.0
1123 * @last 2.3.0
1124 */
1125 public function add_course_completion_page() {
1126 add_menu_page(
1127 'WPComplete',
1128 __( 'WPComplete', $this->plugin_name ),
1129 'manage_options',
1130 'wpcomplete-courses',
1131 array( $this, 'render_courses_page' ),
1132 'dashicons-yes',
1133 50
1134 );
1135
1136
1137 add_submenu_page(
1138 'wpcomplete-courses',
1139 __( 'Courses', $this->plugin_name ),
1140 __( 'Courses', $this->plugin_name ),
1141 'manage_options',
1142 'wpcomplete-courses',
1143 array( $this, 'render_courses_page' )
1144 );
1145
1146 add_submenu_page(
1147 'wpcomplete-courses',
1148 __( 'General Settings', $this->plugin_name ),
1149 __( 'General Settings', $this->plugin_name ),
1150 'manage_options',
1151 'wpcomplete-settings',
1152 array( $this, 'render_course_settings_page' )
1153 );
1154 }
1155
1156 /**
1157 *
1158 *
1159 * @since 2.3.0
1160 * @last 2.3.0
1161 */
1162 public function render_courses_page() {
1163 if ( ! current_user_can( 'manage_options' ) ) {
1164 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1165 }
1166 if ( ! WPCOMPLETE_IS_ACTIVATED ) {
1167 wp_die( __( 'You only get access to this data once you activate your license.' ) );
1168 }
1169
1170 $total_posts = $this->get_buttons( array( 'course' => 'all' ) );
1171 $post_data = $this->get_completable_posts();
1172 $courses = array();
1173
1174 foreach ($total_posts as $button) {
1175 list($post_id, $button_id) = $this->extract_button_info($button);
1176
1177 if ( isset( $post_data[$post_id]['course'] ) ) {
1178 $course_name = $post_data[$post_id]['course'];
1179 } else {
1180 $course_name = get_bloginfo( 'name' );
1181 }
1182
1183 if (!isset($courses[$course_name])) {
1184 $courses[$course_name] = array();
1185 $courses[$course_name]['buttons'] = array();
1186 //$courses[$course_name]['stats'] = array('completed' => 0);
1187 }
1188
1189 if ($this->post_has_multiple_buttons($post_id)) {
1190 if ($button != $post_id) {
1191 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ") - Button: " . $button_id;
1192 $courses[$course_name]['buttons'][$button_name] = array('id' => $button, 'link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id . "&amp;button=" . $button_id, 'completed' => 0);
1193 } else {
1194 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ") - Default Button";
1195 $courses[$course_name]['buttons'][$button_name] = array('id' => $button, 'link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id, 'completed' => 0);
1196 }
1197 } else {
1198 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ")";
1199 $courses[$course_name]['buttons'][$button_name] = array('id' => $button, 'link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id, 'completed' => 0);
1200 }
1201 }
1202
1203 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
1204 // Get all users that are able to complete the post:
1205 $args = array('fields' => 'id');
1206 if ($selected_role != 'all') $args['role'] = $selected_role;
1207 $total_users = get_users($args);
1208
1209 foreach ($total_users as $user_id) {
1210 $user_completed_raw = $this->get_user_completed( $user_id );
1211 $user_completed = array();
1212 foreach ($user_completed_raw as $button_id => $value) {
1213 if ( in_array( $button_id, $total_posts ) && isset( $value['completed'] ) ) {
1214 if ($value['completed'] === true) {
1215 $value['completed'] = 'Yes';
1216 }
1217 $user_completed[$button_id] = $value['completed'];
1218 }
1219 }
1220
1221 // array('course' => array('stats' => array('ratio', 'percentage'), 'buttons' => array('button' => array('link', 'completion' => completion_datetime/false))))
1222 foreach ( $courses as $course_name => $course_info) {
1223 foreach ( $course_info['buttons'] as $button_name => $button ) {
1224 if ( isset( $user_completed[$button['id']] ) ) {
1225 $courses[$course_name]['buttons'][$button_name]['completed']++;
1226 }
1227 }
1228 }
1229 }
1230
1231 ksort($courses);
1232
1233 if ( isset( $_GET['course'] ) && isset( $courses[$_GET['course']] ) ) {
1234 $courses = array($_GET['course'] => $courses[$_GET['course']]);
1235 }
1236
1237 include_once 'partials/wpcomplete-admin-courses.php';
1238 }
1239
1240 /**
1241 *
1242 *
1243 * @since 2.3.0
1244 * @last 2.3.0
1245 */
1246 public function render_course_settings_page() {
1247 echo "<p><em>loading...</em></p><script type='text/javascript'> window.location = 'options-general.php?page=wpcomplete'; </script>";
1248 //wp_redirect('options-general.php?page=wpcomplete');
1249 //exit;
1250 }
1251
1252 /**
1253 *
1254 *
1255 * @since 1.4.0
1256 */
1257 public function add_post_completion_page() {
1258 add_submenu_page(
1259 null,
1260 __( 'Post Completion', $this->plugin_name ),
1261 __( 'Post Completion', $this->plugin_name ),
1262 'manage_options',
1263 'wpcomplete-posts',
1264 array( $this, 'render_post_completion_page' )
1265 );
1266 }
1267
1268 /**
1269 *
1270 *
1271 * @since 1.4.0
1272 * @last 2.0.0
1273 */
1274 public function render_post_completion_page() {
1275 global $wpdb;
1276
1277 if ( ! current_user_can( 'manage_options' ) ) {
1278 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1279 }
1280 if ( ! $_GET['post_id'] ) {
1281 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1282 }
1283 if ( ! WPCOMPLETE_IS_ACTIVATED ) {
1284 wp_die( __( 'You only get access to this data once you activate your license.' ) );
1285 }
1286 // Get post info:
1287 $post_id = $_GET['post_id'];
1288 $post = get_post($post_id);
1289
1290 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
1291 // Get all users that are able to complete the post:
1292 $args = array('fields' => 'all');
1293 if ($selected_role != 'all') $args['role'] = $selected_role;
1294 $total_users = get_users($args);
1295
1296 $user_completed = array();
1297 foreach ($total_users as $user) {
1298 $user_completed_raw = $this->get_user_completed( $user->ID );
1299 if ( isset( $user_completed_raw[$post_id] ) && isset( $user_completed_raw[$post_id]['completed'] ) ) {
1300 if ($user_completed_raw[$post_id]['completed'] === true) {
1301 $user_completed_raw[$post_id]['completed'] = 'Yes';
1302 }
1303 $user_completed[$user->ID] = $user_completed_raw[$post_id]['completed'];
1304 }
1305 }
1306
1307 include_once 'partials/wpcomplete-admin-post-completion.php';
1308 }
1309
1310 /**
1311 *
1312 *
1313 * @since 2.0.0
1314 * @last 2.0.0
1315 */
1316 public function add_button_completion_page() {
1317 add_submenu_page(
1318 null,
1319 __( 'Button Completion', $this->plugin_name ),
1320 __( 'Button Completion', $this->plugin_name ),
1321 'manage_options',
1322 'wpcomplete-buttons',
1323 array( $this, 'render_button_completion_page' )
1324 );
1325 }
1326
1327 /**
1328 *
1329 *
1330 * @since 2.0.0
1331 * @last 2.0.6
1332 */
1333 public function render_button_completion_page() {
1334 global $wpdb;
1335
1336 if ( ! current_user_can( 'manage_options' ) ) {
1337 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1338 }
1339 if ( ! $_GET['post_id'] || ! $_GET['button'] ) {
1340 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1341 }
1342 if ( ! WPCOMPLETE_IS_ACTIVATED ) {
1343 wp_die( __( 'You only get access to this data once you activate your license.' ) );
1344 }
1345 // Get post info:
1346 $button_id = $_GET['button'];
1347 list($post_id, $button) = $this->extract_button_info($button_id);
1348 $post_id = $_GET['post_id'];
1349 $post = get_post($post_id);
1350
1351 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
1352 // Get all users that are able to complete the post:
1353 $args = array('fields' => 'all');
1354 if ($selected_role != 'all') $args['role'] = $selected_role;
1355 $total_users = get_users($args);
1356 # TODO: confirm this update is fixed
1357 $total_posts = $this->get_completable_posts();//$this->get_buttons();
1358
1359 $user_completed = array();
1360 foreach ($total_users as $user) {
1361 $user_completed_raw = $this->get_user_completed( $user->ID );
1362 //if ( in_array( $post_id, $total_posts ) && isset( $user_completed_raw[$button_id] ) && isset( $user_completed_raw[$button_id]['completed'] ) ) {
1363 if ( isset( $total_posts[ $post_id ] ) && isset( $user_completed_raw[$button_id] ) && isset( $user_completed_raw[$button_id]['completed'] ) ) {
1364 if ($user_completed_raw[$button_id]['completed'] === true) {
1365 $user_completed_raw[$button_id]['completed'] = 'Yes';
1366 }
1367 $user_completed[$user->ID] = $user_completed_raw[$button_id]['completed'];
1368 }
1369 }
1370
1371 include_once 'partials/wpcomplete-admin-button-completion.php';
1372 }
1373
1374 /**
1375 * Add custom field for quick edit of posts and pages.
1376 *
1377 * @since 1.0.0
1378 */
1379 public function add_custom_quick_edit( $column_name, $post_type ) {
1380 if ( in_array( $post_type, $this->get_enabled_post_types() ) ) {
1381 include 'partials/wpcomplete-admin-quickedit.php';
1382 }
1383 }
1384
1385 /**
1386 * Add the new custom column header, "Lesson Completion" to users page.
1387 *
1388 * @since 1.0.0
1389 * @last 2.0.0
1390 */
1391 public function add_user_column_header( $columns ) {
1392 $posts = $this->get_completable_posts();
1393 if ( count($posts) > 0 ) {
1394 return array_merge( $columns, array( 'completable' => __( 'Completion', $this->plugin_name) ));
1395 } else {
1396 return $columns;
1397 }
1398 }
1399
1400 /**
1401 * Add the values for each user of the new custom "Completion" column.
1402 * If user is not in a student role, it shows — in column.
1403 * Otherwise, it'll show the ratio and percentage of student's completion.
1404 *
1405 * @since 1.0.0
1406 * @last 2.3.0
1407 */
1408 public function add_user_column_value( $value, $column_name, $user_id ) {
1409 if ( $column_name == 'completable' ) {
1410 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
1411 $user = get_userdata( $user_id );
1412
1413 if ( ( $selected_role == 'all' ) || in_array( $selected_role, $user->roles ) ) {
1414 $total_posts = $this->get_buttons( array( 'course' => 'all' ) );
1415 $post_data = $this->get_completable_posts();
1416
1417 $user_completed_raw = $this->get_user_completed( $user_id );
1418 $user_completed = array();
1419 foreach ($user_completed_raw as $button_id => $value) {
1420 if ( in_array( $button_id, $total_posts ) && isset( $value['completed'] ) ) {
1421 if ($value['completed'] === true) {
1422 $value['completed'] = 'Yes';
1423 }
1424 $user_completed[$button_id] = $value['completed'];
1425 }
1426 }
1427
1428 $courses = array();
1429 // array('course' => array('stats' => array('ratio', 'percentage'), 'buttons' => array('button' => array('link', 'completion' => completion_datetime/false))))
1430 foreach ($total_posts as $button) {
1431 list($post_id, $button_id) = $this->extract_button_info($button);
1432
1433 if ( isset( $post_data[$post_id]['course'] ) ) {
1434 $course_name = $post_data[$post_id]['course'];
1435 } else {
1436 $course_name = get_bloginfo( 'name' );
1437 }
1438
1439 if (!isset($courses[$course_name])) {
1440 $courses[$course_name] = array();
1441 $courses[$course_name]['buttons'] = array();
1442 $courses[$course_name]['stats'] = array('completed' => 0);
1443 }
1444
1445 if ($this->post_has_multiple_buttons($post_id)) {
1446 if ($button != $post_id) {
1447 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ") - Button: " . $button_id;
1448 $courses[$course_name]['buttons'][$button_name] = array('link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id . "&amp;button=" . $button_id);
1449 } else {
1450 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ") - Default Button";
1451 $courses[$course_name]['buttons'][$button_name] = array('link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id);
1452 }
1453 } else {
1454 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ")";
1455 $courses[$course_name]['buttons'][$button_name] = array('link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id);
1456 }
1457 if ( isset($user_completed[$button]) ) {
1458 $courses[$course_name]['buttons'][$button_name]['completed'] = $user_completed[$button];
1459 $courses[$course_name]['stats']['completed']++;
1460 } else {
1461 $courses[$course_name]['buttons'][$button_name]['completed'] = 'No';
1462 }
1463 }
1464 ksort($courses);
1465
1466 ob_start();
1467 include 'partials/wpcomplete-admin-user-completion-column.php';
1468 return ob_get_clean();
1469 } else {
1470 return '<div id="completable-' . $user_id . '">—</div>';
1471 }
1472 } else {
1473 return $value;
1474 }
1475 }
1476
1477 /**
1478 *
1479 *
1480 * @since 1.4.0
1481 */
1482 public function add_user_completion_page() {
1483 add_submenu_page(
1484 null,
1485 __( 'User Completion', $this->plugin_name ),
1486 __( 'User Completion', $this->plugin_name ),
1487 'manage_options',
1488 'wpcomplete-users',
1489 array( $this, 'render_user_completion_page' )
1490 );
1491 }
1492
1493 /**
1494 *
1495 *
1496 * @since 1.4.0
1497 * @last 2.0.0
1498 */
1499 public function render_user_completion_page() {
1500 if ( ! current_user_can( 'manage_options' ) ) {
1501 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1502 }
1503 if ( ! $_GET['user_id'] ) {
1504 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1505 }
1506 if ( ! WPCOMPLETE_IS_ACTIVATED ) {
1507 wp_die( __( 'You only get access to this data once you activate your license.' ) );
1508 }
1509
1510 $user_id = $_GET['user_id'];
1511 $total_posts = $this->get_buttons( array( 'course' => 'all' ) );
1512 $post_data = $this->get_completable_posts();
1513
1514 $user_completed_raw = $this->get_user_completed( $user_id );
1515 $user_completed = array();
1516 foreach ($user_completed_raw as $button_id => $value) {
1517 if ( in_array( $button_id, $total_posts ) && isset( $value['completed'] ) ) {
1518 if ($value['completed'] === true) {
1519 $value['completed'] = 'Yes';
1520 }
1521 $user_completed[$button_id] = $value['completed'];
1522 }
1523 }
1524
1525 $user = get_userdata( $user_id );
1526 $courses = array();
1527 // array('course' => array('stats' => array('ratio', 'percentage'), 'buttons' => array('button' => array('link', 'completion' => completion_datetime/false))))
1528 foreach ($total_posts as $button) {
1529 list($post_id, $button_id) = $this->extract_button_info($button);
1530
1531 if ( isset( $post_data[$post_id]['course'] ) ) {
1532 $course_name = $post_data[$post_id]['course'];
1533 } else {
1534 $course_name = get_bloginfo( 'name' );
1535 }
1536
1537 if (!isset($courses[$course_name])) {
1538 $courses[$course_name] = array();
1539 $courses[$course_name]['buttons'] = array();
1540 $courses[$course_name]['stats'] = array('completed' => 0);
1541 }
1542
1543 if ($this->post_has_multiple_buttons($post_id)) {
1544 if ($button != $post_id) {
1545 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ") - Button: " . $button_id;
1546 $courses[$course_name]['buttons'][$button_name] = array('link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id . "&amp;button=" . $button_id);
1547 } else {
1548 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ") - Default Button";
1549 $courses[$course_name]['buttons'][$button_name] = array('link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id);
1550 }
1551 } else {
1552 $button_name = get_the_title($post_id) . " (" . ucwords( str_replace( "_", " ", get_post_type( $post_id ) ) ) . " #" . $post_id . ")";
1553 $courses[$course_name]['buttons'][$button_name] = array('link' => "edit.php?page=wpcomplete-posts&amp;post_id=" . $post_id);
1554 }
1555 if ( isset($user_completed[$button]) ) {
1556 $courses[$course_name]['buttons'][$button_name]['completed'] = $user_completed[$button];
1557 $courses[$course_name]['stats']['completed']++;
1558 } else {
1559 $courses[$course_name]['buttons'][$button_name]['completed'] = 'No';
1560 }
1561 }
1562 ksort($courses);
1563
1564 include_once 'partials/wpcomplete-admin-user-completion.php';
1565 }
1566
1567 /**
1568 * Add multiple buttons to a post's meta data if they exist in the post content.
1569 *
1570 * @since 2.2.6
1571 * @last 2.2.6
1572 */
1573 public function delete_button() {
1574 // delete the button...
1575 // add flash message?
1576 $post_id = $_REQUEST['post_id'];
1577
1578 $post_meta_json = get_post_meta( $post_id, 'wpcomplete', true );
1579 $post_meta = json_decode( $post_meta_json, JSON_UNESCAPED_UNICODE );
1580
1581 //var_dump($post_meta);
1582
1583 $buttons = $post_meta['buttons'];
1584 // delete specific button
1585 $key = array_search($_REQUEST['button'], $buttons);
1586 unset($buttons[$key]);
1587
1588 $post_meta['buttons'] = $buttons;
1589
1590 //var_dump($post_meta);
1591
1592 $saved = update_post_meta( $post_id, 'wpcomplete', json_encode( $post_meta, JSON_UNESCAPED_UNICODE ) );
1593
1594 //wp_redirect( $_SERVER['HTTP_REFERER'] );
1595 $response = json_encode( $saved, JSON_UNESCAPED_UNICODE );
1596 echo $response;
1597 exit();
1598 }
1599
1600 /**
1601 * Add multiple buttons to a post's meta data if they exist in the post content.
1602 *
1603 * @since 2.0.0
1604 * @last 2.0.2
1605 */
1606 public function add_multiple_buttons_to_meta($post_id, $post_meta, $post_content = '') {
1607 // check if we need to store multiple buttons...
1608 if ( false !== strpos( $post_content, '[' ) ) {
1609 // Check for shortcodes to see what buttons we have...
1610 //$pattern = get_shortcode_regex();
1611 // We just want wpcomplete buttons... can ignore anything else:
1612 $pattern = '\[(\[?)(complete_button|wpc_complete_button|wpc_button|wpcomplete_button)(?![\w-])([^\]\/]*(?:\/(?!\])[^\]\/]*)*?)(?:(\/)\]|\](?:([^\[]*+(?:\[(?!\/\2\])[^\[]*+)*+)\[\/\2\])?)(\]?)';
1613 preg_match_all('/'.$pattern.'/s', $post_content, $matches);
1614
1615 $shortcodes = array_unique( $matches[0] );
1616
1617 // loop through and only keep button tags...
1618 $shortcodes = array_filter($shortcodes, function($value) {
1619 if (strstr($value, '[complete_button') !== false) return true;
1620 if (strstr($value, '[wpc_complete_button') !== false) return true;
1621 if (strstr($value, '[wpc_button') !== false) return true;
1622 if (strstr($value, '[wpcomplete_button') !== false) return true;
1623
1624 return false;
1625 });
1626
1627 $buttons = array();
1628 foreach ($shortcodes as $code) {
1629 // normalize button code...
1630 $code = str_replace('[complete_button', '[wpc_complete_button', $code);
1631 $code = str_replace('[wpc_button', '[wpc_complete_button', $code);
1632 $code = str_replace('[wpcomplete_button', '[wpc_complete_button', $code);
1633 $code = str_replace('', '"', $code);
1634
1635 $parsed_args = shortcode_parse_atts($code);
1636
1637 if ( count( $parsed_args ) <= 1 ) {
1638 // no attributes:
1639 $buttons[] = $this->get_button_id($post_id);
1640 } else {
1641 $unparsed_args = trim(str_replace(array('[wpc_complete_button', ']'), '', $code));
1642 $atts_array = new SimpleXMLElement("<element " . stripslashes($unparsed_args) . " />");
1643 $parsed_args = current((array) $atts_array);
1644
1645 // cleanup attributes...
1646 $args = array();
1647 foreach( $parsed_args as $key => $value ) {
1648 if ( $key == 'name' ) $key = 'id';
1649 if ( $key == 'post' ) $key = 'post_id';
1650 $args[$key] = $value;
1651 }
1652
1653 if ( isset( $args['post_id'] ) && !empty( $args['post_id'] ) ) {
1654 // Skip this button, because its not related to this post...
1655 continue;
1656 }
1657
1658 // build button based on defaults...
1659 if ( isset( $args['id'] ) && !empty( $args['id'] ) ) {
1660 $buttons[] = $this->get_button_id($post_id, $args['id']);
1661 } else {
1662 $buttons[] = $this->get_button_id($post_id);
1663 }
1664 }
1665 }
1666
1667 $buttons = array_unique($buttons);
1668 // Only store the buttons if we have more than 1 or its named different than the post id:
1669 if ( ( count( $buttons ) > 0 ) && ( $buttons != array( ''.$post_id ) ) ) {
1670 $post_meta['buttons'] = $buttons;
1671 }
1672
1673 }
1674
1675 return $post_meta;
1676 }
1677
1678 /**
1679 * PREMIUM:
1680 * Autocomplete ajax lookup function. Given search criteria, returns matching posts and pages.
1681 *
1682 * @since 1.0.0
1683 */
1684 public function post_lookup() {
1685 // TODO: don't include current page in returned results.
1686 $term = strtolower( $_GET['term'] );
1687 $suggestions = array();
1688 // We want to allow redirect to ANY post type on completion, not just enabled ones:
1689 $args = array('s' => $term, 'posts_per_page' => 20);
1690
1691 $loop = new WP_Query( $args );
1692
1693 while ( $loop->have_posts() ) {
1694 $loop->the_post();
1695 $suggestion = array();
1696 $suggestion['label'] = get_the_title() . " (" . ucwords(str_replace("_", " ", get_post_type( get_the_ID() ))) . " #" . get_the_ID() . ")";
1697 $suggestion['link'] = get_permalink();
1698
1699 $suggestions[] = $suggestion;
1700 }
1701
1702 wp_reset_query();
1703
1704
1705 $response = json_encode( $suggestions, JSON_UNESCAPED_UNICODE );
1706 echo $response;
1707 exit();
1708 }
1709
1710 /**
1711 * Return boolean of whether user has started a course or not.
1712 *
1713 * @since 2.0.0
1714 * @last 2.0.4
1715 */
1716 public function user_has_started_course( $user_id, $course, $posts = false ) {
1717 $user_activity = $this->get_user_activity( $user_id );
1718
1719 foreach ($user_activity as $post_id => $value) {
1720 if ( ''.$this->post_course($post_id, $posts) === $course ) {
1721 if ( isset( $value['first_seen'] ) || isset( $value['completed'] ) ) {
1722 return true;
1723 }
1724 }
1725 }
1726
1727 return false;
1728 }
1729
1730 /**
1731 * Return boolean of whether user has finished a course or not.
1732 *
1733 * @since 2.0.0
1734 * @last 2.0.4
1735 */
1736 public function user_has_completed_course( $user_id, $course, $posts = false ) {
1737 // Get the buttons just for this course:
1738 $buttons = $this->get_course_buttons($course, $posts);
1739
1740 if ( count( $buttons ) <= 0 ) return false;
1741
1742 $user_activity = $this->get_user_activity( $user_id );
1743 foreach ($buttons as $button_id ) {
1744 // if user hasnt completed this button, they havent completed the course...
1745 if ( !isset( $user_activity[$button_id] ) || !isset( $user_activity[$button_id]['completed'] ) ) {
1746 return false;
1747 }
1748 }
1749
1750 return true;
1751 }
1752
1753 /**
1754 * Script that deletes a user's course completion data.
1755 *
1756 * @since 2.3.0
1757 * @last 2.3.0
1758 */
1759 public function delete_user_data( ) {
1760 if ( ! current_user_can( 'edit_users' ) ) {
1761 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1762 }
1763 if ( ! $_GET['user_id'] ) {
1764 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1765 }
1766 if ( ! WPCOMPLETE_IS_ACTIVATED ) {
1767 wp_die( __( 'You only get access to this data once you activate your license.' ) );
1768 }
1769
1770 $user_id = $_REQUEST['user_id'];
1771 $user_completed = array();
1772
1773 // delete a users specific course info...
1774 if ( isset( $_REQUEST['course'] ) ) {
1775 $course = $_REQUEST['course'];
1776 $user_completed = $this->get_user_activity($user_id);
1777
1778 $buttons = $this->get_course_buttons($course);
1779
1780 foreach ( $buttons as $button ) {
1781 // Remove this button's data:
1782 if ( isset( $user_completed[ $button ] ) ) {
1783 unset($user_completed[ $button ]);
1784 }
1785 }
1786 }
1787
1788 $this->set_user_activity($user_completed, $user_id);
1789
1790 if ( wp_get_referer() ) {
1791 wp_safe_redirect( wp_get_referer() );
1792 } else {
1793 wp_safe_redirect( admin_url("users.php?page=wpcomplete-users&user_id=" . $_REQUEST['user_id']) );
1794 }
1795
1796 }
1797
1798 }
1799