1: <?php
2: if ( ! defined( 'ABSPATH' ) ) exit;
3:
4: 5: 6: 7: 8: 9: 10: 11: 12:
13: class Sensei_Updates
14: {
15: public $token = 'woothemes-sensei';
16: public $version;
17: public $updates_run;
18: public $updates;
19: private $parent;
20:
21: 22: 23: 24: 25: 26: 27: 28:
29: public function __construct($parent)
30: {
31:
32:
33: $this->parent = $parent;
34: $this->updates_run = get_option( 'woothemes-sensei-upgrades', array() );
35:
36:
37: $this->updates = array('1.1.0' => array('auto' => array('assign_role_caps' => array('title' => __('Assign role capabilities', 'woothemes-sensei'), 'desc' => __('Assigns Sensei capabilites to the relevant user roles.', 'woothemes-sensei'), 'product' => 'Sensei')),
38: 'manual' => array()
39: ),
40: '1.3.0' => array('auto' => array('set_default_quiz_grade_type' => array('title' => __('Set default quiz grade type', 'woothemes-sensei'), 'desc' => __('Sets all quizzes to the default \'auto\' grade type.', 'woothemes-sensei')),
41: 'set_default_question_type' => array('title' => __('Set default question type', 'woothemes-sensei'), 'desc' => __('Sets all questions to the default \'multiple choice\' type.', 'woothemes-sensei'))
42: ),
43: 'manual' => array('update_question_answer_data' => array('title' => __('Update question answer data', 'woothemes-sensei'), 'desc' => __('Updates questions to use the new question types structure.', 'woothemes-sensei')))
44: ),
45: '1.4.0' => array('auto' => array('update_question_grade_points' => array('title' => __('Update question grade points', 'woothemes-sensei'), 'desc' => __('Sets all question grade points to the default value of \'1\'.', 'woothemes-sensei'))),
46: 'manual' => array()
47: ),
48: '1.5.0' => array('auto' => array('convert_essay_paste_questions' => array('title' => __('Convert essay paste questions into multi-line questions', 'woothemes-sensei'), 'desc' => __('Converts all essay paste questions into multi-line questions as the essay paste question type was removed in v1.5.0.', 'woothemes-sensei'))),
49: 'manual' => array('set_random_question_order' => array('title' => __('Set all quizzes to have a random question order', 'woothemes-sensei'), 'desc' => __('Sets the order all of questions in all quizzes to a random order, which can be switched off per quiz.', 'woothemes-sensei')),
50: 'set_default_show_question_count' => array('title' => __('Set all quizzes to show all questions', 'woothemes-sensei'), 'desc' => __('Sets all quizzes to show all questions - this can be changed per quiz.', 'woothemes-sensei')),
51: 'remove_deleted_user_activity' => array('title' => __('Remove Sensei activity for deleted users', 'woothemes-sensei'), 'desc' => __('Removes all course, lesson & quiz activity for users that have already been deleted from the database. This will fix incorrect learner counts in the Analysis section.', 'woothemes-sensei')))
52: ),
53: '1.6.0' => array('auto' => array('add_teacher_role' => array('title' => __('Add \'Teacher\' role', 'woothemes-sensei'), 'desc' => __('Adds a \'Teacher\' role to your WordPress site that will allow users to mange the Grading and Analysis pages.', 'woothemes-sensei')),
54: 'add_sensei_caps' => array('title' => __('Add administrator capabilities', 'woothemes-sensei'), 'desc' => __('Adds the \'manage_sensei\' and \'manage_sensei_grades\' capabilities to the Administrator role.', 'woothemes-sensei')),
55: 'restructure_question_meta' => array('title' => __('Restructure question meta data', 'woothemes-sensei'), 'desc' => __('Restructures the question meta data as it relates to quizzes - this accounts for changes in the data structure in v1.6+.', 'woothemes-sensei')),
56: 'update_quiz_settings' => array('title' => __('Add new quiz settings', 'woothemes-sensei'), 'desc' => __('Adds new settings to quizzes that were previously registered as global settings.', 'woothemes-sensei')),
57: 'reset_lesson_order_meta' => array('title' => __('Set default order of lessons', 'woothemes-sensei'), 'desc' => __('Adds data to lessons to ensure that they show up on the \'Order Lessons\' screen - if this update has been run once before then it will reset all lessons to the default order.', 'woothemes-sensei')),),
58: 'manual' => array()
59: ),
60: '1.7.0' => array('auto' => array('add_editor_caps' => array('title' => __('Add Editor capabilities', 'woothemes-sensei'), 'desc' => __('Adds the \'manage_sensei_grades\' capability to the Editor role.', 'woothemes-sensei')),),
61: 'forced' => array('update_question_gap_fill_separators' => array('title' => __('Update Gap Fill questions', 'woothemes-sensei'), 'desc' => __('Updates the format of gap fill questions to allow auto grading and greater flexibility in matching.', 'woothemes-sensei')),
62: 'update_quiz_lesson_relationship' => array('title' => __('Restructure quiz lesson relationship', 'woothemes-sensei'), 'desc' => __('Adds data to quizzes and lessons to ensure that they maintain their 1 to 1 relationship.', 'woothemes-sensei')),
63: 'status_changes_fix_lessons' => array('title' => __('Update lesson statuses', 'woothemes-sensei'), 'desc' => __('Update existing lesson statuses.', 'woothemes-sensei')),
64: 'status_changes_convert_lessons' => array('title' => __('Convert lesson statuses', 'woothemes-sensei'), 'desc' => __('Convert to new lesson statuses.', 'woothemes-sensei')),
65: 'status_changes_convert_courses' => array('title' => __('Convert course statuses', 'woothemes-sensei'), 'desc' => __('Convert to new course statuses.', 'woothemes-sensei')),
66: 'status_changes_convert_questions' => array('title' => __('Convert question statuses', 'woothemes-sensei'), 'desc' => __('Convert to new question statuses.', 'woothemes-sensei')),
67: 'update_legacy_sensei_comments_status' => array('title' => __('Convert legacy Sensei activity types', 'woothemes-sensei'), 'desc' => __('Convert all legacy Sensei activity types such as \'sensei_lesson_start\' and \'sensei_user_answer\' to new status format.', 'woothemes-sensei')),
68: 'update_comment_course_lesson_comment_counts' => array('title' => __('Update comment counts', 'woothemes-sensei'), 'desc' => __('Update comment counts on Courses and Lessons due to status changes.', 'woothemes-sensei')),),
69: ),
70: '1.7.2' => array('auto' => array('index_comment_status_field' => array('title' => __('Add database index to comment statuses', 'woothemes-sensei'), 'desc' => __('This indexes the comment statuses in the database, which will speed up all Sensei activity queries.', 'woothemes-sensei')),),
71:
72: ),
73: '1.8.0' => array('auto' => array('enhance_teacher_role' => array('title' => 'Enhance the \'Teacher\' role', 'desc' => 'Adds the ability for a \'Teacher\' to create courses, lessons , quizes and manage their learners.'),),
74: 'manual' => array()
75: ),
76: );
77:
78: $this->updates = apply_filters( 'sensei_upgrade_functions', $this->updates, $this->updates );
79: $this->version = get_option( 'woothemes-sensei-version' );
80:
81:
82: add_action('admin_menu', array($this, 'add_update_admin_screen'), 50);
83:
84: }
85:
86: 87: 88: 89: 90: 91: 92:
93: public function add_update_admin_screen()
94: {
95: if (current_user_can('manage_options')) {
96: add_submenu_page('sensei', __('Sensei Updates', 'woothemes-sensei'), __('Data Updates', 'woothemes-sensei'), 'manage_options', 'sensei_updates', array($this, 'sensei_updates_page'));
97: }
98: }
99:
100: 101: 102: 103: 104: 105: 106:
107: public function sensei_updates_page() {
108:
109:
110: if ( ! current_user_can('manage_options')) {
111:
112: return;
113:
114: }
115: ?>
116:
117: <div class="wrap">
118:
119: <div id="icon-woothemes-sensei" class="icon32"><br></div>
120: <h2><?php _e('Sensei Updates', 'woothemes-sensei'); ?></h2>
121:
122: <?php
123: $function_name= '';
124: if ( isset($_GET['action']) && $_GET['action'] == 'update'
125: && isset($_GET['n']) && intval($_GET['n']) >= 0
126: && ( (isset($_POST['checked'][0]) && '' != $_POST['checked'][0]) || (isset($_GET['functions']) && '' != $_GET['functions']))) {
127:
128:
129: $n = intval($_GET['n']);
130: $functions_list = '';
131: $done_processing = false;
132:
133:
134: if (isset($_POST['checked'][0]) && '' != $_POST['checked'][0]) {
135:
136: foreach ($_POST['checked'] as $key => $function_name) {
137:
138: if( ! isset( $_POST[ $function_name.'_nonce_field' ] )
139: || ! wp_verify_nonce( $_POST[ $function_name.'_nonce_field' ] , 'run_'.$function_name ) ){
140:
141: wp_die(
142: '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' .
143: '<p>' . __( 'The nonce supplied in order to run this update function is invalid','woothemes-sensei') . '</p>',
144: 403
145: );
146:
147: }
148:
149:
150: if (method_exists($this, $function_name)) {
151:
152: $done_processing = call_user_func_array(array($this, $function_name), array(50, $n));
153:
154: } elseif ($this->function_in_whitelist($function_name)) {
155:
156: $done_processing = call_user_func_array($function_name, array(50, $n));
157:
158: } else {
159:
160: _doing_it_wrong( esc_html( $function_name) , 'Is not a valid Sensei updater function', 'Sensei 1.9.0');
161: return;
162:
163: }
164:
165:
166: if ('' == $functions_list) {
167: $functions_list .= $function_name;
168: } else {
169: $functions_list .= '+' . $function_name;
170: }
171:
172:
173: $this->set_update_run($function_name);
174:
175: }
176:
177: }
178:
179:
180: if (isset($_GET['functions']) && '' != $_GET['functions']) {
181:
182:
183: $functions_array = $_GET['functions'];
184:
185: foreach ($functions_array as $key => $function_name) {
186:
187: if( ! isset( $_GET[ $function_name.'_nonce' ] )
188: || ! wp_verify_nonce( $_GET[ $function_name.'_nonce' ] , 'run_'.$function_name ) ){
189:
190: wp_die(
191: '<h1>' . __( 'Cheatin’ uh?' ) . '</h1>' .
192: '<p>' . __( 'The nonce supplied in order to run this update function is invalid','woothemes-sensei') . '</p>',
193: 403
194: );
195:
196: }
197:
198:
199: if (method_exists($this, $function_name)) {
200:
201: $done_processing = call_user_func_array(array($this, $function_name), array(50, $n));
202:
203: } elseif ($this->function_in_whitelist($function_name)) {
204:
205: $done_processing = call_user_func_array($function_name, array(50, $n));
206:
207: } else {
208:
209: _doing_it_wrong( esc_html( $function_name) , 'Is not a valid Sensei updater function', 'Sensei 1.9.0');
210: return;
211:
212: }
213:
214:
215: if ('' == $functions_list) {
216: $functions_list .= $function_name;
217: } else {
218: $functions_list .= '+' . $function_name;
219: }
220:
221: $this->set_update_run($function_name);
222:
223: }
224:
225: }
226:
227: if (!$done_processing) { ?>
228:
229: <h3><?php _e('Processing Updates...', 'woothemes-sensei'); ?></h3>
230:
231: <p>
232:
233: <?php _e( "If your browser doesn't start loading the next page automatically, click this button:", 'woothemes-sensei' ); ?>
234:
235: <?php
236: $next_action_url = add_query_arg( array(
237: 'page' => 'sensei_updates',
238: 'action' => 'update',
239: 'n' => $n + 50,
240: 'functions' => array( $functions_list ),
241: $function_name.'_nonce' => wp_create_nonce( 'run_'. $function_name ),
242: ), admin_url( 'admin.php' ) );
243: ?>
244:
245: <a class="button" href="<?php echo esc_url( $next_action_url ); ?>">
246:
247: <?php _e( 'Next', 'woothemes-sensei' ); ?>
248:
249: </a>
250:
251: </p>
252: <script type='text/javascript'>
253: <!--
254: function js_sensei_nextpage() {
255: location.href = "<?php echo esc_url_raw( $next_action_url );?>";
256: }
257: setTimeout( "js_sensei_nextpage()", 250 );
258:
259: </script>
260:
261: <?php } else { ?>
262:
263: <p><strong><?php _e('Update completed successfully!', 'woothemes-sensei'); ?></strong></p>
264: <p>
265: <a href="<?php echo admin_url('edit.php?post_type=lesson'); ?>"><?php _e('Create a new lesson', 'woothemes-sensei'); ?></a>
266: or <a
267: href="<?php echo admin_url('admin.php?page=sensei_updates'); ?>"><?php _e('run some more updates', 'woothemes-sensei'); ?></a>.
268: </p>
269:
270: <?php }
271:
272: } else { ?>
273:
274: <h3><?php _e('Updates', 'woothemes-sensei'); ?></h3>
275: <p><?php printf(__('These are updates that have been made available as new Sensei versions have been released. Updates of type %1$sAuto%2$s will run as you update Sensei to the relevant version - other updates need to be run manually and you can do that here.', 'woothemes-sensei'), '<code>', '</code>'); ?></p>
276:
277: <div class="updated"><p>
278: <strong><?php _e('Only run these updates if you have been instructed to do so by WooThemes support staff.', 'woothemes-sensei'); ?></strong>
279: </p></div>
280:
281: <table class="widefat" cellspacing="0" id="update-plugins-table">
282:
283: <thead>
284: <tr>
285: <th scope="col" class="manage-column"><?php _e('Update', 'woothemes-sensei'); ?></th>
286: <th scope="col" class="manage-column"><?php _e('Type', 'woothemes-sensei'); ?></th>
287: <th scope="col" class="manage-column"><?php _e('Action', 'woothemes-sensei'); ?></th>
288: </tr>
289: </thead>
290:
291: <tfoot>
292: <tr>
293: <th scope="col" class="manage-column"><?php _e('Update', 'woothemes-sensei'); ?></th>
294: <th scope="col" class="manage-column"><?php _e('Type', 'woothemes-sensei'); ?></th>
295: <th scope="col" class="manage-column"><?php _e('Action', 'woothemes-sensei'); ?></th>
296: </tr>
297: </tfoot>
298:
299: <tbody class="updates">
300: <?php
301:
302: uksort($this->updates, array($this, 'sort_updates'));
303: $this->updates = array_reverse($this->updates, true);
304: $class = 'alternate';
305: foreach ($this->updates as $version => $version_updates) {
306: foreach ($version_updates as $type => $updates) {
307: foreach ($updates as $update => $data) {
308: $update_run = $this->has_update_run($update);
309: $product = 'Sensei';
310: if (isset($data['product']) && '' != $data['product']) {
311: $product = $data['product'];
312: }
313: ?>
314: <form method="post" action="admin.php?page=sensei_updates&action=update&n=0"
315: name="update-sensei" class="upgrade">
316: <tr class="<?php echo $class; ?>">
317: <td>
318: <p>
319: <input type="hidden" name="checked[]" value="<?php echo $update; ?>">
320: <strong><?php echo $data['title']; ?></strong><br><?php echo $data['desc']; ?>
321: <br>
322: <em><?php printf(__('Originally included in %s v%s', 'woothemes-sensei'), $product, $version); ?></em>
323: </p>
324: </td>
325: <?php
326: $type_label = __('Auto', 'woothemes-sensei');
327: if ($type != 'auto') {
328: $type_label = __('Manual', 'woothemes-sensei');
329: }
330: ?>
331: <td><p><?php echo $type_label; ?></p></td>
332: <td>
333: <p>
334: <input onclick="javascript:return confirm('<?php echo addslashes( sprintf( __( 'Are you sure you want to run the \'%s\' update?', 'woothemes-sensei' ), $data['title'] ) ); ?>');"
335: id="update-sensei"
336: class="button<?php if( ! $update_run ) { echo ' button-primary'; } ?>"
337: type="submit"
338: value="<?php if( $update_run ) { _e( 'Re-run Update', 'woothemes-sensei' ); } else { _e( 'Run Update', 'woothemes-sensei' ); } ?>"
339: name="update">
340:
341: <?php
342: $nonce_action = 'run_'.$update;
343: $nonce_field_name = $update.'_nonce_field';
344: wp_nonce_field( $nonce_action, $nonce_field_name, false, true );
345: ?>
346: </p>
347: </td>
348: </tr>
349: </form>
350: <?php
351: if ('alternate' == $class) {
352: $class = '';
353: } else {
354: $class = 'alternate';
355: }
356: }
357: }
358: }
359: ?>
360: </tbody>
361:
362: </table>
363:
364: </div>
365:
366: <?php
367: }
368: }
369:
370: 371: 372: 373: 374: 375: 376: 377:
378: public function function_in_whitelist( $function_name ){
379:
380: $function_whitelist = array(
381: 'status_changes_convert_questions',
382: 'status_changes_fix_lessons',
383: 'status_changes_convert_courses',
384: 'status_changes_convert_lessons',
385: 'status_changes_repair_course_statuses',
386:
387: );
388:
389: return in_array($function_name, $function_whitelist );
390:
391: }
392:
393: 394: 395: 396: 397: 398: 399:
400: private function sort_updates( $a, $b ) {
401: return strcmp( $a, $b );
402: }
403:
404: 405: 406: 407: 408: 409: 410: 411:
412: public function update ( $type = 'auto' ) {
413:
414:
415: if( ! current_user_can( 'manage_options' ) ) {
416: return false;
417: }
418:
419: $this->force_updates();
420:
421:
422: foreach ( $this->updates as $version => $value ) {
423: foreach ( $this->updates[$version] as $upgrade_type => $function_to_run ) {
424: if ( $upgrade_type == $type ) {
425: $updated = false;
426:
427: foreach ( $function_to_run as $function_name => $update_data ) {
428: if ( isset( $function_name ) && '' != $function_name ) {
429: if ( ! in_array( $function_name, $this->updates_run ) ) {
430: $updated = false;
431: if ( method_exists( $this, $function_name ) ) {
432:
433: $this->updates_run = array_unique( $this->updates_run );
434: update_option( Sensei()->token . '-upgrades', $this->updates_run );
435: return true;
436:
437: } elseif( $this->function_in_whitelist( $function_name ) ) {
438:
439: $updated = call_user_func( $function_name );
440:
441: }
442:
443: if ( $updated ) {
444: array_push( $this->updates_run, $function_name );
445: }
446: }
447: }
448: }
449: }
450: }
451: }
452:
453: $this->updates_run = array_unique( $this->updates_run );
454: update_option( $this->token . '-upgrades', $this->updates_run );
455:
456: return true;
457:
458: }
459:
460: private function force_updates() {
461:
462: if( ! isset( $_GET['page'] ) || 'sensei_updates' != $_GET['page'] ) {
463:
464:
465:
466:
467:
468:
469:
470: $skip_forced_updates = false;
471: $lesson_posts = wp_count_posts( 'lesson' );
472: if( ! isset( $lesson_posts->publish ) || ! $lesson_posts->publish ) {
473: $skip_forced_updates = true;
474: }
475:
476: $use_the_force = false;
477:
478: $updates_to_run = array();
479:
480: foreach ( $this->updates as $version => $value ) {
481: foreach ( $this->updates[$version] as $upgrade_type => $function_to_run ) {
482: if ( $upgrade_type == 'forced' ) {
483: foreach ( $function_to_run as $function_name => $update_data ) {
484:
485: if( $skip_forced_updates ) {
486: $this->set_update_run( $function_name );
487: continue;
488: }
489:
490: $update_run = $this->has_update_run( $function_name );
491:
492: if( ! $update_run ) {
493: $use_the_force = true;
494: $updates_to_run[ $function_name ] = $update_data;
495: }
496: }
497: }
498: }
499: }
500:
501: if( $skip_forced_updates ) {
502: return;
503: }
504:
505: if( $use_the_force && 0 < count( $updates_to_run ) ) {
506:
507: $update_title = __( 'Important Sensei updates required', 'woothemes-sensei' );
508:
509: $update_message = '<h1>' . __( 'Important Sensei upgrades required!', 'woothemes-sensei' ) . '</h1>' . "\n";
510:
511:
512:
513: $update_message .= '<p>' . __( 'The latest version of Sensei requires some important database upgrades. In order to run these upgrades you will need to follow the step by step guide below. Your site will not function correctly unless you run these critical updates.', 'woothemes-sensei' ) . '</p>' . "\n";
514:
515: $update_message .= '<p><b>' . __( 'To run the upgrades click on each of the links below in the order that they appear.', 'woothemes-sensei' ) . '</b></p>' . "\n";
516:
517: $update_message .= '<p>' . __( 'Clicking each link will open up a new window/tab - do not close that window/tab until you see the message \'Update completed successfully\'. Once you see that message you can close the window/tab and start the next upgrade by clicking on the next link in the list.', 'woothemes-sensei' ) . '</p>' . "\n";
518:
519: $update_message .= '<p><b>' . __( 'Once all the upgrades have been completed you will be able to use your WordPress site again.', 'woothemes-sensei' ) . '</b></p>' . "\n";
520:
521: $update_message .= '<ol>' . "\n";
522:
523: foreach( $updates_to_run as $function => $data ) {
524:
525: if( ! isset( $data['title'] ) ) {
526: break;
527: }
528:
529: $update_message .= '<li style="margin:5px 0;"><a href="' . admin_url( 'admin.php?page=sensei_updates&action=update&n=0&functions[]=' . $function ) . '" target="_blank">' . $data['title'] . '</a></li>';
530: }
531:
532: $update_message .= '</ol>' . "\n";
533:
534: switch( $version ) {
535:
536: case '1.7.0':
537: $update_message .= '<p><em>' . sprintf( __( 'Want to know what these upgrades are all about? %1$sFind out more here%2$s.', 'woothemes-sensei' ), '<a href="http://develop.woothemes.com/sensei/2014/12/03/important-information-about-sensei-1-7" target="_blank">', '</a>' ) . '</em></p>' . "\n";
538: break;
539:
540: }
541:
542: wp_die( $update_message, $update_title );
543: }
544: }
545: }
546:
547: 548: 549: 550: 551: 552: 553:
554: private function has_update_run( $update ) {
555: if ( in_array( $update, $this->updates_run ) ) {
556: return true;
557: }
558: return false;
559: }
560:
561: 562: 563: 564: 565: 566:
567: private function set_update_run( $update ) {
568: array_push( $this->updates_run, $update );
569: $this->updates_run = array_unique( $this->updates_run );
570: update_option( Sensei()->token . '-upgrades', $this->updates_run );
571: }
572:
573: 574: 575: 576: 577: 578: 579:
580: public function assign_role_caps() {
581: foreach ( $this->parent->post_types->role_caps as $role_cap_set ) {
582: foreach ( $role_cap_set as $role_key => $capabilities_array ) {
583:
584: $role = get_role( $role_key );
585: foreach ( $capabilities_array as $cap_name ) {
586:
587: if ( !empty( $role ) ) {
588: if ( !$role->has_cap( $cap_name ) ) {
589: $role->add_cap( $cap_name );
590: }
591: }
592: }
593: }
594: }
595: }
596:
597: 598: 599: 600: 601: 602: 603:
604: public function set_default_quiz_grade_type() {
605: $args = array( 'post_type' => 'quiz',
606: 'posts_per_page' => -1,
607: 'post_status' => 'publish',
608: 'suppress_filters' => 0
609: );
610: $quizzes = get_posts( $args );
611:
612: foreach( $quizzes as $quiz ) {
613: update_post_meta( $quiz->ID, '_quiz_grade_type', 'auto' );
614: update_post_meta( $quiz->ID, '_quiz_grade_type_disabled', '' );
615: }
616: }
617:
618: 619: 620: 621: 622: 623: 624:
625: public function set_default_question_type() {
626: $args = array( 'post_type' => 'question',
627: 'posts_per_page' => -1,
628: 'post_status' => 'publish',
629: 'suppress_filters' => 0
630: );
631: $questions = get_posts( $args );
632:
633: $already_run = true;
634: foreach( $questions as $question ) {
635: if( $already_run ) {
636: $terms = wp_get_post_terms( $question->ID, 'question-type' );
637: if( is_array( $terms ) && count( $terms ) > 0 ) {
638: break;
639: }
640: }
641: $already_run = false;
642: wp_set_post_terms( $question->ID, array( 'multiple-choice' ), 'question-type' );
643: }
644:
645: }
646:
647: 648: 649: 650: 651: 652: 653:
654: public function update_question_answer_data( $n = 50, $offset = 0 ) {
655:
656:
657: $quiz_count_object = wp_count_posts( 'quiz' );
658: $quiz_count_published = $quiz_count_object->publish;
659:
660:
661: if ( 0 == $offset ) {
662: $current_page = 1;
663: } else {
664: $current_page = intval( $offset / $n );
665: }
666: $total_pages = intval( $quiz_count_published / $n );
667:
668:
669: $args = array( 'post_type' => 'quiz',
670: 'posts_per_page' => $n,
671: 'offset' => $offset,
672: 'post_status' => 'publish',
673: 'suppress_filters' => 0
674: );
675: $quizzes = get_posts( $args );
676:
677: $old_answers = array();
678: $right_answers = array();
679: $old_user_answers = array();
680:
681: if( is_array( $quizzes ) ) {
682: foreach( $quizzes as $quiz ) {
683: $quiz_id = $quiz->ID;
684:
685:
686: $comments = Sensei_Utils::sensei_check_for_activity( array( 'post_id' => $quiz_id, 'type' => 'sensei_quiz_answers' ), true );
687:
688: if ( !is_array($comments) ) {
689: $comments = array( $comments );
690: }
691: foreach ( $comments as $comment ) {
692: $user_id = $comment->user_id;
693: $content = maybe_unserialize( base64_decode( $comment->comment_content ) );
694: $old_user_answers[ $quiz_id ][ $user_id ] = $content;
695: }
696:
697:
698: $questions = Sensei_Utils::sensei_get_quiz_questions( $quiz_id );
699: if( is_array( $questions ) ) {
700: foreach( $questions as $question ) {
701: $right_answer = get_post_meta( $question->ID, '_question_right_answer', true );
702: $right_answers[ $quiz_id ][ $question->ID ] = $right_answer;
703: }
704: }
705: }
706: }
707:
708: if( is_array( $right_answers ) ) {
709: foreach( $right_answers as $quiz_id => $question ) {
710: $count = 0;
711: if( is_array( $question ) ) {
712: foreach( $question as $question_id => $answer ) {
713: ++$count;
714: if( isset( $old_user_answers[ $quiz_id ] ) ) {
715: $answers_linkup[ $quiz_id ][ $count ] = $question_id;
716: }
717: }
718: }
719: }
720: }
721:
722: if( is_array( $old_user_answers ) ) {
723: foreach( $old_user_answers as $quiz_id => $user_answers ) {
724: foreach( $user_answers as $user_id => $answers ) {
725: foreach( $answers as $answer_id => $user_answer ) {
726: $question_id = $answers_linkup[ $quiz_id ][ $answer_id ];
727: $new_user_answers[ $question_id ] = $user_answer;
728: Sensei_Utils::sensei_grade_question_auto( $question_id, '', $user_answer, $user_id );
729: }
730: $lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
731: Sensei_Utils::sensei_start_lesson( $lesson_id, $user_id );
732: Sensei_Utils::sensei_save_quiz_answers( $new_user_answers, $user_id );
733: }
734: }
735: }
736:
737: if ( $current_page == $total_pages ) {
738: return true;
739: } else {
740: return false;
741: }
742:
743: }
744:
745: 746: 747: 748: 749: 750:
751: public function update_question_grade_points() {
752: $args = array( 'post_type' => 'question',
753: 'posts_per_page' => -1,
754: 'post_status' => 'publish',
755: 'suppress_filters' => 0
756: );
757: $questions = get_posts( $args );
758:
759: foreach( $questions as $question ) {
760: update_post_meta( $question->ID, '_question_grade', '1' );
761: }
762: return true;
763: }
764:
765: 766: 767: 768: 769: 770:
771: public function convert_essay_paste_questions() {
772: $args = array( 'post_type' => 'question',
773: 'posts_per_page' => -1,
774: 'post_status' => 'publish',
775: 'tax_query' => array(
776: array(
777: 'taxonomy' => 'question-type',
778: 'terms' => 'essay-paste',
779: 'field' => 'slug'
780: )
781: ),
782: 'suppress_filters' => 0
783: );
784: $questions = get_posts( $args );
785:
786: foreach( $questions as $question ) {
787: wp_set_object_terms( $question->ID, 'multi-line', 'question-type', false );
788:
789: $quiz_id = get_post_meta( $question->ID, '_quiz_id', true );
790: if( 0 < intval( $quiz_id ) ) {
791: add_post_meta( $question->ID, '_quiz_question_order' . $quiz_id, $quiz_id . '0000', true );
792: }
793: }
794: return true;
795: }
796:
797: 798: 799: 800: 801: 802:
803: public function set_random_question_order( $n = 50, $offset = 0 ) {
804:
805:
806: $quiz_count_object = wp_count_posts( 'quiz' );
807: $quiz_count_published = $quiz_count_object->publish;
808:
809:
810: if ( 0 == $offset ) {
811: $current_page = 1;
812: } else {
813: $current_page = intval( $offset / $n );
814: }
815: $total_pages = intval( $quiz_count_published / $n );
816:
817: $args = array( 'post_type' => 'quiz',
818: 'post_status' => 'any',
819: 'posts_per_page' => $n,
820: 'offset' => $offset,
821: 'suppress_filters' => 0
822: );
823: $quizzes = get_posts( $args );
824:
825: foreach( $quizzes as $quiz ) {
826: update_post_meta( $quiz->ID, '_random_question_order', 'yes' );
827: }
828:
829: if ( $current_page == $total_pages ) {
830: return true;
831: } else {
832: return false;
833: }
834:
835: }
836:
837: 838: 839: 840: 841: 842:
843: public function set_default_show_question_count( $n = 50, $offset = 0 ) {
844:
845: $args = array( 'post_type' => 'quiz',
846: 'post_status' => 'any',
847: 'posts_per_page' => $n,
848: 'offset' => $offset,
849: 'meta_key' => '_show_questions',
850: 'suppress_filters' => 0
851: );
852: $quizzes = get_posts( $args );
853:
854: $total_quizzes = count( $quizzes );
855:
856: if( 0 == intval( $total_quizzes ) ) {
857: return true;
858: }
859:
860: foreach( $quizzes as $quiz ) {
861: delete_post_meta( $quiz->ID, '_show_questions' );
862: }
863:
864: $total_pages = intval( $total_quizzes / $n );
865:
866:
867: if ( 0 == $offset ) {
868: $current_page = 1;
869: } else {
870: $current_page = intval( $offset / $n );
871: }
872:
873: if ( $current_page == $total_pages ) {
874: return true;
875: } else {
876: return false;
877: }
878:
879: }
880:
881: public function remove_deleted_user_activity( $n = 50, $offset = 0 ) {
882:
883: $all_activity = get_comments( array( 'status' => 'approve' ) );
884: $activity_count = array();
885: foreach( $all_activity as $activity ) {
886: if( '' == $activity->comment_type ) continue;
887: if( strpos( 'sensei_', $activity->comment_type ) != 0 ) continue;
888: if( 0 == $activity->user_id ) continue;
889: $activity_count[] = $activity->comment_ID;
890: }
891:
892: $args = array(
893: 'number' => $n,
894: 'offset' => $offset,
895: 'status' => 'approve'
896: );
897:
898: $activities = get_comments( $args );
899:
900: foreach( $activities as $activity ) {
901: if( '' == $activity->comment_type ) continue;
902: if( strpos( 'sensei_', $activity->comment_type ) != 0 ) continue;
903: if( 0 == $activity->user_id ) continue;
904:
905: $user_exists = get_userdata( $activity->user_id );
906:
907: if( ! $user_exists ) {
908: wp_delete_comment( intval( $activity->comment_ID ), true );
909: wp_cache_flush();
910: }
911: }
912:
913: $total_activities = count( $activity_count );
914:
915: $total_pages = intval( $total_activities / $n );
916:
917:
918: if ( 0 == $offset ) {
919: $current_page = 1;
920: } else {
921: $current_page = intval( $offset / $n );
922: }
923:
924: if ( $current_page >= $total_pages ) {
925: return true;
926: } else {
927: return false;
928: }
929:
930: }
931:
932: public function add_teacher_role() {
933: add_role( 'teacher', __( 'Teacher', 'woothemes-sensei' ), array( 'read' => true, 'manage_sensei_grades' => true ) );
934: return true;
935: }
936:
937: public function add_sensei_caps() {
938: $role = get_role( 'administrator' );
939:
940: if( ! is_null( $role ) ) {
941: $role->add_cap( 'manage_sensei' );
942: $role->add_cap( 'manage_sensei_grades' );
943: }
944:
945: return true;
946: }
947:
948: public function restructure_question_meta() {
949: $args = array(
950: 'post_type' => 'question',
951: 'posts_per_page' => -1,
952: 'post_status' => 'any',
953: 'suppress_filters' => 0
954: );
955:
956: $questions = get_posts( $args );
957:
958: foreach( $questions as $question ) {
959:
960: if( ! isset( $question->ID ) ) continue;
961:
962: $quiz_id = get_post_meta( $question->ID, '_quiz_id', true );
963:
964: $question_order = get_post_meta( $question->ID, '_quiz_question_order', true );
965: update_post_meta( $question->ID, '_quiz_question_order' . $quiz_id, $question_order );
966:
967: }
968:
969: return true;
970: }
971:
972: public function update_quiz_settings() {
973:
974: $settings = get_option( 'woothemes-sensei-settings', array() );
975:
976: $lesson_completion = false;
977: if( isset( $settings['lesson_completion'] ) ) {
978: $lesson_completion = $settings['lesson_completion'];
979: }
980:
981: $reset_quiz_allowed = false;
982: if( isset( $settings['quiz_reset_allowed'] ) ) {
983: $reset_quiz_allowed = $settings['quiz_reset_allowed'];
984: }
985:
986: $args = array(
987: 'post_type' => 'quiz',
988: 'posts_per_page' => -1,
989: 'post_status' => 'any',
990: 'suppress_filters' => 0
991: );
992:
993: $quizzes = get_posts( $args );
994:
995: foreach( $quizzes as $quiz ) {
996:
997: if( ! isset( $quiz->ID ) ) continue;
998:
999: if( isset( $lesson_completion ) && 'passed' == $lesson_completion ) {
1000: update_post_meta( $quiz->ID, '_pass_required', 'on' );
1001: } else {
1002: update_post_meta( $quiz->ID, '_quiz_passmark', 0 );
1003: }
1004:
1005: if( isset( $reset_quiz_allowed ) && $reset_quiz_allowed ) {
1006: update_post_meta( $quiz->ID, '_enable_quiz_reset', 'on' );
1007: }
1008: }
1009:
1010: return true;
1011: }
1012:
1013: public function reset_lesson_order_meta() {
1014: $args = array(
1015: 'post_type' => 'lesson',
1016: 'posts_per_page' => -1,
1017: 'post_status' => 'any',
1018: 'suppress_filters' => 0
1019: );
1020:
1021: $lessons = get_posts( $args );
1022:
1023: foreach( $lessons as $lesson ) {
1024:
1025: if( ! isset( $lesson->ID ) ) continue;
1026:
1027: $course_id = get_post_meta( $lesson->ID, '_lesson_course', true);
1028:
1029: if( $course_id ) {
1030: update_post_meta( $lesson->ID, '_order_' . $course_id, 0 );
1031: }
1032:
1033: $module = Sensei()->modules->get_lesson_module( $lesson->ID );
1034:
1035: if( $module ) {
1036: update_post_meta( $lesson->ID, '_order_module_' . $module->term_id, 0 );
1037: }
1038:
1039: }
1040:
1041: return true;
1042: }
1043:
1044: public function add_editor_caps() {
1045: $role = get_role( 'editor' );
1046:
1047: if( ! is_null( $role ) ) {
1048: $role->add_cap( 'manage_sensei_grades' );
1049: }
1050:
1051: return true;
1052: }
1053:
1054: 1055: 1056: 1057: 1058: 1059:
1060: public function update_question_gap_fill_separators() {
1061: global $wpdb;
1062:
1063: $sql = "UPDATE $wpdb->postmeta AS m, $wpdb->term_relationships AS tr, $wpdb->term_taxonomy AS tt, $wpdb->terms AS t SET m.meta_value = replace(m.meta_value, '|', '||')
1064: WHERE m.meta_key = '_question_right_answer' AND m.meta_value LIKE '%|%' AND m.meta_value NOT LIKE '%||%'
1065: AND m.post_id = tr.object_id AND tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.term_id = t.term_id
1066: AND tt.taxonomy = 'question-type' AND t.slug = 'gap-fill'";
1067: $wpdb->query( $sql );
1068:
1069: return true;
1070: }
1071:
1072: public function update_quiz_lesson_relationship( $n = 50, $offset = 0 ) {
1073: $count_object = wp_count_posts( 'quiz' );
1074:
1075: $count_published = 0;
1076: foreach ( $count_object AS $status => $count ) {
1077: $count_published += $count;
1078: }
1079:
1080:
1081: if ( 0 == $offset ) {
1082: $current_page = 1;
1083: } else {
1084: $current_page = intval( $offset / $n );
1085: }
1086: $total_pages = ceil( $count_published / $n );
1087:
1088: $args = array(
1089: 'post_type' => 'quiz',
1090: 'posts_per_page' => $n,
1091: 'offset' => $offset,
1092: 'post_status' => 'any'
1093: );
1094:
1095: $quizzes = get_posts( $args );
1096:
1097: foreach( $quizzes as $quiz ) {
1098:
1099: if( ! isset( $quiz->ID ) || 0 != $quiz->post_parent ) continue;
1100:
1101: $lesson_id = get_post_meta( $quiz->ID, '_quiz_lesson', true );
1102:
1103: if( empty( $lesson_id ) ) continue;
1104:
1105: $data = array(
1106: 'ID' => $quiz->ID,
1107: 'post_parent' => $lesson_id,
1108: );
1109: wp_update_post( $data );
1110:
1111: update_post_meta( $lesson_id, '_lesson_quiz', $quiz->ID );
1112: }
1113:
1114: if ( $current_page == $total_pages || 0 == $total_pages ) {
1115: return true;
1116: } else {
1117: return false;
1118: }
1119: }
1120:
1121: function status_changes_fix_lessons( $n = 50, $offset = 0 ) {
1122: global $wpdb;
1123:
1124: $count_object = wp_count_posts( 'lesson' );
1125: $count_published = 0;
1126: foreach ( $count_object AS $status => $count ) {
1127: $count_published += $count;
1128: }
1129:
1130: if ( 0 == $count_published ) {
1131: return true;
1132: }
1133:
1134:
1135: if ( 0 == $offset ) {
1136: $current_page = 1;
1137: } else {
1138: $current_page = intval( $offset / $n );
1139: }
1140: $total_pages = ceil( $count_published / $n );
1141:
1142:
1143: $args = array(
1144: 'post_type' => 'lesson',
1145: 'post_status' => 'any',
1146: 'posts_per_page' => $n,
1147: 'offset' => $offset,
1148: 'fields' => 'ids'
1149: );
1150: $lesson_ids = get_posts( $args );
1151:
1152:
1153: $id_list = join( ',', $lesson_ids );
1154: $meta_list = $wpdb->get_results( "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '_quiz_lesson' AND meta_value IN ($id_list)", ARRAY_A );
1155: $lesson_quiz_ids = array();
1156: if ( !empty($meta_list) ) {
1157: foreach ( $meta_list as $metarow ) {
1158: $lesson_id = $metarow['meta_value'];
1159: $quiz_id = $metarow['post_id'];
1160: $lesson_quiz_ids[ $lesson_id ] = $quiz_id;
1161: }
1162: }
1163:
1164:
1165: $id_list = join( ',', array_values($lesson_quiz_ids) );
1166: $meta_list = $wpdb->get_results( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = '_quiz_id' AND meta_value IN ($id_list)", ARRAY_A );
1167: $lesson_quiz_ids_with_questions = array();
1168: if ( !empty($meta_list) ) {
1169: foreach ( $meta_list as $metarow ) {
1170: $quiz_id = $metarow['meta_value'];
1171: $lesson_quiz_ids_with_questions[] = $quiz_id;
1172: }
1173: }
1174:
1175:
1176:
1177: $d_count = $a_count =0;
1178: foreach ( $lesson_quiz_ids AS $lesson_id => $quiz_id ) {
1179: if ( !in_array( $quiz_id, $lesson_quiz_ids_with_questions ) ) {
1180:
1181:
1182: delete_post_meta( $quiz_id, '_pass_required' );
1183: delete_post_meta( $quiz_id, '_quiz_passmark' );
1184: delete_post_meta( $lesson_id, '_quiz_has_questions' );
1185: $d_count++;
1186: }
1187: else if ( in_array( $quiz_id, $lesson_quiz_ids_with_questions ) ) {
1188:
1189:
1190: update_post_meta( $lesson_id, '_quiz_has_questions', true );
1191: $a_count++;
1192: }
1193: }
1194:
1195: if ( $current_page == $total_pages ) {
1196: return true;
1197: } else {
1198: return false;
1199: }
1200: }
1201:
1202: function status_changes_convert_lessons( $n = 50, $offset = 0 ) {
1203: global $wpdb;
1204:
1205: wp_defer_comment_counting( true );
1206:
1207: $user_count_result = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users " );
1208:
1209: if ( 0 == $user_count_result ) {
1210: return true;
1211: }
1212:
1213: if ( 0 == $offset ) {
1214: $current_page = 1;
1215: } else {
1216: $current_page = intval( $offset / $n );
1217: }
1218:
1219: $total_pages = ceil( $user_count_result / $n );
1220:
1221:
1222: $args = array(
1223: 'post_type' => 'lesson',
1224: 'post_status' => 'any',
1225: 'posts_per_page' => -1,
1226: 'meta_query' => array(
1227: array(
1228: 'key' => '_quiz_has_questions',
1229: 'value' => 1,
1230: ),
1231: ),
1232: 'fields' => 'ids'
1233: );
1234: $lesson_ids_with_quizzes = get_posts( $args );
1235:
1236: $id_list = join( ',', $lesson_ids_with_quizzes );
1237: $meta_list = $wpdb->get_results( "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '_quiz_lesson' AND meta_value IN ($id_list)", ARRAY_A );
1238: $lesson_quiz_ids = array();
1239: if ( !empty($meta_list) ) {
1240: foreach ( $meta_list as $metarow ) {
1241: $lesson_id = $metarow['meta_value'];
1242: $quiz_id = $metarow['post_id'];
1243: $lesson_quiz_ids[ $lesson_id ] = $quiz_id;
1244: }
1245: }
1246:
1247:
1248: $id_list = join( ',', array_values($lesson_quiz_ids) );
1249: $meta_list = $wpdb->get_results( "SELECT post_id, meta_key, meta_value FROM $wpdb->postmeta WHERE ( meta_key = '_pass_required' OR meta_key = '_quiz_passmark' ) AND post_id IN ($id_list)", ARRAY_A );
1250: $quizzes_pass_required = $quizzes_passmarks = array();
1251: if ( !empty($meta_list) ) {
1252: foreach ( $meta_list as $metarow ) {
1253: if ( !empty($metarow['meta_value']) ) {
1254: $quiz_id = $metarow['post_id'];
1255: $key = $metarow['meta_key'];
1256: $value = $metarow['meta_value'];
1257: if ( '_pass_required' == $key ) {
1258: $quizzes_pass_required[ $quiz_id ] = $value;
1259: }
1260: if ( '_quiz_passmark' == $key ) {
1261: $quizzes_passmarks[ $quiz_id ] = $value;
1262: }
1263: }
1264: }
1265: }
1266:
1267: $users_sql = "SELECT ID FROM $wpdb->users ORDER BY ID ASC LIMIT %d OFFSET %d";
1268: $start_sql = "SELECT comment_post_ID, comment_date FROM $wpdb->comments WHERE comment_type = 'sensei_lesson_start' AND user_id = %d GROUP BY comment_post_ID ";
1269: $end_sql = "SELECT comment_post_ID, comment_date FROM $wpdb->comments WHERE comment_type = 'sensei_lesson_end' AND user_id = %d GROUP BY comment_post_ID ";
1270: $grade_sql = "SELECT comment_post_ID, comment_content FROM $wpdb->comments WHERE comment_type = 'sensei_quiz_grade' AND user_id = %d GROUP BY comment_post_ID ORDER BY comment_content DESC ";
1271: $answers_sql = "SELECT comment_post_ID, comment_content FROM $wpdb->comments WHERE comment_type = 'sensei_quiz_asked' AND user_id = %d GROUP BY comment_post_ID ORDER BY comment_date_gmt DESC ";
1272: $check_existing_sql = "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d AND user_id = %d AND comment_type = 'sensei_lesson_status' ";
1273:
1274:
1275: $user_ids = $wpdb->get_col( $wpdb->prepare($users_sql, $n, $offset) );
1276:
1277: foreach ( $user_ids AS $user_id ) {
1278:
1279: $lesson_ends = $lesson_grades = $lesson_answers = array();
1280:
1281:
1282: $_lesson_ends = $wpdb->get_results( $wpdb->prepare($end_sql, $user_id), ARRAY_A );
1283: foreach ( $_lesson_ends as $lesson_end ) {
1284:
1285: $lesson_ends[ $lesson_end['comment_post_ID'] ] = $lesson_end['comment_date'];
1286: }
1287: unset( $_lesson_ends );
1288:
1289:
1290: $_lesson_grades = $wpdb->get_results( $wpdb->prepare($grade_sql, $user_id), ARRAY_A );
1291: foreach ( $_lesson_grades as $lesson_grade ) {
1292:
1293: if ( empty($lesson_grades[ $lesson_grade['comment_post_ID'] ]) || $lesson_grades[ $lesson_grade['comment_post_ID'] ] < $lesson_grade['comment_content'] ) {
1294: $lesson_grades[ $lesson_grade['comment_post_ID'] ] = $lesson_grade['comment_content'];
1295: }
1296: }
1297: unset( $_lesson_grades );
1298:
1299:
1300: $_lesson_answers = $wpdb->get_results( $wpdb->prepare($answers_sql, $user_id), ARRAY_A );
1301: foreach ( $_lesson_answers as $lesson_answer ) {
1302:
1303: $lesson_answers[ $lesson_answer['comment_post_ID'] ] = $lesson_answer['comment_content'];
1304: }
1305: unset( $_lesson_answers );
1306:
1307:
1308: $lesson_starts = $wpdb->get_results( $wpdb->prepare($start_sql, $user_id), ARRAY_A );
1309: foreach ( $lesson_starts as $lesson_log ) {
1310:
1311: $lesson_id = $lesson_log['comment_post_ID'];
1312:
1313:
1314: $status = 'in-progress';
1315:
1316: $status_date = $lesson_log['comment_date'];
1317:
1318: $meta_data = array(
1319: 'start' => $status_date,
1320: );
1321:
1322: if ( !empty($lesson_ends[$lesson_id]) ) {
1323: $status_date = $lesson_ends[$lesson_id];
1324:
1325: if ( !empty( $lesson_quiz_ids[$lesson_id] ) ) {
1326:
1327: if ( !empty($lesson_answers[$quiz_id]) ) {
1328: $meta_data['questions_asked'] = $lesson_answers[$quiz_id];
1329: }
1330:
1331: $quiz_id = $lesson_quiz_ids[$lesson_id];
1332: if ( !empty($lesson_grades[$quiz_id]) ) {
1333: $meta_data['grade'] = $quiz_grade = $lesson_grades[$quiz_id];
1334:
1335: if ( !empty( $quizzes_pass_required[$quiz_id] ) && $quizzes_passmarks[$quiz_id] <= $quiz_grade ) {
1336: $status = 'passed';
1337: }
1338: elseif ( !empty( $quizzes_pass_required[$quiz_id] ) && $quizzes_passmarks[$quiz_id] > $quiz_grade ) {
1339: $status = 'failed';
1340: }
1341: else {
1342: $status = 'graded';
1343: }
1344: }
1345: else {
1346:
1347: $status = 'ungraded';
1348: }
1349: }
1350: else {
1351:
1352: $status = 'complete';
1353: }
1354: }
1355: $data = array(
1356:
1357: 'comment_post_ID' => $lesson_id,
1358: 'comment_approved' => $status,
1359: 'comment_type' => 'sensei_lesson_status',
1360: 'comment_date' => $status_date,
1361: 'user_id' => $user_id,
1362: 'comment_date_gmt' => get_gmt_from_date($status_date),
1363: 'comment_author' => '',
1364: );
1365:
1366: $sql = $wpdb->prepare( $check_existing_sql, $lesson_id, $user_id );
1367: $comment_ID = $wpdb->get_var( $sql );
1368: if ( !$comment_ID ) {
1369:
1370: $wpdb->insert($wpdb->comments, $data);
1371: $comment_ID = (int) $wpdb->insert_id;
1372:
1373: if ( $comment_ID && !empty($meta_data) ) {
1374: foreach ( $meta_data as $key => $value ) {
1375:
1376: if ( $wpdb->get_var( $wpdb->prepare(
1377: "SELECT COUNT(*) FROM $wpdb->commentmeta WHERE comment_id = %d AND meta_key = %s ",
1378: $comment_ID, $key ) ) ) {
1379: continue;
1380: }
1381: $result = $wpdb->insert( $wpdb->commentmeta, array(
1382: 'comment_id' => $comment_ID,
1383: 'meta_key' => $key,
1384: 'meta_value' => $value
1385: ) );
1386: }
1387: }
1388: }
1389: }
1390: }
1391: $wpdb->flush();
1392:
1393: if ( $current_page == $total_pages ) {
1394: return true;
1395: } else {
1396: return false;
1397: }
1398: }
1399:
1400: function status_changes_convert_courses( $n = 50, $offset = 0 ) {
1401: global $wpdb;
1402:
1403: wp_defer_comment_counting( true );
1404:
1405: $user_count_result = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users " );
1406:
1407: if ( 0 == $user_count_result ) {
1408: return true;
1409: }
1410:
1411: if ( 0 == $offset ) {
1412: $current_page = 1;
1413: } else {
1414: $current_page = intval( $offset / $n );
1415: }
1416:
1417: $total_pages = ceil( $user_count_result / $n );
1418:
1419:
1420: $meta_list = $wpdb->get_results( "SELECT $wpdb->postmeta.post_id, $wpdb->postmeta.meta_value FROM $wpdb->postmeta INNER JOIN $wpdb->posts ON ($wpdb->posts.ID = $wpdb->postmeta.post_id) WHERE $wpdb->posts.post_type = 'lesson' AND $wpdb->postmeta.meta_key = '_lesson_course'", ARRAY_A );
1421: $course_lesson_ids = array();
1422: if ( !empty($meta_list) ) {
1423: foreach ( $meta_list as $metarow ) {
1424: $lesson_id = $metarow['post_id'];
1425: $course_id = $metarow['meta_value'];
1426: $course_lesson_ids[ $course_id ][] = $lesson_id;
1427: }
1428: }
1429:
1430: $users_sql = "SELECT ID FROM $wpdb->users ORDER BY ID ASC LIMIT %d OFFSET %d";
1431: $start_sql = "SELECT comment_post_ID, comment_date FROM $wpdb->comments WHERE comment_type = 'sensei_course_start' AND user_id = %d GROUP BY comment_post_ID ";
1432: $lessons_sql = "SELECT comment_approved AS status, comment_date FROM $wpdb->comments WHERE comment_type = 'sensei_lesson_status' AND user_id = %d AND comment_post_ID IN ( %s ) GROUP BY comment_post_ID ORDER BY comment_date_gmt DESC ";
1433: $check_existing_sql = "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d AND user_id = %d AND comment_type = 'sensei_course_status' ";
1434:
1435:
1436: $user_ids = $wpdb->get_col( $wpdb->prepare($users_sql, $n, $offset) );
1437:
1438: foreach ( $user_ids AS $user_id ) {
1439:
1440:
1441: $course_starts = $wpdb->get_results( $wpdb->prepare($start_sql, $user_id), ARRAY_A );
1442: foreach ( $course_starts as $course_log ) {
1443:
1444: $course_id = $course_log['comment_post_ID'];
1445:
1446:
1447: $status = 'complete';
1448:
1449: $status_date = $course_log['comment_date'];
1450:
1451: $meta_data = array(
1452: 'start' => $status_date,
1453: 'complete' => 0,
1454: 'percent' => 0,
1455: );
1456:
1457: if ( !empty( $course_lesson_ids[$course_id] ) ) {
1458:
1459: $lessons_completed = 0;
1460: $total_lessons = count( $course_lesson_ids[ $course_id ] );
1461:
1462:
1463: $sql = sprintf($lessons_sql, $user_id, join(', ', $course_lesson_ids[ $course_id ]) );
1464:
1465: $lesson_statuses = $wpdb->get_results( $sql, ARRAY_A );
1466:
1467: if ( $total_lessons > count($lesson_statuses) ) {
1468: $status = 'in-progress';
1469: }
1470:
1471: foreach ( $lesson_statuses as $lesson_status ) {
1472: $status_date = $lesson_status['comment_date'];
1473: switch ( $lesson_status['status'] ) {
1474: case 'complete':
1475: case 'graded':
1476: case 'passed':
1477: $lessons_completed++;
1478: break;
1479:
1480: case 'in-progress':
1481: case 'ungraded':
1482: case 'failed':
1483: $status = 'in-progress';
1484: break;
1485: }
1486: }
1487: $meta_data['complete'] = $lessons_completed;
1488: $meta_data['percent'] = abs( round( ( doubleval( $lessons_completed ) * 100 ) / ( $total_lessons ), 0 ) );
1489: }
1490: else {
1491:
1492: $status = 'in-progress';
1493: }
1494: $data = array(
1495:
1496: 'comment_post_ID' => $course_id,
1497: 'comment_approved' => $status,
1498: 'comment_type' => 'sensei_course_status',
1499: 'comment_date' => $status_date,
1500: 'user_id' => $user_id,
1501: 'comment_date_gmt' => get_gmt_from_date($status_date),
1502: 'comment_author' => '',
1503: );
1504:
1505: $sql = $wpdb->prepare( $check_existing_sql, $course_id, $user_id );
1506: $comment_ID = $wpdb->get_var( $sql );
1507: if ( !$comment_ID ) {
1508:
1509: $wpdb->insert($wpdb->comments, $data);
1510: $comment_ID = (int) $wpdb->insert_id;
1511:
1512: if ( $comment_ID && !empty($meta_data) ) {
1513: foreach ( $meta_data as $key => $value ) {
1514:
1515: if ( $wpdb->get_var( $wpdb->prepare(
1516: "SELECT COUNT(*) FROM $wpdb->commentmeta WHERE comment_id = %d AND meta_key = %s ",
1517: $comment_ID, $key ) ) ) {
1518: continue;
1519: }
1520: $result = $wpdb->insert( $wpdb->commentmeta, array(
1521: 'comment_id' => $comment_ID,
1522: 'meta_key' => $key,
1523: 'meta_value' => $value
1524: ) );
1525: }
1526: }
1527: }
1528: }
1529: }
1530: $wpdb->flush();
1531:
1532: if ( $current_page == $total_pages ) {
1533: return true;
1534: } else {
1535: return false;
1536: }
1537: }
1538:
1539: 1540: 1541: 1542: 1543: 1544: 1545: 1546: 1547:
1548: function status_changes_repair_course_statuses( $n = 50, $offset = 0 ) {
1549: global $wpdb;
1550:
1551: $count_object = wp_count_posts( 'lesson' );
1552: $count_published = $count_object->publish;
1553:
1554: if ( 0 == $count_published ) {
1555: return true;
1556: }
1557:
1558:
1559: if ( 0 == $offset ) {
1560: $current_page = 1;
1561: } else {
1562: $current_page = intval( $offset / $n );
1563: }
1564: $total_pages = ceil( $count_published / $n );
1565:
1566: $course_lesson_ids = $lesson_user_statuses = array();
1567:
1568:
1569: $meta_list = $wpdb->get_results( "SELECT $wpdb->postmeta.post_id, $wpdb->postmeta.meta_value FROM $wpdb->postmeta INNER JOIN $wpdb->posts ON ($wpdb->posts.ID = $wpdb->postmeta.post_id) WHERE $wpdb->posts.post_type = 'lesson' AND $wpdb->postmeta.meta_key = '_lesson_course' LIMIT $n OFFSET $offset ", ARRAY_A );
1570: if ( !empty($meta_list) ) {
1571: foreach ( $meta_list as $metarow ) {
1572: $lesson_id = $metarow['post_id'];
1573: $course_id = $metarow['meta_value'];
1574: $course_lesson_ids[ $course_id ][] = $lesson_id;
1575: }
1576: }
1577:
1578:
1579: $status_list = $wpdb->get_results( "SELECT user_id, comment_post_ID, comment_approved FROM $wpdb->comments WHERE comment_type = 'sensei_lesson_status' GROUP BY user_id, comment_post_ID ", ARRAY_A );
1580: if ( !empty($status_list) ) {
1581: foreach ( $status_list as $status ) {
1582: $lesson_user_statuses[ $status['comment_post_ID'] ][ $status['user_id'] ] = $status['comment_approved'];
1583: }
1584: }
1585:
1586: $course_completion = Sensei()->settings->settings[ 'course_completion' ];
1587:
1588: $per_page = 40;
1589: $comment_id_offset = $count = 0;
1590:
1591: $course_sql = "SELECT * FROM $wpdb->comments WHERE comment_type = 'sensei_course_status' AND comment_ID > %d LIMIT $per_page";
1592:
1593: while ( $course_statuses = $wpdb->get_results( $wpdb->prepare($course_sql, $comment_id_offset) ) ) {
1594:
1595: foreach ( $course_statuses AS $course_status ) {
1596: $user_id = $course_status->user_id;
1597: $course_id = $course_status->comment_post_ID;
1598: $total_lessons = count( $course_lesson_ids[ $course_id ] );
1599: if ( $total_lessons <= 0 ) {
1600: $total_lessons = 1;
1601: }
1602: $lessons_completed = 0;
1603: $status = 'in-progress';
1604:
1605:
1606: if ( !empty($course_lesson_ids[ $course_id ]) ) {
1607: foreach( $course_lesson_ids[ $course_id ] AS $lesson_id ) {
1608: $lesson_status = $lesson_user_statuses[ $lesson_id ][ $user_id ];
1609:
1610: if ( 'passed' != $course_completion ) {
1611: switch ( $lesson_status ) {
1612:
1613: case 'in-progress':
1614: case 'ungraded':
1615: break;
1616:
1617: default:
1618: $lessons_completed++;
1619: break;
1620: }
1621: }
1622: else {
1623: switch ( $lesson_status ) {
1624: case 'complete':
1625: case 'graded':
1626: case 'passed':
1627: $lessons_completed++;
1628: break;
1629:
1630:
1631: case 'failed':
1632: default:
1633: break;
1634: }
1635: }
1636: }
1637: }
1638: if ( $lessons_completed == $total_lessons ) {
1639: $status = 'complete';
1640: }
1641:
1642: $metadata = array(
1643: 'complete' => $lessons_completed,
1644: 'percent' => abs( round( ( doubleval( $lessons_completed ) * 100 ) / ( $total_lessons ), 0 ) ),
1645: );
1646: Sensei_Utils::update_course_status( $user_id, $course_id, $status, $metadata );
1647: $count++;
1648:
1649: }
1650: $comment_id_offset = $course_status->comment_ID;
1651: }
1652:
1653: if ( $current_page == $total_pages ) {
1654: return true;
1655: } else {
1656: return false;
1657: }
1658: }
1659:
1660: function status_changes_convert_questions( $n = 50, $offset = 0 ) {
1661: global $wpdb;
1662:
1663: wp_defer_comment_counting( true );
1664:
1665: $user_count_result = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users " );
1666:
1667: if ( 0 == $user_count_result ) {
1668: return true;
1669: }
1670:
1671:
1672: if ( 0 == $offset ) {
1673: $current_page = 1;
1674: } else {
1675: $current_page = intval( $offset / $n );
1676: }
1677:
1678: $total_pages = ceil( $user_count_result / $n );
1679:
1680: $users_sql = "SELECT ID FROM $wpdb->users ORDER BY ID ASC LIMIT %d OFFSET %d";
1681: $answers_sql = "SELECT * FROM $wpdb->comments WHERE comment_type = 'sensei_user_answer' AND user_id = %d GROUP BY comment_post_ID ";
1682: $grades_sql = "SELECT comment_post_ID, comment_content FROM $wpdb->comments WHERE comment_type = 'sensei_user_grade' AND user_id = %d GROUP BY comment_post_ID ";
1683: $notes_sql = "SELECT comment_post_ID, comment_content FROM $wpdb->comments WHERE comment_type = 'sensei_answer_notes' AND user_id = %d GROUP BY comment_post_ID ";
1684:
1685: $user_ids = $wpdb->get_col( $wpdb->prepare($users_sql, $n, $offset) );
1686:
1687: foreach ( $user_ids AS $user_id ) {
1688:
1689: $answer_grades = $answer_notes = array();
1690:
1691:
1692: $_answer_grades = $wpdb->get_results( $wpdb->prepare($grades_sql, $user_id), ARRAY_A );
1693: foreach ( $_answer_grades as $answer_grade ) {
1694:
1695: $answer_grades[ $answer_grade['comment_post_ID'] ] = $answer_grade['comment_content'];
1696: }
1697: unset( $_answer_grades );
1698:
1699:
1700: $_answer_notes = $wpdb->get_results( $wpdb->prepare($notes_sql, $user_id), ARRAY_A );
1701: foreach ( $_answer_notes as $answer_note ) {
1702:
1703: $answer_notes[ $answer_note['comment_post_ID'] ] = $answer_note['comment_content'];
1704: }
1705: unset( $_answer_notes );
1706:
1707:
1708: $sql = $wpdb->prepare($answers_sql, $user_id);
1709: $answers = $wpdb->get_results( $sql, ARRAY_A );
1710: foreach ( $answers as $answer ) {
1711:
1712:
1713: $answer = wp_slash($answer);
1714:
1715: $comment_ID = $answer['comment_ID'];
1716:
1717: $meta_data = array();
1718:
1719:
1720: if ( !empty($answer_grades[ $answer['comment_post_ID'] ]) ) {
1721: $meta_data['user_grade'] = $answer_grades[ $answer['comment_post_ID'] ];
1722: }
1723:
1724: if ( !empty($answer_notes[ $answer['comment_post_ID'] ]) ) {
1725: $meta_data['answer_note'] = $answer_notes[ $answer['comment_post_ID'] ];
1726: }
1727:
1728:
1729: $data = array(
1730: 'comment_author' => '',
1731: 'comment_author_email' => '',
1732: 'comment_author_url' => '',
1733: 'comment_author_IP' => '',
1734: 'comment_agent' => '',
1735: );
1736: $data = array_merge($answer, $data);
1737:
1738: $rval = $wpdb->update( $wpdb->comments, $data, compact( 'comment_ID' ) );
1739: if ( $rval ) {
1740: if ( !empty($meta_data) ) {
1741: foreach ( $meta_data as $key => $value ) {
1742:
1743: if ( $wpdb->get_var( $wpdb->prepare(
1744: "SELECT COUNT(*) FROM $wpdb->commentmeta WHERE comment_id = %d AND meta_key = %s ",
1745: $comment_ID, $key ) ) ) {
1746: continue;
1747: }
1748: $result = $wpdb->insert( $wpdb->commentmeta, array(
1749: 'comment_id' => $comment_ID,
1750: 'meta_key' => $key,
1751: 'meta_value' => $value
1752: ) );
1753: }
1754: }
1755: }
1756: }
1757: }
1758: $wpdb->flush();
1759:
1760: if ( $current_page == $total_pages ) {
1761: return true;
1762: } else {
1763: return false;
1764: }
1765: }
1766:
1767: 1768: 1769: 1770: 1771: 1772:
1773: public function update_legacy_sensei_comments_status() {
1774: global $wpdb;
1775:
1776:
1777: $wpdb->query( "UPDATE $wpdb->comments SET comment_approved = 'log' WHERE comment_type = 'sensei_user_answer' " );
1778:
1779:
1780: $wpdb->query( "UPDATE $wpdb->comments SET comment_approved = 'legacy' WHERE comment_type IN ('sensei_course_start', 'sensei_course_end', 'sensei_lesson_start', 'sensei_lesson_end', 'sensei_quiz_asked', 'sensei_user_grade', 'sensei_answer_notes', 'sensei_quiz_grade') " );
1781:
1782: return true;
1783: }
1784:
1785: 1786: 1787: 1788: 1789: 1790: 1791: 1792:
1793: public function update_comment_course_lesson_comment_counts( $n = 50, $offset = 0 ) {
1794: global $wpdb;
1795:
1796: $item_count_result = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type IN ('course', 'lesson') " );
1797:
1798: if ( 0 == $item_count_result ) {
1799: return true;
1800: }
1801:
1802:
1803: if ( 0 == $offset ) {
1804: $current_page = 1;
1805: } else {
1806: $current_page = intval( $offset / $n );
1807: }
1808:
1809: $total_pages = ceil( $item_count_result / $n );
1810:
1811:
1812: $items = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type IN ('course', 'lesson') LIMIT %d OFFSET %d", $n, $offset ) );
1813: foreach ( (array) $items as $post ) {
1814:
1815: $new = (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post->ID) );
1816: $wpdb->update( $wpdb->posts, array('comment_count' => $new), array('ID' => $post->ID) );
1817:
1818: clean_post_cache( $post->ID );
1819: }
1820:
1821: if ( $current_page == $total_pages ) {
1822: return true;
1823: } else {
1824: return false;
1825: }
1826: }
1827:
1828: public function remove_legacy_comments () {
1829: global $wpdb;
1830:
1831: $result = $wpdb->delete( $wpdb->comments, array( 'comment_approved' => 'legacy' ) );
1832:
1833: return true;
1834: }
1835:
1836: public function index_comment_status_field () {
1837: global $wpdb;
1838:
1839: $wpdb->query("ALTER TABLE `$wpdb->comments` ADD INDEX `comment_type` ( `comment_type` )");
1840: $wpdb->query("ALTER TABLE `$wpdb->comments` ADD INDEX `comment_type_user_id` ( `comment_type`, `user_id` )");
1841:
1842: return true;
1843:
1844:
1845: }
1846:
1847: 1848: 1849: 1850: 1851: 1852: 1853: 1854:
1855: public function enhance_teacher_role ( ) {
1856:
1857: require_once('class-sensei-teacher.php');
1858: $teacher = new Sensei_Teacher();
1859: $teacher->create_role();
1860: return true;
1861:
1862: }
1863:
1864: }
1865:
1866: 1867: 1868: 1869: 1870:
1871: class WooThemes_Sensei_Updates extends Sensei_Updates {}
1872: