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 / public / class-wpcomplete-public.php

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

1,218 lines 48.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The public-facing functionality of the plugin.
5 *
6 * @link https://wpcomplete.co
7 * @since 1.0.0
8 *
9 * @package WPComplete
10 * @subpackage wpcomplete/public
11 */
12
13 /**
14 * The public-facing functionality of the plugin.
15 *
16 * Defines the plugin name, version, and two examples hooks for how to
17 * enqueue the admin-specific stylesheet and JavaScript.
18 *
19 * @package WPComplete
20 * @subpackage wpcomplete/public
21 * @author Zack Gilbert <zack@zackgilbert.com>
22 */
23 class WPComplete_Public extends WPComplete_Common {
24
25 /**
26 * The ID of this plugin.
27 *
28 * @since 1.0.0
29 * @access protected
30 * @var string $plugin_name The ID of this plugin.
31 */
32 protected $plugin_name;
33
34 /**
35 * The version of this plugin.
36 *
37 * @since 1.0.0
38 * @access protected
39 * @var string $version The current version of this plugin.
40 */
41 protected $version;
42
43 /**
44 * Register the stylesheets for the public-facing side of the site.
45 *
46 * @since 1.0.0
47 */
48 public function enqueue_styles() {
49 if (is_user_logged_in()) {
50 wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wpcomplete-public.css', array(), $this->version, 'all' );
51 }
52 }
53
54 /**
55 * Register the JavaScript for the public-facing side of the site.
56 *
57 * @since 1.0.0
58 */
59 public function enqueue_scripts() {
60 if (is_user_logged_in()) {
61 wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wpcomplete-public.js', array( 'jquery' ), $this->version, true );
62
63 $completion_nonce = wp_create_nonce( 'completion' );
64 wp_localize_script( $this->plugin_name, 'wpcompletable', array(
65 'ajax_url' => admin_url( 'admin-ajax.php' ),
66 'nonce' => $completion_nonce
67 ) );
68 }
69 }
70
71 /**
72 * Allow for async (ajax) loading of buttons...
73 *
74 * @since 2.2.0
75 * @last 2.2.0
76 */
77 public function get_button() {
78 $nonce = $_POST['_ajax_nonce'];
79 if ( empty( $_POST ) || !wp_verify_nonce( $nonce, 'completion' ) ) {
80 wp_send_json( array() );
81 exit;
82 }
83
84 $unique_button_id = $_REQUEST['button_id'];
85 list($post_id, $button_id) = $this->extract_button_info($unique_button_id);
86
87 // Feature: Allow for custom button texts if it exists for this button:
88 $button_text = get_option($this->plugin_name . '_incomplete_text', 'Mark as complete');
89 if ( isset( $_POST['old_button_text'] ) && !empty( $_POST['old_button_text'] ) ) {
90 $button_text = $_POST['old_button_text'];
91 }
92 $completed_button_text = get_option($this->plugin_name . '_completed_text', 'COMPLETED');
93 if ( isset( $_POST['new_button_text'] ) && !empty( $_POST['new_button_text'] ) ) {
94 $completed_button_text = $_POST['new_button_text'];
95 }
96
97 $updates_to_sendback = array(
98 ('.wpc-button-' . $this->get_button_class( $unique_button_id )) => $this->complete_button_cb( array( 'post_id' => $post_id, 'name' => $button_id, 'text' => $completed_button_text, 'completed_text' => $button_text ) )
99 );
100
101 // Send back array of posts:
102 wp_send_json( $updates_to_sendback );
103 }
104
105 /**
106 * Register the shortcode for [complete_button] for the public-facing side of the site.
107 *
108 * @since 1.0.0
109 * @last 2.2.0
110 */
111 public function complete_button_cb($atts, $content = null, $tag = '') {
112 if ( ! is_user_logged_in() ) return; // should replace with button redirect to signup
113
114 $post_id = get_the_ID();
115 $button_id = '';
116 if ( isset( $atts['id'] ) && !empty( $atts['id'] ) ) {
117 $button_id = $atts['id'];
118 } else if ( isset( $atts['name'] ) && !empty( $atts['name'] ) ) {
119 $button_id = $atts['name'];
120 }
121 if ( isset( $atts['post_id'] ) && !empty( $atts['post_id'] ) ) {
122 $post_id = $atts['post_id'];
123 } else if ( isset( $atts['post'] ) && !empty( $atts['post'] ) ) {
124 $post_id = $atts['post'];
125 }
126
127 if ( ! in_array( get_post_type( $post_id ), $this->get_enabled_post_types() ) ) return;
128 if ( ! $this->post_can_complete( $post_id ) ) return;
129
130 $unique_button_id = $this->get_button_id($post_id, $button_id);
131
132 // Feature: Allow for custom button texts if it exists for this button:
133 $button_text = get_option($this->plugin_name . '_incomplete_text', 'Mark as complete');
134 if ( isset( $atts['text'] ) && !empty( $atts['text'] ) ) {
135 $button_text = $atts['text'];
136 }
137 $completed_button_text = get_option($this->plugin_name . '_completed_text', 'COMPLETED');
138 if ( isset( $atts['completed_text'] ) && !empty( $atts['completed_text'] ) ) {
139 $completed_button_text = $atts['completed_text'];
140 }
141
142 ob_start();
143 if (isset($atts['async'])) {
144 include 'partials/wpcomplete-public-loading-button.php';
145 } else {
146 // Start displaying button:
147 if ( $this->button_is_completed( $post_id, $button_id ) ) {
148 include 'partials/wpcomplete-public-completed-button.php';
149 } else {
150 // Let's make sure we track that this user has seen this button...
151 $user_activity = $this->get_user_activity();
152 if ( !isset( $user_activity[$unique_button_id] ) ) $user_activity[$unique_button_id] = array();
153 if ( !isset( $user_activity[$unique_button_id]['first_seen'] ) && !isset( $user_activity[$unique_button_id]['completed'] ) ) {
154 $user_activity[$unique_button_id]['first_seen'] = date('Y-m-d H:i:s');
155 $this->set_user_activity($user_activity);
156 }
157
158 include 'partials/wpcomplete-public-incomplete-button.php';
159 }
160 }
161
162 return ob_get_clean();
163 }
164
165 /**
166 * PREMIUM:
167 * Register the shortcode for [progress_in_percentage] for the public-facing side of the site.
168 *
169 * @since 1.0.0
170 */
171 public function progress_percentage_cb($atts, $content = null, $tag = '') {
172 if ( ! is_user_logged_in() ) return;
173 // normalize attribute keys, lowercase
174 $atts = array_change_key_case((array)$atts, CASE_LOWER);
175
176 // find the current course's
177 if ( ( !isset( $atts['course'] ) || empty( $atts['course'] ) ) && is_numeric( get_the_ID() ) ) {
178 if ( $post_course = $this->post_course( get_the_ID() ) ) $atts['course'] = $post_course;
179 }
180 $percentage = $this->get_percentage( $atts );
181
182 return '<span class="wpcomplete-progress-percentage ' . $this->get_course_class( $atts ) . '">' . $percentage . "%" . '</span>';
183 }
184
185 /**
186 * PREMIUM:
187 * Register the shortcode for [progress_ratio] for the public-facing side of the site.
188 *
189 * @since 1.0.0
190 */
191 public function progress_ratio_cb($atts, $content = null, $tag = '') {
192 if ( ! is_user_logged_in() ) return;
193 // normalize attribute keys, lowercase
194 $atts = array_change_key_case((array)$atts, CASE_LOWER);
195 // find the current course's
196 if ( ( !isset( $atts['course'] ) || empty( $atts['course'] ) ) && is_numeric( get_the_ID() ) ) {
197 if ( $post_course = $this->post_course( get_the_ID() ) ) $atts['course'] = $post_course;
198 }
199 $total_buttons = $this->get_buttons( $atts );
200 // Don't show chart if there's no data to populate it:
201 if ( count($total_buttons) <= 0 ) return;
202
203 $user_completed = $this->get_user_completed();
204
205 $completed_posts = array_intersect( $total_buttons, array_keys( $user_completed ) );
206
207 return '<span class="wpcomplete-progress-ratio ' . $this->get_course_class($atts) . '">' . count($completed_posts) . "/" . count($total_buttons) . '</span>';
208 }
209
210 /**
211 * Allow for async (ajax) loading of progress graphs...
212 *
213 * @since 2.2.0
214 * @last 2.2.0
215 */
216 public function get_graphs() {
217 $nonce = $_POST['_ajax_nonce'];
218 if ( empty( $_POST ) || !wp_verify_nonce( $nonce, 'completion' ) ) {
219 wp_send_json( array() );
220 exit;
221 }
222
223 $courses = $this->get_course_names();
224 $updates_to_sendback = array();
225 foreach ($courses as $course) {
226 $atts = array('course' => $course);
227
228 $updates_to_sendback['.wpcomplete-progress-ratio.' . $this->get_course_class($atts)] = $this->progress_ratio_cb( $atts );
229 $updates_to_sendback['.wpcomplete-progress-percentage.' . $this->get_course_class($atts)] = $this->progress_percentage_cb( $atts );
230
231 $percentage = $this->get_percentage( $atts );
232 ob_start();
233 include 'partials/wpcomplete-public-radial-graph.php';
234 $updates_to_sendback['.wpc-radial-loading.' . $this->get_course_class($atts)] = ob_get_clean();
235
236 ob_start();
237 include 'partials/wpcomplete-public-bar-graph.php';
238 $updates_to_sendback['.wpc-bar-loading.' . $this->get_course_class($atts)] = ob_get_clean();
239 }
240
241 $updates_to_sendback['.wpcomplete-progress-ratio.all-courses'] = $this->progress_ratio_cb( array('course' => 'all') );
242 $updates_to_sendback['.wpcomplete-progress-percentage.all-courses'] = $this->progress_percentage_cb( array('course' => 'all') );
243
244 $percentage = $this->get_percentage( array('course' => 'all') );
245 ob_start();
246 include 'partials/wpcomplete-public-radial-graph.php';
247 $updates_to_sendback['.wpc-radial-loading.all-courses'] = ob_get_clean();
248
249 ob_start();
250 include 'partials/wpcomplete-public-bar-graph.php';
251 $updates_to_sendback['.wpc-bar-loading.all-courses'] = ob_get_clean();
252
253 // Send back array of posts:
254 wp_send_json( $updates_to_sendback );
255 }
256
257 /**
258 * PREMIUM:
259 * Register the shortcode for [progress_graph] for the public-facing side of the site.
260 *
261 * @since 1.0.0
262 * @last 2.2.0
263 */
264 public function progress_radial_graph_cb($atts, $content = null, $tag = '') {
265 if ( ! is_user_logged_in() ) return;
266 // normalize attribute keys, lowercase
267 $atts = array_change_key_case((array)$atts, CASE_LOWER);
268 // find the current course's
269 if ( ( !isset( $atts['course'] ) || empty( $atts['course'] ) ) && is_numeric( get_the_ID() ) ) {
270 if ( $post_course = $this->post_course( get_the_ID() ) ) $atts['course'] = $post_course;
271 }
272
273 ob_start();
274 if (isset($atts['async'])) {
275 include 'partials/wpcomplete-public-loading-radial-graph.php';
276 } else {
277 $percentage = $this->get_percentage( $atts );
278
279 include 'partials/wpcomplete-public-radial-graph.php';
280 }
281 return ob_get_clean();
282 }
283
284 /**
285 * PREMIUM:
286 * Register the shortcode for [progress_graph] for the public-facing side of the site.
287 *
288 * @since 1.0.0
289 */
290 public function progress_bar_graph_cb($atts, $content = null, $tag = '') {
291 if ( ! is_user_logged_in() ) return;
292 // normalize attribute keys, lowercase
293 $atts = array_change_key_case((array)$atts, CASE_LOWER);
294 // find the current course's
295 if ( ( !isset( $atts['course'] ) || empty( $atts['course'] ) ) && is_numeric( get_the_ID() ) ) {
296 if ( $post_course = $this->post_course(get_the_ID()) ) $atts['course'] = $post_course;
297 }
298
299 ob_start();
300 if (isset($atts['async'])) {
301 include 'partials/wpcomplete-public-loading-bar-graph.php';
302 } else {
303 $percentage = $this->get_percentage( $atts );
304
305 include 'partials/wpcomplete-public-bar-graph.php';
306 }
307 return ob_get_clean();
308 }
309
310 /**
311 * PREMIUM:
312 * Add custom completion code to the end of post and page content
313 *
314 * @since 1.0.0
315 */
316 public function append_custom_styles() {
317 if (!is_user_logged_in()) return;
318 if (!WPCOMPLETE_IS_ACTIVATED) return;
319
320 $style_default = '
321 li .wpc-lesson-completed { opacity: .5; }
322 li .wpc-lesson-completed:after { content: "✔"; margin-left: 5px; }
323 ';
324
325 $complete_background = get_option( $this->plugin_name . '_incomplete_background', '#ff0000' );
326 $complete_color = get_option( $this->plugin_name . '_incomplete_color', '#ffffff' );
327 $completed_background = get_option( $this->plugin_name . '_completed_background', '#666666' );
328 $completed_color = get_option( $this->plugin_name . '_completed_color', '#ffffff' );
329 $graph_primary_color = get_option( $this->plugin_name . '_graph_primary', '#97a71d' );
330 $graph_secondary_color = get_option( $this->plugin_name . '_graph_secondary', '#ebebeb' );
331 $custom_styles = get_option( $this->plugin_name . '_custom_styles', $style_default );
332
333 echo "<style type=\"text/css\"> a.wpc-complete { background: $complete_background; color: $complete_color; } a.wpc-completed { background: $completed_background; color: $completed_color; } .wpc-radial-progress, .wpc-bar-progress .wpc-progress-track { background-color: $graph_secondary_color; } .wpc-radial-progress .wpc-fill, .wpc-bar-progress .wpc-progress-fill { background-color: $graph_primary_color; } .wpc-radial-progress .wpc-numbers, .wpc-bar-progress .wpc-numbers { color: $graph_primary_color; } .wpc-bar-progress[data-progress=\"75\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"76\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"77\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"78\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"79\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"80\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"81\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"82\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"83\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"84\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"85\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"86\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"87\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"88\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"89\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"90\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"91\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"92\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"93\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"94\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"95\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"96\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"97\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"98\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"99\"] .wpc-numbers, .wpc-bar-progress[data-progress=\"100\"] .wpc-numbers { color: $graph_secondary_color; } $custom_styles </style>";
334 }
335
336 /**
337 * Add custom completion code to the end of post and page content
338 *
339 * @since 1.0.0
340 */
341 public function append_completion_code($content) {
342 $post_type = get_post_type();
343
344 // Don't append if it's been disabled:
345 if ( get_option( $this->plugin_name . '_auto_append', 'true' ) == 'false' ) {
346 return $content;
347 }
348
349 // Don't append if we aren't suppose to complete this type of post:
350 if ( ! in_array( $post_type, $this->get_enabled_post_types() ) ) {
351 return $content;
352 }
353
354 // See if this post is actually completable:
355 if ( ! $this->post_can_complete(get_the_ID()) ) {
356 return $content;
357 }
358
359 // Only append to body if we can't find any record of the button anywhere on the content:
360 // NOTE: This doesn't fix the issue with OptimizePress... but it should help:
361 if ( ( strpos( get_the_content(), '[complete_button' ) === false ) && ( strpos( get_the_content(), '[wpc_complete_button' ) === false ) && ( strpos( get_the_content(), '[wpc_button' ) === false ) && is_main_query() ) {
362 if ( ( strpos( $content, '[complete_button' ) === false ) && ( strpos( $content, '[wpc_complete_button' ) === false ) && ( strpos( $content, '[wpc_button' ) === false ) && ( strpos( $content, 'class="wpc-button' ) === false ) ) {
363 $content .= "\n\n[wpc_complete_button]";
364 }
365 }
366
367 return $content;
368 }
369
370 /**
371 * Handle trying to mark a lesson as completed as a logged out user... should just redirect to login.
372 *
373 * @since 1.0.0
374 */
375 public function nopriv_mark_completed() {
376 $redirect = 'http' . ((isset($_SERVER['HTTPS']) && ('on' === $_SERVER['HTTPS'])) ? 's' : '') . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
377 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
378 // return something indicating that the page should redirect to login?
379 echo json_encode( array( 'redirect' => wp_login_url( $redirect ) ) );
380 die();
381 } else {
382 wp_redirect( wp_login_url( $redirect ) );
383 exit();
384 }
385 }
386
387 /**
388 * Handle marking a lesson as completed.
389 *
390 * @since 1.0.0
391 * @last 2.1.0
392 */
393 public function mark_completed() {
394 //check_ajax_referer( 'completion' );
395
396 // Get any existing lessons this user has completed:
397 $user_completed = $this->get_user_activity();
398
399 $unique_button_id = $_REQUEST['button'];
400 list($post_id, $button_id) = $this->extract_button_info($unique_button_id);
401
402 $course = $this->post_course($post_id);
403
404 $posts = $this->get_completable_posts();
405 if ( isset( $button_id ) && ( !isset( $posts[$post_id]['buttons'] ) || !in_array( $unique_button_id, $posts[$post_id]['buttons'] ) ) ) {
406 $post_meta = $posts[$post_id];
407 if ( !isset( $post_meta['buttons'] ) ) $post_meta['buttons'] = array();
408 $post_meta['buttons'][] = $unique_button_id;
409 $posts[ $post_id ] = $post_meta;
410 // Save changes:
411 update_post_meta( $post_id, 'wpcomplete', json_encode( $post_meta, JSON_UNESCAPED_UNICODE ) );
412 wp_cache_set( "posts", json_encode( $posts, JSON_UNESCAPED_UNICODE ), 'wpcomplete' );
413 }
414
415 // Mark this button as completed:
416 if ( ! isset( $user_completed[ $unique_button_id ] ) ) $user_completed[ $unique_button_id ] = array();
417 $user_completed[ $unique_button_id ]['completed'] = date('Y-m-d H:i:s');
418
419 // Save to database/cache:
420 $this->set_user_activity($user_completed);
421
422 // Feature: Allow for custom button texts if it exists for this button:
423 $button_text = get_option($this->plugin_name . '_incomplete_text', 'Mark as complete');
424 if ( isset( $_POST['old_button_text'] ) && !empty( $_POST['old_button_text'] ) ) {
425 $button_text = $_POST['old_button_text'];
426 }
427 $completed_button_text = get_option($this->plugin_name . '_completed_text', 'COMPLETED');
428 if ( isset( $_POST['new_button_text'] ) && !empty( $_POST['new_button_text'] ) ) {
429 $completed_button_text = $_POST['new_button_text'];
430 }
431
432 // update the button
433 $updates_to_sendback = array(
434 ('.wpc-button-' . $this->get_button_class( $unique_button_id )) => $this->complete_button_cb( array( 'post_id' => $post_id, 'name' => $button_id, 'text' => $button_text, 'completed_text' => $completed_button_text ) )
435 );
436 $updates_to_sendback['fetch'] = 'true';
437
438 $post_status = $this->post_completion_status( $post_id );
439
440 //sleep ( rand ( 0, 2 ) );
441
442 // PREMIUM: redirect student if teacher has added redirect url:
443 if (WPCOMPLETE_IS_ACTIVATED) {
444 // PREMIUM: get info for progress percentage:
445 $atts = array();
446 if ( $course ) {
447 $atts['course'] = $course;
448 }
449 // Add lesson indicators
450 $updates_to_sendback['lesson-' . $post_status] = $post_id;
451
452 // Toggle content blocks:
453 $updates_to_sendback['.wpc-content-button-' . $this->get_button_class( $unique_button_id ) . '-completed'] = 'show';
454 $updates_to_sendback['.wpc-content-button-' . $this->get_button_class( $unique_button_id ) . '-incomplete'] = 'hide';
455 $updates_to_sendback['wpc-button-completed'] = 'trigger';
456 $updates_to_sendback['wpc-button-completed::' . $unique_button_id ] = 'trigger';
457 if ( $this->post_completion_status($post_id) == 'completed' ) {
458 $updates_to_sendback['.wpc-content-page-' . $post_id . '-completed'] = 'show';
459 $updates_to_sendback['.wpc-content-page-' . $post_id . '-incomplete'] = 'hide';
460 $updates_to_sendback['wpc-page-completed'] = 'trigger';
461 $updates_to_sendback['wpc-page-completed::' . $post_id ] = 'trigger';
462 }
463 if ( $this->course_completion_status($course) == 'completed' ) {
464 $updates_to_sendback['.wpc-content-course-' . $this->get_course_class( $course ) . '-completed'] = 'show';
465 $updates_to_sendback['.wpc-content-course-' . $this->get_course_class( $course ) . '-incomplete'] = 'hide';
466 $updates_to_sendback['wpc-course-completed'] = 'trigger';
467 $updates_to_sendback['wpc-course-completed::' . $course ] = 'trigger';
468 }
469
470 // Update premium feature widgets:
471 $updates_to_sendback['.wpcomplete-progress-ratio.' . $this->get_course_class($atts)] = $this->progress_ratio_cb( $atts );
472 $updates_to_sendback['.wpcomplete-progress-percentage.' . $this->get_course_class($atts)] = $this->progress_percentage_cb( $atts );
473 $updates_to_sendback['.wpcomplete-progress-ratio.all-courses'] = $this->progress_ratio_cb( array('course' => 'all') );
474 $updates_to_sendback['.wpcomplete-progress-percentage.all-courses'] = $this->progress_percentage_cb( array('course' => 'all') );
475 $updates_to_sendback['.' . $this->get_course_class($atts) . '[data-progress]'] = $this->get_percentage($atts);
476 $updates_to_sendback['.all-courses[data-progress]'] = $this->get_percentage(array('course' => 'all') );
477 $updates_to_sendback['peer-pressure'] = $this->get_peer_pressure_stats($post_id);
478
479 // Add redirect if needed:
480 $posts = $this->get_completable_posts();
481 if ( ( $post_status == 'completed' ) && isset( $posts[ $post_id ] ) && isset( $posts[ $post_id ]['redirect'] ) ) {
482 $redirect = $posts[ $post_id ]['redirect'];
483
484 if ($redirect['url'] && !empty($redirect['url'])) {
485 $updates_to_sendback['redirect'] = $redirect['url'];
486 } else if (strpos($redirect['title'], 'http') === 0) {
487 $updates_to_sendback['redirect'] = $redirect['title'];
488 }
489 }
490 }
491
492 // Add action for other plugins to hook in:
493 do_action( 'wpcomplete_mark_completed', array( 'user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
494 do_action( 'wpcomplete_button_completed', array( 'user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
495 if ( $this->post_completion_status($post_id) == 'completed' ) {
496 do_action( 'wpcomplete_page_completed', array('user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
497 }
498 if ( $this->course_completion_status($course) == 'completed' ) {
499 do_action( 'wpcomplete_course_completed', array('user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
500 }
501
502 if (defined('DOING_AJAX') && DOING_AJAX) {
503 echo json_encode( $updates_to_sendback, JSON_UNESCAPED_UNICODE );
504 die();
505 } else {
506 if ( wp_get_referer() ) {
507 wp_safe_redirect( wp_get_referer() );
508 } else {
509 wp_safe_redirect( get_home_url() );
510 }
511 }
512 }
513
514 /**
515 * Handle trying to mark a lesson as incomplete as a logged out user... should just redirect to login.
516 *
517 * @since 1.0.0
518 */
519 public function nopriv_mark_uncompleted() {
520 $redirect = 'http' . ((isset($_SERVER['HTTPS']) && ('on' === $_SERVER['HTTPS'])) ? 's' : '') . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
521 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
522 // return something indicating that the page should redirect to login?
523 echo json_encode( array( 'redirect' => wp_login_url( $redirect ) ) );
524 die();
525 } else {
526 wp_redirect( wp_login_url( $redirect ) );
527 exit();
528 }
529 }
530
531 /**
532 * Handle mark a lesson as incomplete.
533 *
534 * @since 1.0.0
535 * @last 2.1.0
536 */
537 public function mark_uncompleted() {
538 //check_ajax_referer( 'completion' );
539
540 // Get any existing lessons this user has completed:
541 $user_completed = $this->get_user_activity();
542
543 // Get any existing lessons this user has completed:
544 $unique_button_id = $_REQUEST['button'];
545 list($post_id, $button_id) = $this->extract_button_info($unique_button_id);
546
547 $course = $this->post_course($post_id);
548 $previous_post_status = $this->post_completion_status($post_id);
549 $previous_course_status = $this->course_completion_status($course);
550
551 // Remove this post id:
552 if ( ! isset( $user_completed[ $unique_button_id ] ) ) $user_completed[ $unique_button_id ] = array();
553 unset($user_completed[ $unique_button_id ]['completed']);
554
555 // and update the meta storage values:
556 $this->set_user_activity($user_completed);
557
558 // Feature: Allow for custom button texts if it exists for this button:
559 $button_text = get_option($this->plugin_name . '_incomplete_text', 'Mark as complete');
560 if ( isset( $_POST['new_button_text'] ) && !empty( $_POST['new_button_text'] ) ) {
561 $button_text = $_POST['new_button_text'];
562 }
563 $completed_button_text = get_option($this->plugin_name . '_completed_text', 'COMPLETED');
564 if ( isset( $_POST['old_button_text'] ) && !empty( $_POST['old_button_text'] ) ) {
565 $completed_button_text = $_POST['old_button_text'];
566 }
567
568 $updates_to_sendback = array(
569 ('.wpc-button-' . $this->get_button_class( $unique_button_id )) => $this->complete_button_cb( array( 'post_id' => $post_id, 'name' => $button_id, 'text' => $button_text, 'completed_text' => $completed_button_text ) )
570 );
571 $updates_to_sendback['fetch'] = 'true';
572
573 //sleep ( rand ( 0, 2 ) );
574
575 // PREMIUM:
576 if (WPCOMPLETE_IS_ACTIVATED) {
577 // get info for progress percentage:
578 $atts = array();
579 if ( $course ) {
580 $atts['course'] = $course;
581 }
582 // Add lesson indicators:
583 $updates_to_sendback['lesson-' . $this->post_completion_status( $post_id )] = $post_id;
584
585 // Toggle content blocks:
586 $updates_to_sendback['.wpc-content-button-' . $this->get_button_class( $unique_button_id ) . '-incomplete'] = 'show';
587 $updates_to_sendback['.wpc-content-button-' . $this->get_button_class( $unique_button_id ) . '-completed'] = 'hide';
588 $updates_to_sendback['.wpc-content-page-' . $post_id . '-incomplete'] = 'show';
589 $updates_to_sendback['.wpc-content-page-' . $post_id . '-completed'] = 'hide';
590 $updates_to_sendback['.wpc-content-course-' . $this->get_course_class( $course ) . '-incomplete'] = 'show';
591 $updates_to_sendback['.wpc-content-course-' . $this->get_course_class( $course ) . '-completed'] = 'hide';
592
593 $updates_to_sendback['.wpcomplete-progress-ratio.' . $this->get_course_class($atts)] = $this->progress_ratio_cb( $atts );
594 $updates_to_sendback['.wpcomplete-progress-percentage.' . $this->get_course_class($atts)] = $this->progress_percentage_cb( $atts );
595 $updates_to_sendback['.wpcomplete-progress-ratio.all-courses'] = $this->progress_ratio_cb( array('course' => 'all') );
596 $updates_to_sendback['.wpcomplete-progress-percentage.all-courses'] = $this->progress_percentage_cb( array('course' => 'all') );
597 $updates_to_sendback['.' . $this->get_course_class($atts) . '[data-progress]'] = $this->get_percentage($atts);
598 $updates_to_sendback['.all-courses[data-progress]'] = $this->get_percentage(array('course' => 'all') );
599 $updates_to_sendback['peer-pressure'] = $this->get_peer_pressure_stats($post_id);
600 }
601
602 // Add action for other plugins to hook in:
603 do_action( 'wpcomplete_mark_incomplete', array('user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
604 do_action( 'wpcomplete_button_uncompleted', array( 'user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
605 if ( $previous_post_status == 'completed' ) {
606 do_action( 'wpcomplete_page_uncompleted', array('user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
607 }
608 if ( $previous_course_status == 'completed' ) {
609 do_action( 'wpcomplete_course_uncompleted', array('user_id' => get_current_user_id(), 'post_id' => $post_id, 'button_id' => $unique_button_id, 'course' => $course ) );
610 }
611
612 if (defined('DOING_AJAX') && DOING_AJAX) {
613 // Send back new button:
614 echo json_encode( $updates_to_sendback, JSON_UNESCAPED_UNICODE );
615 die();
616 } else {
617 if ( wp_get_referer() ) {
618 wp_safe_redirect( wp_get_referer() );
619 } else {
620 wp_safe_redirect( get_home_url() );
621 }
622 }
623 }
624
625 /**
626 * Returns an array of all wordpress posts that are "completable".
627 *
628 * @since 1.2.0
629 * @last 2.2.0
630 */
631 public function get_completable_list() {
632 $nonce = $_POST['_ajax_nonce'];
633 if ( empty( $_POST ) || !wp_verify_nonce( $nonce, 'completion' ) ) {
634 wp_send_json( array() );
635 exit;
636 }
637
638 $updates_to_sendback = array();
639
640 if ( get_current_user_id() > 0 ) {
641 $user_id = get_current_user_id();
642 $updates_to_sendback['timestamp'] = time();
643 $updates_to_sendback['user'] = $user_id;
644 $total_posts = $this->get_completable_posts();
645 $user_completed = $this->get_user_completed();
646 foreach ( $total_posts as $post_id => $value ) {
647 $status = $this->post_completion_status( $post_id, $user_id, $value, $user_completed );
648 $updates_to_sendback[ get_permalink( $post_id ) ] = array(
649 'id' => $post_id,
650 'status' => $status,
651 'completed' => ( $status == 'completed' ) ? true : false
652 );
653 }
654 }
655 // Send back array of posts:
656 wp_send_json( $updates_to_sendback );
657 }
658
659 /**
660 * PREMIUM:
661 * Handles the [wpc_completed_content] or [wpc_if_completed] shortcodes
662 *
663 * @since 2.0.0
664 * @last 2.1.0
665 */
666 public function completed_content_cb($atts, $content = null, $tag = '') {
667 if ( isset( $atts['course'] ) && !empty( $atts['course'] ) ) {
668 return $this->if_course_completed_cb($atts, $content, $tag);
669 } else if ( isset( $atts['page'] ) && !empty( $atts['page'] ) ) {
670 return $this->if_page_completed_cb($atts, $content, $tag);
671 } else {
672 return $this->if_button_completed_cb($atts, $content, $tag);
673 }
674 }
675
676 /**
677 * PREMIUM:
678 * Handles the [wpc_incomplete_content] or [wpc_if_incomplete] shortcodes
679 *
680 * @since 2.0.0
681 * @last 2.1.0
682 */
683 public function incomplete_content_cb($atts, $content = null, $tag = '') {
684 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
685 if ( isset( $atts['course'] ) ) {
686 return $this->if_course_incomplete_cb($atts, $content, $tag);
687 } else if ( isset( $atts['page'] ) ) {
688 return $this->if_page_incomplete_cb($atts, $content, $tag);
689 } else {
690 return $this->if_button_incomplete_cb($atts, $content, $tag);
691 }
692 }
693
694 /**
695 * PREMIUM:
696 * Handles the [wpc_if_button_completed] or [wpc_if_completed] shortcodes
697 *
698 * @since 2.1.0
699 * @last 2.1.0
700 */
701 public function if_button_completed_cb($atts, $content = null, $tag = '') {
702 if ( ! is_user_logged_in() ) return; // dont show conditional content for logged out users
703 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
704
705 $post_id = get_the_ID();
706 $button_id = '';
707 if ( isset( $atts['id'] ) && !empty( $atts['id'] ) ) {
708 if ( is_numeric($atts['id']) ) {
709 $post_id = $atts['id'];
710 } else {
711 $button_id = $atts['id'];
712 }
713 } else if ( isset( $atts['name'] ) && !empty( $atts['name'] ) ) {
714 $button_id = $atts['name'];
715 } else if ( isset( $atts['button'] ) && !empty( $atts['button'] ) ) {
716 $button_id = $atts['button'];
717 }
718 if ( isset( $atts['post'] ) && !empty( $atts['post'] ) ) {
719 $post_id = $atts['post'];
720 } else if ( isset( $atts['post_id'] ) && !empty( $atts['post_id'] ) ) {
721 $post_id = $atts['post_id'];
722 }
723
724 // dont show conditional content if post isn't completable
725 if ( ! in_array( get_post_type( $post_id ), $this->get_enabled_post_types() ) ) return;
726 if ( ! $this->post_can_complete( $post_id ) ) return;
727
728 $unique_button_id = $this->get_button_id($post_id, $button_id);
729 $completion_status = 'completed';
730
731 $user_completed = $this->get_user_completed();
732 $should_hide = isset($atts['async']) || !isset( $user_completed[ $unique_button_id ] );
733
734 ob_start();
735 include 'partials/wpcomplete-public-content-button.php';
736 return ob_get_clean();
737 }
738
739 /**
740 * PREMIUM:
741 * Handles the [wpc_if_page_completed] or [wpc_if_completed page=""] shortcodes
742 *
743 * @since 2.1.0
744 * @last 2.1.0
745 */
746 public function if_page_completed_cb($atts, $content = null, $tag = '') {
747 if ( ! is_user_logged_in() ) return; // dont show conditional content for logged out users
748 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
749
750 $post_id = get_the_ID();
751 if ( isset( $atts['page'] ) && !empty( $atts['page'] ) ) {
752 $post_id = $atts['page'];
753 } else if ( isset( $atts['page_id'] ) && !empty( $atts['page_id'] ) ) {
754 $post_id = $atts['page_id'];
755 } else if ( isset( $atts['post'] ) && !empty( $atts['post'] ) ) {
756 $post_id = $atts['post'];
757 } else if ( isset( $atts['post_id'] ) && !empty( $atts['post_id'] ) ) {
758 $post_id = $atts['post_id'];
759 }
760
761 // dont show conditional content if post isn't completable
762 if ( ! in_array( get_post_type( $post_id ), $this->get_enabled_post_types() ) ) return;
763 if ( ! $this->post_can_complete( $post_id ) ) return;
764
765 $completion_status = 'completed';
766 $should_hide = isset($atts['async']) || ( $this->post_completion_status($post_id) != 'completed' );
767
768 ob_start();
769 include 'partials/wpcomplete-public-content-page.php';
770 return ob_get_clean();
771 }
772
773 /**
774 * PREMIUM:
775 * Handles the [wpc_if_course_completed] or [wpc_if_completed course=""] shortcodes
776 *
777 * @since 2.1.0
778 * @last 2.1.0
779 */
780 public function if_course_completed_cb($atts, $content = null, $tag = '') {
781 if ( ! is_user_logged_in() ) return; // dont show conditional content for logged out users
782 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
783
784 if ( isset( $atts['course'] ) && !empty( $atts['course'] ) ) {
785 $course = $atts['course'];
786 } else {
787 $course = $this->post_course( get_the_ID() );
788 }
789
790 $completion_status = 'completed';
791 $should_hide = isset($atts['async']) || ( $this->course_completion_status($course) != 'completed' );
792
793 ob_start();
794 include 'partials/wpcomplete-public-content-course.php';
795 return ob_get_clean();
796 }
797
798 /**
799 * PREMIUM:
800 * Handles the [wpc_if_incomplete] or [wpc_if_button_incomplete] shortcodes
801 *
802 * @since 2.1.0
803 * @last 2.1.0
804 */
805 public function if_button_incomplete_cb($atts, $content = null, $tag = '') {
806 if ( ! is_user_logged_in() ) return; // dont show conditional content for logged out users
807 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
808
809 $post_id = get_the_ID();
810 $button_id = '';
811 if ( isset( $atts['id'] ) && !empty( $atts['id'] ) ) {
812 if ( is_numeric($atts['id']) ) {
813 $post_id = $atts['id'];
814 } else {
815 $button_id = $atts['id'];
816 }
817 } else if ( isset( $atts['name'] ) && !empty( $atts['name'] ) ) {
818 $button_id = $atts['name'];
819 }
820 if ( isset( $atts['post'] ) && !empty( $atts['post'] ) ) {
821 $post_id = $atts['post'];
822 } else if ( isset( $atts['post_id'] ) && !empty( $atts['post_id'] ) ) {
823 $post_id = $atts['post_id'];
824 }
825
826 // dont show conditional content if post isn't completable
827 if ( ! in_array( get_post_type( $post_id ), $this->get_enabled_post_types() ) ) return;
828 if ( ! $this->post_can_complete( $post_id ) ) return;
829
830 $unique_button_id = $this->get_button_id($post_id, $button_id);
831 $completion_status = 'incomplete';
832
833 $user_completed = $this->get_user_completed();
834 $should_hide = isset($atts['async']) || isset( $user_completed[ $unique_button_id ] );
835
836 ob_start();
837 include 'partials/wpcomplete-public-content-button.php';
838 return ob_get_clean();
839 }
840
841 /**
842 * PREMIUM:
843 * Handles the [wpc_if_page_incomplete] or [wpc_if_incomplete page=""] shortcodes
844 *
845 * @since 2.1.0
846 * @last 2.1.0
847 */
848 public function if_page_incomplete_cb($atts, $content = null, $tag = '') {
849 if ( ! is_user_logged_in() ) return; // dont show conditional content for logged out users
850 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
851
852 $post_id = get_the_ID();
853 if ( isset( $atts['page'] ) && !empty( $atts['page'] ) ) {
854 $post_id = $atts['page'];
855 } else if ( isset( $atts['page_id'] ) && !empty( $atts['page_id'] ) ) {
856 $post_id = $atts['page_id'];
857 } else if ( isset( $atts['post'] ) && !empty( $atts['post'] ) ) {
858 $post_id = $atts['post'];
859 } else if ( isset( $atts['post_id'] ) && !empty( $atts['post_id'] ) ) {
860 $post_id = $atts['post_id'];
861 }
862
863 // dont show conditional content if post isn't completable
864 if ( ! in_array( get_post_type( $post_id ), $this->get_enabled_post_types() ) ) return;
865 if ( ! $this->post_can_complete( $post_id ) ) return;
866
867 $completion_status = 'incomplete';
868 $should_hide = isset($atts['async']) || ( $this->post_completion_status($post_id) == 'completed' );
869
870 ob_start();
871 include 'partials/wpcomplete-public-content-page.php';
872 return ob_get_clean();
873 }
874
875 /**
876 * PREMIUM:
877 * Handles the [wpc_if_course_incomplete] or [wpc_if_incomplete course=""] shortcodes
878 *
879 * @since 2.1.0
880 * @last 2.1.0
881 */
882 public function if_course_incomplete_cb($atts, $content = null, $tag = '') {
883 if ( ! is_user_logged_in() ) return; // dont show conditional content for logged out users
884 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
885
886 if ( isset( $atts['course'] ) && !empty( $atts['course'] ) ) {
887 $course = $atts['course'];
888 } else {
889 $course = $this->post_course( get_the_ID() );
890 }
891
892 $completion_status = 'incomplete';
893 $should_hide = isset($atts['async']) || ( $this->course_completion_status($course) == 'completed' );
894
895 ob_start();
896 include 'partials/wpcomplete-public-content-course.php';
897 return ob_get_clean();
898 }
899
900 /**
901 * Allow for async (ajax) loading of conditional content blocks...
902 *
903 * @since 2.3.0
904 * @last 2.3.0
905 */
906 public function get_content() {
907 $nonce = $_POST['_ajax_nonce'];
908 if ( empty( $_POST ) || !wp_verify_nonce( $nonce, 'completion' ) ) {
909 wp_send_json( array() );
910 exit;
911 }
912
913 $updates_to_sendback = array();
914
915 if ( $_POST['type'] == 'button' ) {
916 $unique_button_id = $_POST['unique_id'];
917 $user_completed = $this->get_user_completed();
918 $is_completed = isset( $user_completed[ $unique_button_id ] );
919 $updates_to_sendback[".wpc-content-button-" . $this->get_button_class( $unique_button_id ) . "-completed"] = ($is_completed) ? 'show' : 'hide';
920 $updates_to_sendback[".wpc-content-button-" . $this->get_button_class( $unique_button_id ) . "-incomplete"] = (!$is_completed) ? 'show' : 'hide';
921 } else if ( $_POST['type'] == 'page' ) {
922 $post_id = $_POST['unique_id'];
923 $is_completed = ( $this->post_completion_status($post_id) == 'completed' );
924 $updates_to_sendback[".wpc-content-page-" . $post_id . "-completed"] = ($is_completed) ? 'show' : 'hide';
925 $updates_to_sendback[".wpc-content-page-" . $post_id . "-incomplete"] = (!$is_completed) ? 'show' : 'hide';
926 } else if ( $_POST['type'] == 'course' ) {
927 $course = $_POST['unique_id'];
928 $is_completed = ( $this->course_completion_status($course) == 'completed' );
929 $updates_to_sendback[".wpc-content-course-" . $this->get_course_class( $course ) . "-completed"] = ($is_completed) ? 'show' : 'hide';
930 $updates_to_sendback[".wpc-content-course-" . $this->get_course_class( $course ) . "-incomplete"] = (!$is_completed) ? 'show' : 'hide';
931 }
932
933 wp_send_json( $updates_to_sendback );
934 }
935
936 /**
937 * PREMIUM:
938 * Handles the [wpc_list_completable] or [wpc_list_pages] shortcodes
939 *
940 * @since 2.2.0
941 * @last 2.2.0
942 */
943 public function list_completable_shortcode($atts, $content = null, $tag = '') {
944 $args = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
945
946 $defaults = array(
947 'depth' => 0,
948 'show_date' => '',
949 'date_format' => get_option( 'date_format' ),
950 'child_of' => 0,
951 'exclude' => '',
952 'include' => '',
953 'title_li' => '',
954 'echo' => 1,
955 'authors' => '',
956 'sort_column' => 'menu_order, post_title',
957 'link_before' => '',
958 'link_after' => '',
959 'item_spacing' => 'preserve',
960 'walker' => '',
961 );
962
963 $completable_posts = $this->get_completable_posts();
964 $course = 'all';
965 if (isset($args['course']) && !empty($args['course'])) {
966 $course = $args['course'];
967 }
968
969 $post_ids = array();
970 foreach ($completable_posts as $post_id => $post) {
971 if ( !isset( $post['course'] ) || ( $post['course'] === 'true' ) ) $post['course'] = get_bloginfo('name');
972
973 if (strtolower($course) == 'all') { // All posts on entire site
974 $post_ids[] = ''.$post_id;
975 // Specific course:
976 } else if ( strtolower( $post['course'] ) == strtolower( $course ) ) {
977 $post_ids[] = ''.$post_id;
978 }
979 }
980
981 if (isset($args['child_of'])) {
982 $child_pages_ids = array();
983 // look through all content types (really only handles pages + posts)
984 foreach ($this->get_enabled_post_types() as $key => $type) {
985 // if we have a requested child_of, grab all the possible ids of the children...
986 if ( $child_pages = get_pages( array( 'child_of' => $args['child_of'], 'post_type' => $type ) ) ) {
987 // map just IDs...
988 foreach ($child_pages as $p) { $child_pages_ids[] = "".$p->ID; }
989 }
990 }
991 // then get rid of any that aren't completable...
992 $post_ids = array_intersect( $child_pages_ids, $post_ids );
993 //var_dump($post_ids);
994 unset($args['child_of']);
995 }
996
997 if ( !empty($args['include']) ) {
998 $args['include'] = array_intersect( $post_ids, explode( ',', $args['include'] ) );
999 } else {
1000 $args['include'] = $post_ids;
1001 }
1002
1003 if ( empty( $args['include'] ) ) {
1004 return '<!-- No available pages found. -->';
1005 }
1006
1007 $args['include'] = join(',', $args['include']);
1008
1009 $args['post_type'] = array_values( $this->get_enabled_post_types() );
1010
1011 if (isset($args['sort_column']) && !isset($args['orderby'])) {
1012 $args['orderby'] = $args['sort_column'];
1013 }
1014
1015 $r = wp_parse_args( $args, $defaults );
1016
1017 if ( ! in_array( $r['item_spacing'], array( 'preserve', 'discard' ), true ) ) {
1018 // invalid value, fall back to default.
1019 $r['item_spacing'] = $defaults['item_spacing'];
1020 }
1021
1022 $output = '';
1023 $current_page = 0;
1024
1025 // sanitize, mostly to keep spaces out
1026 $r['exclude'] = preg_replace( '/[^0-9,]/', '', $r['exclude'] );
1027
1028 // Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array)
1029 $exclude_array = ( $r['exclude'] ) ? explode( ',', $r['exclude'] ) : array();
1030
1031 /**
1032 * Filters the array of pages to exclude from the pages list.
1033 *
1034 * @since 2.1.0
1035 *
1036 * @param array $exclude_array An array of page IDs to exclude.
1037 */
1038 $r['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', $exclude_array ) );
1039
1040 if (count( explode( ',', $r['exclude'] ) ) > 0) {
1041 $r['include'] = join( ',', array_diff( explode( ',', $r['include'] ), explode( ',', $r['exclude'] ) ) );
1042 }
1043
1044 // Query pages.
1045 $r['hierarchical'] = 0;
1046
1047 $pages = get_posts( $r );
1048
1049 ob_start();
1050
1051 if ( ! empty( $pages ) ) {
1052 $output .= '<ul class="wpc-list">';
1053 global $wp_query;
1054 if ( is_page() || is_attachment() || $wp_query->is_posts_page ) {
1055 $current_page = get_queried_object_id();
1056 } elseif ( is_singular() ) {
1057 $queried_object = get_queried_object();
1058 if ( is_post_type_hierarchical( $queried_object->post_type ) ) {
1059 $current_page = $queried_object->ID;
1060 }
1061 }
1062
1063 $output .= walk_page_tree( $pages, $r['depth'], $current_page, $r );
1064
1065 $output .= '</ul>';
1066 }
1067
1068 $html = apply_filters( 'wp_list_pages', $output, $r, $pages );
1069
1070 echo $html;
1071 return ob_get_clean();
1072 }
1073
1074 /**
1075 * PREMIUM:
1076 * Handles the [wpc_peer_pressure] shortcode
1077 * Can supply: zero, single, plural, completed attributes.
1078 *
1079 * @since 2.2.0
1080 * @last 2.2.0
1081 */
1082 public function peer_pressure_shortcode($atts = array(), $content = null, $tag = '') {
1083 #if ( ! is_user_logged_in() ) return; // dont show for logged out users
1084 $atts = array_change_key_case((array)$atts, CASE_LOWER); // normalize attribute keys, lowercase
1085
1086 $post_id = get_the_ID();
1087 if ( isset( $atts['page'] ) && !empty( $atts['page'] ) ) {
1088 $post_id = $atts['page'];
1089 } else if ( isset( $atts['page_id'] ) && !empty( $atts['page_id'] ) ) {
1090 $post_id = $atts['page_id'];
1091 } else if ( isset( $atts['post'] ) && !empty( $atts['post'] ) ) {
1092 $post_id = $atts['post'];
1093 } else if ( isset( $atts['post_id'] ) && !empty( $atts['post_id'] ) ) {
1094 $post_id = $atts['post_id'];
1095 }
1096
1097 // dont show if post isn't completable
1098 if ( ! in_array( get_post_type( $post_id ), $this->get_enabled_post_types() ) ) return;
1099 if ( ! $this->post_can_complete( $post_id ) ) return;
1100
1101 $zero = 'Be the first to complete this!';
1102 if ( isset( $atts['zero'] ) ) $empty = $atts['zero'];
1103 if ($zero == 'false') $zero = '';
1104 $single = "1 person has already completed this!";
1105 if ( isset( $atts['single'] ) && !empty( $atts['single'] ) ) $single = $atts['single'];
1106 $plural = "{number} people have already completed this!";
1107 if ( isset( $atts['plural'] ) && !empty( $atts['plural'] ) ) $plural = $atts['plural'];
1108 $completed = "Yay, you've completed this!";
1109 if ( isset( $atts['completed'] ) ) $completed = $atts['completed'];
1110 if ($completed == 'false') $completed = '';
1111
1112 $stats = $this->get_peer_pressure_stats($post_id);
1113
1114 ob_start();
1115
1116 if ($stats === false) {
1117 $output = '<!-- Tracking user count is 0 -->';
1118 } else {
1119 $output = '';
1120 $output .= '<div class="wpc-peer-pressure wpc-peer-pressure-' . $post_id . '" data-zero="' . $zero . '" data-single="' . $single . '" data-plural="' . $plural . '" data-completed="' . $completed . '">';
1121
1122 // 2. if any, run $copy through a replace and display that.
1123 if ( $stats['user_completed'] ) {
1124 $copy = $completed;
1125 } else if ( $stats['{number}'] > 0 ) {
1126 $copy = ($stats['{number}'] == 1) ? $single : $plural;
1127 } else {
1128 // 3. if there are zero, just display empty text.
1129 $copy = $zero;
1130 }
1131
1132 $copy = str_ireplace(
1133 array( '{number}', '{percentage}', '{next_with_ordinal}' ),
1134 array(
1135 $stats['{number}'],
1136 $stats['{percentage}'],
1137 $stats['{next_with_ordinal}']
1138 ),
1139 $copy
1140 );
1141 $output .= $copy;
1142
1143 $output .= '</div>';
1144 }
1145
1146 echo $output;
1147 return ob_get_clean();
1148 }
1149
1150 /**
1151 * PREMIUM:
1152 * Helper for peer pressure short code.
1153 * Accepts the post_id and the user_id (defaults to current logged in user)
1154 *
1155 * @since 2.2.0
1156 * @last 2.2.0
1157 */
1158 public function get_peer_pressure_stats($post_id, $user_id = false) {
1159 global $wpdb;
1160
1161 if ( !$user_id ) $user_id = get_current_user_id();
1162
1163 // 1. grab the total number of users completed for this post
1164 $selected_role = get_option( $this->plugin_name . '_role', 'subscriber' );
1165 // Get all users that are able to complete the post:
1166 $args = array('fields' => 'ID');
1167 if ($selected_role != 'all') $args['role'] = $selected_role;
1168 $args['meta_key'] = 'wpcomplete';
1169 $total_users = get_users($args);
1170
1171 // Bail if we don't have any users to track.
1172 if (count($total_users) < 1) return false;
1173
1174 $user_ids = join( ",", $total_users );
1175 $SQL = "SELECT b.user_id, b.meta_value FROM $wpdb->usermeta b WHERE ( b.meta_key = 'wpcomplete' ) AND ( b.user_id IN ($user_ids) )";
1176
1177 $total_users_meta = $wpdb->get_results( $SQL, ARRAY_A );
1178
1179 $is_completed_by_user = false;
1180
1181 $user_completed = array();
1182 foreach ($total_users_meta as $user) {
1183 $user_completed_json = $user['meta_value'];
1184 $user_completed_raw = ( $user_completed_json ) ? json_decode( $user_completed_json, true ) : array();
1185
1186 //$user_completed_raw = $this->get_user_completed( $user->ID );
1187 if ( isset( $user_completed_raw[$post_id] ) && isset( $user_completed_raw[$post_id]['completed'] ) ) {
1188 if ($user_completed_raw[$post_id]['completed'] === true) {
1189 $user_completed_raw[$post_id]['completed'] = 'Yes';
1190 }
1191 $user_completed[$user['user_id']] = $user_completed_raw[$post_id]['completed'];
1192 if ($user['user_id'] == $user_id) $is_completed_by_user = true;
1193 }
1194 }
1195 // just double check to see if user has confirmed but isn't the right user type...
1196 if ($user_id && !$is_completed_by_user) {
1197 $user_completed_raw = $this->get_user_completed();
1198 if ( isset( $user_completed_raw[$post_id] ) && isset( $user_completed_raw[$post_id]['completed'] ) ) {
1199 $is_completed_by_user = true;
1200 }
1201 }
1202
1203 $count = count($user_completed);
1204 $ord = array('st', 'nd', 'rd');
1205
1206 $stats = array(
1207 'post_id' => $post_id,
1208 'user_completed' => $is_completed_by_user,
1209 '{number}' => $count,
1210 '{percentage}' => (round(100 * ( $count / count($total_users) ), 0) . '%'),
1211 '{next_with_ordinal}' => (($count+1).((($j=abs($count+1)%100)>10&&$j<14) ? 'th' : ((($j%=10)>0&&$j<4) ? $ord[$j-1] : 'th')))
1212 );
1213
1214 return $stats;
1215 }
1216
1217 }
1218