Tutor LMS – eLearning and online course solution - Version 1.6.3

Version Description

  • 20 May, 2020 =

New: Now students can answer Q&A from the frontend New: Set answer word limit for Open-Ended and Essay type questions New: Replace the login error message for an incorrect password with your own custom message Update: Course single page overview tab renamed to resources page. Fix: Randomize feature not working for answering options Fix: Close login popup without show any error message. Fix: Password/username validation messages and UX Fix: Social share content issue. Fix: Course duration and video playback time validation.

Download this release

Release Info

Developer themeum
Plugin Icon wp plugin Tutor LMS – eLearning and online course solution
Version 1.6.3
Comparing to
See all releases

Code changes from version 1.6.2 to 1.6.3

assets/js/tutor-front.js CHANGED
@@ -1,13 +1,13 @@
1
- jQuery(document).ready(function($){
2
  'use strict';
3
 
4
  /**
5
  * Initiate Select2
6
  * @since v.1.3.4
7
  */
8
- if (jQuery().select2){
9
  $('.tutor_select2').select2({
10
- escapeMarkup : function(markup) {
11
  return markup;
12
  }
13
  });
@@ -25,14 +25,14 @@ jQuery(document).ready(function($){
25
  * jquery.ui.widget.js
26
  * jquery.ui.mouse.js
27
  */
28
- !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);
29
 
30
  /**
31
  * END jQuery UI Touch Punch
32
  */
33
 
34
 
35
- $(document).on('change', '.tutor-course-filter-form', function(e){
36
  e.preventDefault();
37
  $(this).closest('form').submit();
38
  });
@@ -40,16 +40,16 @@ jQuery(document).ready(function($){
40
  const videoPlayer = {
41
  ajaxurl: _tutorobject.ajaxurl,
42
  nonce_key: _tutorobject.nonce_key,
43
- video_data: function() {
44
  const video_track_data = $('#tutor_video_tracking_information').val();
45
  return video_track_data ? JSON.parse(video_track_data) : {};
46
  },
47
- track_player: function() {
48
  const that = this;
49
  if (typeof Plyr !== 'undefined') {
50
  const player = new Plyr('#tutorPlayer');
51
  const video_data = that.video_data();
52
- player.on('ready', function(event){
53
  const instance = event.detail.plyr;
54
  const { best_watch_time } = video_data;
55
  if (best_watch_time > 0 && instance.duration > Math.round(best_watch_time)) {
@@ -60,49 +60,49 @@ jQuery(document).ready(function($){
60
 
61
  let tempTimeNow = 0;
62
  let intervalSeconds = 30; //Send to tutor backend about video playing time in this interval
63
- player.on('timeupdate', function(event) {
64
  const instance = event.detail.plyr;
65
  const tempTimeNowInSec = (tempTimeNow / 4); //timeupdate firing 250ms interval
66
- if (tempTimeNowInSec >= intervalSeconds){
67
  that.sync_time(instance);
68
  tempTimeNow = 0;
69
  }
70
  tempTimeNow++;
71
  });
72
 
73
- player.on('ended', function(event) {
74
  const video_data = that.video_data();
75
  const instance = event.detail.plyr;
76
- const data = {is_ended:true};
77
  that.sync_time(instance, data);
78
- if(video_data.autoload_next_course_content) {
79
  that.autoload_content();
80
  }
81
  });
82
  }
83
  },
84
- sync_time: function(instance, options) {
85
  const post_id = this.video_data().post_id;
86
  //TUTOR is sending about video playback information to server.
87
- let data = {action: 'sync_video_playback', currentTime: instance.currentTime, duration:instance.duration, post_id};
88
  data[this.nonce_key] = _tutorobject[this.nonce_key];
89
  let data_send = data;
90
- if(options){
91
  data_send = Object.assign(data, options);
92
  }
93
  $.post(this.ajaxurl, data_send);
94
  },
95
- autoload_content: function() {
96
  const post_id = this.video_data().post_id;
97
- const data = {action: 'autoload_next_course_content', post_id};
98
  data[this.nonce_key] = _tutorobject[this.nonce_key];
99
- $.post(this.ajaxurl, data).done(function(response) {
100
- if(response.success && response.data.next_url) {
101
  location.href = response.data.next_url;
102
  }
103
  });
104
  },
105
- init: function() {
106
  this.track_player();
107
  }
108
  };
@@ -111,11 +111,11 @@ jQuery(document).ready(function($){
111
  * Fire TUTOR video
112
  * @since v.1.0.0
113
  */
114
- if ($('#tutorPlayer').length){
115
  videoPlayer.init();
116
  }
117
 
118
- $(document).on('change keyup paste', '.tutor_user_name', function(){
119
  $(this).val(tutor_slugify($(this).val()));
120
  });
121
 
@@ -131,16 +131,16 @@ jQuery(document).ready(function($){
131
  /**
132
  * Hover tutor rating and set value
133
  */
134
- $(document).on('hover', '.tutor-write-review-box .tutor-star-rating-group i', function(){
135
  $(this).closest('.tutor-star-rating-group').find('i').removeClass('tutor-icon-star-full').addClass('tutor-icon-star-line');
136
  var currentRateValue = $(this).attr('data-rating-value');
137
- for (var i = 1; i<= currentRateValue; i++){
138
- $(this).closest('.tutor-star-rating-group').find('i[data-rating-value="'+i+'"]').removeClass('tutor-icon-star-line').addClass('tutor-icon-star-full');
139
  }
140
  $(this).closest('.tutor-star-rating-group').find('input[name="tutor_rating_gen_input"]').val(currentRateValue);
141
  });
142
 
143
- $(document).on('click', '.tutor-star-rating-group i', function(){
144
  var rating = $(this).attr('data-rating-value');
145
  $(this).closest('.tutor-star-rating-group').find('input[name="tutor_rating_gen_input"]').val(rating);
146
  });
@@ -153,7 +153,7 @@ jQuery(document).ready(function($){
153
  review = review.trim();
154
 
155
  var course_id = $('input[name="tutor_course_id"]').val();
156
- var data = {course_id : course_id, rating : rating, review:review, action: 'tutor_place_rating' };
157
 
158
  if (review) {
159
  $.ajax({
@@ -166,32 +166,32 @@ jQuery(document).ready(function($){
166
  success: function (data) {
167
  var review_id = data.data.review_id;
168
  var review = data.data.review;
169
- $('.tutor-review-'+review_id+' .review-content').html(review);
170
  location.reload();
171
  }
172
  });
173
  }
174
  });
175
 
176
- $(document).on('click', '.write-course-review-link-btn', function(e){
177
  e.preventDefault();
178
  $(this).siblings('.tutor-write-review-form').slideToggle();
179
  });
180
 
181
- $(document).on('click', '.tutor-ask-question-btn', function(e){
182
  e.preventDefault();
183
  $('.tutor-add-question-wrap').slideToggle();
184
  });
185
- $(document).on('click', '.tutor_question_cancel', function(e){
186
  e.preventDefault();
187
  $('.tutor-add-question-wrap').toggle();
188
  });
189
 
190
- $(document).on('submit', '#tutor-ask-question-form', function(e){
191
  e.preventDefault();
192
 
193
  var $form = $(this);
194
- var data = $(this).serialize()+'&action=tutor_ask_question';
195
 
196
  $.ajax({
197
  url: _tutorobject.ajaxurl,
@@ -201,7 +201,7 @@ jQuery(document).ready(function($){
201
  $form.find('.tutor_ask_question_btn').addClass('updating-icon');
202
  },
203
  success: function (data) {
204
- if (data.success){
205
  $('.tutor-add-question-wrap').hide();
206
  window.location.reload();
207
  }
@@ -212,11 +212,11 @@ jQuery(document).ready(function($){
212
  });
213
  });
214
 
215
- $(document).on('submit', '.tutor-add-answer-form', function(e){
216
  e.preventDefault();
217
 
218
  var $form = $(this);
219
- var data = $(this).serialize()+'&action=tutor_add_answer';
220
 
221
  $.ajax({
222
  url: _tutorobject.ajaxurl,
@@ -226,7 +226,7 @@ jQuery(document).ready(function($){
226
  $form.find('.tutor_add_answer_btn').addClass('updating-icon');
227
  },
228
  success: function (data) {
229
- if (data.success){
230
  window.location.reload();
231
  }
232
  },
@@ -236,29 +236,29 @@ jQuery(document).ready(function($){
236
  });
237
  });
238
 
239
- $(document).on('focus', '.tutor_add_answer_textarea', function(e){
240
  e.preventDefault();
241
 
242
  var question_id = $(this).closest('.tutor_add_answer_wrap').attr('data-question-id');
243
  var conf = {
244
  tinymce: {
245
- wpautop:true,
246
  //plugins : 'charmap colorpicker compat3x directionality fullscreen hr image lists media paste tabfocus textcolor wordpress wpautoresize wpdialogs wpeditimage wpemoji wpgallery wplink wptextpattern wpview',
247
  toolbar1: 'bold italic underline bullist strikethrough numlist blockquote alignleft aligncenter alignright undo redo link unlink spellchecker fullscreen'
248
  },
249
  };
250
- wp.editor.initialize('tutor_answer_'+question_id, conf);
251
  });
252
 
253
- $(document).on('click', '.tutor_cancel_wp_editor', function(e){
254
  e.preventDefault();
255
  $(this).closest('.tutor_wp_editor_wrap').toggle();
256
  $(this).closest('.tutor_add_answer_wrap').find('.tutor_wp_editor_show_btn_wrap').toggle();
257
  var question_id = $(this).closest('.tutor_add_answer_wrap').attr('data-question-id');
258
- wp.editor.remove('tutor_answer_'+question_id);
259
  });
260
 
261
- $(document).on('click', '.tutor_wp_editor_show_btn', function(e){
262
  e.preventDefault();
263
  $(this).closest('.tutor_add_answer_wrap').find('.tutor_wp_editor_wrap').toggle();
264
  $(this).closest('.tutor_wp_editor_show_btn_wrap').toggle();
@@ -269,7 +269,7 @@ jQuery(document).ready(function($){
269
  */
270
  var $tutor_quiz_time_update = $('#tutor-quiz-time-update');
271
  var attempt_settings = null;
272
- if ($tutor_quiz_time_update.length){
273
  attempt_settings = JSON.parse($tutor_quiz_time_update.attr('data-attempt-settings'));
274
  var attempt_meta = JSON.parse($tutor_quiz_time_update.attr('data-attempt-meta'));
275
 
@@ -322,7 +322,7 @@ jQuery(document).ready(function($){
322
 
323
  var quiz_id = $('#tutor_quiz_id').val();
324
  var tutor_quiz_remaining_time_secs = $('#tutor_quiz_remaining_time_secs').val();
325
- var quiz_timeout_data = {quiz_id: quiz_id, action: 'tutor_quiz_timeout'};
326
 
327
  $.ajax({
328
  url: _tutorobject.ajaxurl,
@@ -344,14 +344,14 @@ jQuery(document).ready(function($){
344
  time_now = time_now + 1000;
345
  $tutor_quiz_time_update.html(countdown_human);
346
  }, 1000);
347
- }else{
348
  $tutor_quiz_time_update.closest('.time-remaining').remove();
349
  }
350
  }
351
 
352
  var $quiz_start_form = $('#tutor-quiz-body form#tutor-start-quiz');
353
- if ($quiz_start_form.length){
354
- if (_tutorobject.quiz_options.quiz_auto_start === '1'){
355
  $quiz_start_form.submit();
356
  }
357
  }
@@ -360,7 +360,7 @@ jQuery(document).ready(function($){
360
  * Quiz Frontend Review Action
361
  * @since 1.4.0
362
  */
363
- $(document).on('click', '.quiz-manual-review-action', function(e){
364
  e.preventDefault();
365
  var $that = $(this),
366
  attempt_id = $that.attr('data-attempt-id'),
@@ -368,9 +368,9 @@ jQuery(document).ready(function($){
368
  mark_as = $that.attr('data-mark-as');
369
 
370
  $.ajax({
371
- url : _tutorobject.ajaxurl,
372
- type : 'GET',
373
- data : {action:'review_quiz_answer', attempt_id: attempt_id, attempt_answer_id : attempt_answer_id, mark_as : mark_as },
374
  beforeSend: function () {
375
  $that.find('i').addClass('updating-icon');
376
  },
@@ -405,18 +405,18 @@ jQuery(document).ready(function($){
405
  $.ajax({
406
  url: _tutorobject.ajaxurl,
407
  type: 'POST',
408
- data: {course_id : course_id, 'action': 'tutor_course_add_to_wishlist'},
409
  beforeSend: function () {
410
  $that.addClass('updating-icon');
411
  },
412
  success: function (data) {
413
- if (data.success){
414
- if (data.data.status === 'added'){
415
  $that.addClass('has-wish-listed');
416
- }else{
417
  $that.removeClass('has-wish-listed');
418
  }
419
- }else{
420
  window.location = data.data.redirect_to;
421
  }
422
  },
@@ -434,7 +434,7 @@ jQuery(document).ready(function($){
434
  *
435
  * @updated v.1.4.0
436
  */
437
- if ( ! _tutorobject.enable_lesson_classic_editor) {
438
 
439
  $(document).on('click', '.tutor-single-lesson-a', function (e) {
440
  e.preventDefault();
@@ -446,7 +446,7 @@ jQuery(document).ready(function($){
446
  $.ajax({
447
  url: _tutorobject.ajaxurl,
448
  type: 'POST',
449
- data: {lesson_id : lesson_id, 'action': 'tutor_render_lesson_content'},
450
  beforeSend: function () {
451
  var page_title = $that.find('.lesson_title').text();
452
  $('head title').text(page_title);
@@ -477,7 +477,7 @@ jQuery(document).ready(function($){
477
  $.ajax({
478
  url: _tutorobject.ajaxurl,
479
  type: 'POST',
480
- data: {quiz_id : quiz_id, 'action': 'tutor_render_quiz_content'},
481
  beforeSend: function () {
482
  $('head title').text(page_title);
483
  window.history.pushState('obj', page_title, $that.attr('href'));
@@ -501,7 +501,7 @@ jQuery(document).ready(function($){
501
  * @date 05 Feb, 2019
502
  */
503
 
504
- $(document).on('click', '.tutor-lesson-sidebar-hide-bar', function(e){
505
  e.preventDefault();
506
  $('.tutor-lesson-sidebar').toggle();
507
  $('#tutor-single-entry-content').toggleClass("sidebar-hidden");
@@ -561,7 +561,7 @@ jQuery(document).ready(function($){
561
  */
562
 
563
  var validated = tutor_quiz_validation($question_wrap);
564
- if ( ! validated){
565
  return;
566
  }
567
 
@@ -579,9 +579,9 @@ jQuery(document).ready(function($){
579
  * If pagination exists, set active class
580
  */
581
 
582
- if ($('.tutor-quiz-questions-pagination').length){
583
  $('.tutor-quiz-question-paginate-item').removeClass('active');
584
- $('.tutor-quiz-questions-pagination a[href="'+next_question_id+'"]').addClass('active');
585
  }
586
 
587
  }
@@ -592,13 +592,13 @@ jQuery(document).ready(function($){
592
  $(document).on('submit', '#tutor-answering-quiz', function (e) {
593
  var $questions_wrap = $('.quiz-attempt-single-question');
594
  var validated = true;
595
- if ($questions_wrap.length){
596
- $questions_wrap.each(function(index, question){
597
  validated = tutor_quiz_validation($(question));
598
  });
599
  }
600
 
601
- if ( ! validated){
602
  e.preventDefault();
603
  }
604
 
@@ -620,13 +620,13 @@ jQuery(document).ready(function($){
620
  /**
621
  * Limit Short Answer Question Type
622
  */
623
- $(document).on('keyup', 'textarea.question_type_short_answer', function (e) {
624
  var $that = $(this);
625
  var value = $that.val();
626
- var limit = _tutorobject.quiz_options.short_answer_characters_limit;
627
  var remaining = limit - value.length;
628
 
629
- if (remaining < 1){
630
  $that.val(value.substr(0, limit));
631
  remaining = 0;
632
  }
@@ -643,12 +643,12 @@ jQuery(document).ready(function($){
643
  * @since v.1.4.4
644
  */
645
  var countDraggableAnswers = $('.quiz-draggable-rand-answers').length;
646
- if (countDraggableAnswers){
647
- $('.quiz-draggable-rand-answers').each(function(){
648
  var $that = $(this);
649
  var draggableDivHeight = $that.height();
650
 
651
- $that.css({"height": draggableDivHeight});
652
  });
653
  }
654
 
@@ -659,13 +659,13 @@ jQuery(document).ready(function($){
659
  * @since v.1.6.1
660
  */
661
 
662
- function tutor_quiz_validation($question_wrap){
663
  var validated = true;
664
 
665
  var $required_answer_wrap = $question_wrap.find('.quiz-answer-required');
666
 
667
 
668
- if ($required_answer_wrap.length){
669
 
670
  /**
671
  * Radio field validation
@@ -675,22 +675,22 @@ jQuery(document).ready(function($){
675
  * @since v.1.6.1
676
  */
677
  var $inputs = $required_answer_wrap.find('input');
678
- if ($inputs.length){
679
  var $type = $inputs.attr('type');
680
  if ($type === 'radio') {
681
  if ($required_answer_wrap.find('input[type="radio"]:checked').length == 0) {
682
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">Please select an option to answer</p>');
683
  validated = false;
684
  }
685
- }else if ($type === 'checkbox'){
686
  if ($required_answer_wrap.find('input[type="checkbox"]:checked').length == 0) {
687
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">Please select at least one option to answer.</p>');
688
  validated = false;
689
  }
690
- }else if($type === 'text'){
691
  //Fill in the gaps if many, validation all
692
- $inputs.each(function(index, input){
693
- if ( ! $(input).val().trim().length){
694
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">The answer for this question is required</p>');
695
  validated = false;
696
  }
@@ -699,7 +699,7 @@ jQuery(document).ready(function($){
699
 
700
  }
701
  if ($required_answer_wrap.find('textarea').length) {
702
- if ($required_answer_wrap.find('textarea').val().trim().length < 1){
703
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">The answer for this question is required</p>');
704
  validated = false;
705
  }
@@ -709,10 +709,10 @@ jQuery(document).ready(function($){
709
  * Matching Question
710
  */
711
  var $matchingDropable = $required_answer_wrap.find('.quiz-answer-matching-droppable');
712
- if ($matchingDropable.length){
713
 
714
- $matchingDropable.each(function(index, matching){
715
- if ( ! $(matching).find('.quiz-draggable-answer-item').length){
716
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">Please match all the items</p>');
717
  validated = false;
718
  }
@@ -757,7 +757,7 @@ jQuery(document).ready(function($){
757
  *
758
  * @since v.1.0.4
759
  */
760
- if($.fn.ShareLink){
761
  var $social_share_wrap = $('.tutor-social-share-wrap');
762
  if ($social_share_wrap.length) {
763
  var share_config = JSON.parse($social_share_wrap.attr('data-social-share-config'));
@@ -778,8 +778,8 @@ jQuery(document).ready(function($){
778
  *
779
  * @since v.1.1.2
780
  */
781
- if (jQuery.datepicker){
782
- $( ".tutor_report_datepicker" ).datepicker({"dateFormat" : 'yy-mm-dd'});
783
  }
784
 
785
 
@@ -789,17 +789,17 @@ jQuery(document).ready(function($){
789
  * @since v.1.1.2
790
  */
791
 
792
- $(".withdraw-method-select-input").on('change', function(e){
793
  var $that = $(this);
794
  $('.withdraw-method-form').hide();
795
- $('#withdraw-method-form-'+$that.closest('.withdraw-method-select').attr('data-withdraw-method')).show();
796
  });
797
 
798
  $('.withdraw-method-select-input').each(function () {
799
  var $that = $(this);
800
- if($that.is(":checked")){
801
  $('.withdraw-method-form').hide();
802
- $('#withdraw-method-form-'+$that.closest('.withdraw-method-select').attr('data-withdraw-method')).show();
803
  }
804
  });
805
 
@@ -810,7 +810,7 @@ jQuery(document).ready(function($){
810
  *
811
  * @since v.1.2.0
812
  */
813
- $(document).on('submit', '#tutor-withdraw-account-set-form', function(e){
814
  e.preventDefault();
815
 
816
  var $form = $(this);
@@ -826,8 +826,8 @@ jQuery(document).ready(function($){
826
  $btn.addClass('updating-icon');
827
  },
828
  success: function (data) {
829
- if (data.success){
830
- var successMsg = '<div class="tutor-success-msg" style="display: none;"><i class="tutor-icon-mark"></i> '+data.data.msg+' </div>';
831
  $btn.closest('.withdraw-account-save-btn-wrap').append(successMsg);
832
  if ($form.find('.tutor-success-msg').length) {
833
  $form.find('.tutor-success-msg').slideDown();
@@ -849,12 +849,12 @@ jQuery(document).ready(function($){
849
  * @since v.1.2.0
850
  */
851
 
852
- $(document).on('click', 'a.open-withdraw-form-btn', function(e){
853
  e.preventDefault();
854
  $('.tutor-earning-withdraw-form-wrap').slideToggle();
855
  });
856
 
857
- $(document).on('submit', '#tutor-earning-withdraw-form', function(e){
858
  e.preventDefault();
859
 
860
  var $form = $(this);
@@ -872,15 +872,15 @@ jQuery(document).ready(function($){
872
  },
873
  success: function (data) {
874
  var Msg;
875
- if (data.success){
876
 
877
  if (data.data.available_balance !== 'undefined') {
878
  $('.withdraw-balance-col .available_balance').html(data.data.available_balance);
879
  }
880
- Msg = '<div class="tutor-success-msg"><i class="tutor-icon-mark"></i> '+data.data.msg+' </div>';
881
 
882
- }else{
883
- Msg = '<div class="tutor-error-msg"><i class="tutor-icon-line-cross"></i> '+data.data.msg+' </div>';
884
  }
885
 
886
  $responseDiv.html(Msg);
@@ -898,7 +898,7 @@ jQuery(document).ready(function($){
898
  frontEndModal.each(function () {
899
  var modal = $(this),
900
  action = $(this).data('popup-rel');
901
- $('[href="'+action+'"]').on('click', function (e) {
902
  modal.fadeIn();
903
  e.preventDefault();
904
  });
@@ -930,8 +930,8 @@ jQuery(document).ready(function($){
930
  $btn.addClass('updating-icon');
931
  },
932
  success: function (data) {
933
- if (data.success){
934
- $('#tutor-dashboard-course-'+course_id).remove();
935
  }
936
  },
937
  complete: function () {
@@ -945,37 +945,34 @@ jQuery(document).ready(function($){
945
  * Frontend Profile
946
  */
947
 
948
- if (! $('#tutor_profile_photo_id').val()) {
949
  $('.tutor-profile-photo-delete-btn').hide();
950
  }
951
 
952
- $( document ).on( 'click', '.tutor-profile-photo-delete-btn', function() {
953
- $( '.tutor-profile-photo-upload-wrap' ).find( 'img' ).attr( 'src', _tutorobject.placeholder_img_src );
954
- $( '#tutor_profile_photo_id' ).val( '' );
955
- $( '.tutor-profile-photo-delete-btn' ).hide();
956
 
957
  $.ajax({
958
  url: _tutorobject.ajaxurl,
959
  type: 'POST',
960
- data: {'action' : 'tutor_profile_photo_remove'},
961
  });
962
 
963
  return false;
964
  });
965
 
966
-
967
-
968
  /**
969
  * Assignment
970
  *
971
  * @since v.1.3.3
972
  */
973
-
974
- $( document ).on( 'submit', '#tutor_assignment_start_form', function(e) {
975
  e.preventDefault();
976
 
977
  var $that = $(this);
978
- var form_data = $that.serialize()+'&action=tutor_start_assignment';
979
 
980
  $.ajax({
981
  url: _tutorobject.ajaxurl,
@@ -985,11 +982,11 @@ jQuery(document).ready(function($){
985
  $('#tutor_assignment_start_btn').addClass('updating-icon');
986
  },
987
  success: function (data) {
988
- if (data.success){
989
  location.reload();
990
  }
991
  },
992
- complete : function () {
993
  $('#tutor_assignment_start_btn').removeClass('updating-icon');
994
  }
995
  });
@@ -998,10 +995,10 @@ jQuery(document).ready(function($){
998
  /**
999
  * Assignment answer validation
1000
  */
1001
- $( document ).on( 'submit', '#tutor_assignment_submit_form', function(e) {
1002
  var assignment_answer = $('textarea[name="assignment_answer"]').val();
1003
  if (assignment_answer.trim().length < 1) {
1004
- $('#form_validation_response').html('<div class="tutor-error-msg">'+_tutorobject.text.assignment_text_validation_msg+'</div>');
1005
  e.preventDefault();
1006
  }
1007
  });
@@ -1012,13 +1009,13 @@ jQuery(document).ready(function($){
1012
  */
1013
 
1014
 
1015
- $(document).on( 'click', '.video_source_upload_wrap_html5 .video_upload_btn', function( event ){
1016
  event.preventDefault();
1017
 
1018
  var $that = $(this);
1019
  var frame;
1020
  // If the media frame already exists, reopen it.
1021
- if ( frame ) {
1022
  frame.open();
1023
  return;
1024
  }
@@ -1030,7 +1027,7 @@ jQuery(document).ready(function($){
1030
  library: { type: 'video' },
1031
  multiple: false // Set to true to allow multiple files to be selected
1032
  });
1033
- frame.on( 'select', function() {
1034
  // Get media attachment details from the frame state
1035
  var attachment = frame.state().get('selection').first().toJSON();
1036
  $that.closest('.video_source_upload_wrap_html5').find('span.video_media_id').text(attachment.id).closest('p').show();
@@ -1044,7 +1041,7 @@ jQuery(document).ready(function($){
1044
  * Course and lesson sorting
1045
  */
1046
 
1047
- function enable_sorting_topic_lesson(){
1048
  if (jQuery().sortable) {
1049
  $(".course-contents").sortable({
1050
  handle: ".course-move-handle",
@@ -1068,20 +1065,20 @@ jQuery(document).ready(function($){
1068
  }
1069
  }
1070
  enable_sorting_topic_lesson();
1071
- function tutor_sorting_topics_and_lesson(){
1072
  var topics = {};
1073
- $('.tutor-topics-wrap').each(function(index, item){
1074
  var $topic = $(this);
1075
  var topics_id = parseInt($topic.attr('id').match(/\d+/)[0], 10);
1076
  var lessons = {};
1077
 
1078
- $topic.find('.course-content-item').each(function(lessonIndex, lessonItem){
1079
  var $lesson = $(this);
1080
  var lesson_id = parseInt($lesson.attr('id').match(/\d+/)[0], 10);
1081
 
1082
  lessons[lessonIndex] = lesson_id;
1083
  });
1084
- topics[index] = { 'topic_id' : topics_id, 'lesson_ids' : lessons };
1085
  });
1086
  $('#tutor_topics_lessons_sorting').val(JSON.stringify(topics));
1087
  }
@@ -1089,7 +1086,7 @@ jQuery(document).ready(function($){
1089
  /**
1090
  * Lesson Update or Create Modal
1091
  */
1092
- $(document).on( 'click', '.update_lesson_modal_btn', function( event ){
1093
  event.preventDefault();
1094
 
1095
  var $that = $(this);
@@ -1098,21 +1095,21 @@ jQuery(document).ready(function($){
1098
  if (editor) {
1099
  content = editor.getContent();
1100
  } else {
1101
- content = $('#'+inputid).val();
1102
  }
1103
 
1104
  var form_data = $(this).closest('form').serialize();
1105
- form_data += '&lesson_content='+encodeURIComponent(content);
1106
 
1107
  $.ajax({
1108
- url : ajaxurl,
1109
- type : 'POST',
1110
- data : form_data,
1111
  beforeSend: function () {
1112
  $that.addClass('tutor-updating-message');
1113
  },
1114
  success: function (data) {
1115
- if (data.success){
1116
  $('#tutor-course-content-wrap').html(data.data.course_contents);
1117
  enable_sorting_topic_lesson();
1118
 
@@ -1134,16 +1131,16 @@ jQuery(document).ready(function($){
1134
  * Attachment in forntend course builder
1135
  * @since v.1.3.4
1136
  */
1137
- $(document).on('click', 'a.tutor-delete-attachment', function(e){
1138
  e.preventDefault();
1139
  $(this).closest('.tutor-added-attachment').remove();
1140
  });
1141
- $(document).on('click', '.tutorUploadAttachmentBtn', function(e){
1142
  e.preventDefault();
1143
 
1144
  var $that = $(this);
1145
  var frame;
1146
- if ( frame ) {
1147
  frame.open();
1148
  return;
1149
  }
@@ -1154,13 +1151,13 @@ jQuery(document).ready(function($){
1154
  },
1155
  multiple: true // Set to true to allow multiple files to be selected
1156
  });
1157
- frame.on( 'select', function() {
1158
  var attachments = frame.state().get('selection').toJSON();
1159
- if (attachments.length){
1160
- for (var i=0; i < attachments.length; i++){
1161
  var attachment = attachments[i];
1162
 
1163
- var inputHtml = '<div class="tutor-added-attachment"><i class="tutor-icon-archive"></i><a href="javascript:;" class="tutor-delete-attachment tutor-icon-line-cross"></a> <span> <a href="'+attachment.url+'">'+attachment.filename+'</a> </span> <input type="hidden" name="tutor_attachments[]" value="'+attachment.id+'"></div>';
1164
  $that.closest('.tutor-lesson-attachments-metabox').find('.tutor-added-attachments-wrap').append(inputHtml);
1165
  }
1166
  }
@@ -1205,7 +1202,7 @@ jQuery(document).ready(function($){
1205
  * Tutor Assignments JS
1206
  * @since v.1.3.3
1207
  */
1208
- $(document).on('click', '.tutor-create-assignments-btn', function(e){
1209
  e.preventDefault();
1210
 
1211
  var $that = $(this);
@@ -1213,9 +1210,9 @@ jQuery(document).ready(function($){
1213
  var course_id = $('#post_ID').val();
1214
 
1215
  $.ajax({
1216
- url : ajaxurl,
1217
- type : 'POST',
1218
- data : {topic_id : topic_id, course_id : course_id, action: 'tutor_load_assignments_builder_modal'},
1219
  beforeSend: function () {
1220
  $that.addClass('tutor-updating-message');
1221
  },
@@ -1223,20 +1220,20 @@ jQuery(document).ready(function($){
1223
  $('.tutor-lesson-modal-wrap .modal-container').html(data.data.output);
1224
  $('.tutor-lesson-modal-wrap').attr('data-topic-id', topic_id).addClass('show');
1225
 
1226
- $(document).trigger('assignment_modal_loaded', {topic_id : topic_id, course_id : course_id});
1227
 
1228
  tinymce.init(tinyMCEPreInit.mceInit.course_description);
1229
- tinymce.execCommand( 'mceRemoveEditor', false, 'tutor_assignments_modal_editor' );
1230
  tinyMCE.execCommand('mceAddEditor', false, "tutor_assignments_modal_editor");
1231
  },
1232
  complete: function () {
1233
- quicktags({id : "tutor_assignments_modal_editor"});
1234
  $that.removeClass('tutor-updating-message');
1235
  }
1236
  });
1237
  });
1238
 
1239
- $(document).on('click', '.open-tutor-assignment-modal', function(e){
1240
  e.preventDefault();
1241
 
1242
  var $that = $(this);
@@ -1245,24 +1242,24 @@ jQuery(document).ready(function($){
1245
  var course_id = $('#post_ID').val();
1246
 
1247
  $.ajax({
1248
- url : ajaxurl,
1249
- type : 'POST',
1250
- data : {assignment_id : assignment_id, topic_id : topic_id, course_id : course_id, action: 'tutor_load_assignments_builder_modal'},
1251
  beforeSend: function () {
1252
  $that.addClass('tutor-updating-message');
1253
  },
1254
  success: function (data) {
1255
  $('.tutor-lesson-modal-wrap .modal-container').html(data.data.output);
1256
- $('.tutor-lesson-modal-wrap').attr({'data-assignment-id' : assignment_id, 'data-topic-id':topic_id}).addClass('show');
1257
 
1258
- $(document).trigger('assignment_modal_loaded', {assignment_id : assignment_id, topic_id : topic_id, course_id : course_id});
1259
 
1260
  tinymce.init(tinyMCEPreInit.mceInit.course_description);
1261
- tinymce.execCommand( 'mceRemoveEditor', false, 'tutor_assignments_modal_editor' );
1262
  tinyMCE.execCommand('mceAddEditor', false, "tutor_assignments_modal_editor");
1263
  },
1264
  complete: function () {
1265
- quicktags({id : "tutor_assignments_modal_editor"});
1266
  $that.removeClass('tutor-updating-message');
1267
  }
1268
  });
@@ -1271,7 +1268,7 @@ jQuery(document).ready(function($){
1271
  /**
1272
  * Update Assignment Data
1273
  */
1274
- $(document).on( 'click', '.update_assignment_modal_btn', function( event ){
1275
  event.preventDefault();
1276
 
1277
  var $that = $(this);
@@ -1280,21 +1277,21 @@ jQuery(document).ready(function($){
1280
  if (editor) {
1281
  content = editor.getContent();
1282
  } else {
1283
- content = $('#'+inputid).val();
1284
  }
1285
 
1286
  var form_data = $(this).closest('form').serialize();
1287
- form_data += '&assignment_content='+content;
1288
 
1289
  $.ajax({
1290
- url : ajaxurl,
1291
- type : 'POST',
1292
- data : form_data,
1293
  beforeSend: function () {
1294
  $that.addClass('tutor-updating-message');
1295
  },
1296
  success: function (data) {
1297
- if (data.success){
1298
  $('#tutor-course-content-wrap').html(data.data.course_contents);
1299
  enable_sorting_topic_lesson();
1300
 
@@ -1311,13 +1308,13 @@ jQuery(document).ready(function($){
1311
  /**
1312
  * Add Assignment
1313
  */
1314
- $(document).on( 'click', '.add-assignment-attachments', function( event ){
1315
  event.preventDefault();
1316
 
1317
  var $that = $(this);
1318
  var frame;
1319
  // If the media frame already exists, reopen it.
1320
- if ( frame ) {
1321
  frame.open();
1322
  return;
1323
  }
@@ -1332,11 +1329,11 @@ jQuery(document).ready(function($){
1332
  });
1333
 
1334
  // When an image is selected in the media frame...
1335
- frame.on( 'select', function() {
1336
  // Get media attachment details from the frame state
1337
  var attachment = frame.state().get('selection').first().toJSON();
1338
 
1339
- var field_markup = '<div class="tutor-individual-attachment-file"><p class="attachment-file-name">'+attachment.filename+'</p><input type="hidden" name="tutor_assignment_attachments[]" value="'+attachment.id+'"><a href="javascript:;" class="remove-assignment-attachment-a text-muted"> &times; Remove</a></div>';
1340
 
1341
  $('#assignment-attached-file').append(field_markup);
1342
  $that.closest('.video_source_upload_wrap_html5').find('input').val(attachment.id);
@@ -1345,7 +1342,7 @@ jQuery(document).ready(function($){
1345
  frame.open();
1346
  });
1347
 
1348
- $(document).on( 'click', '.remove-assignment-attachment-a', function( event ){
1349
  event.preventDefault();
1350
  $(this).closest('.tutor-individual-attachment-file').remove();
1351
  });
@@ -1360,16 +1357,16 @@ jQuery(document).ready(function($){
1360
  * @since v.1.3.4
1361
  */
1362
  var tutor_course_builder = $('input[name="tutor_action"]').val();
1363
- if (tutor_course_builder === 'tutor_add_course_builder'){
1364
  setInterval(auto_draft_save_course_builder, 30000);
1365
  }
1366
 
1367
- function auto_draft_save_course_builder(){
1368
  var form_data = $('form#tutor-frontend-course-builder').serialize();
1369
  $.ajax({
1370
  //url : _tutorobject.ajaxurl,
1371
- type : 'POST',
1372
- data : form_data+'&tutor_ajax_action=tutor_course_builder_draft_save',
1373
  beforeSend: function () {
1374
  $('.tutor-dashboard-builder-draft-btn span').text('Saving...');
1375
  },
@@ -1392,9 +1389,9 @@ jQuery(document).ready(function($){
1392
  */
1393
 
1394
  $('.tutor-course-builder-section-title').on('click', function () {
1395
- if($(this).find('i').hasClass("tutor-icon-up")){
1396
  $(this).find('i').removeClass('tutor-icon-up').addClass('tutor-icon-down');
1397
- }else{
1398
  $(this).find('i').removeClass('tutor-icon-down').addClass('tutor-icon-up');
1399
  }
1400
  $(this).next('div').slideToggle();
@@ -1404,7 +1401,7 @@ jQuery(document).ready(function($){
1404
  * Open Tutor Modal to edit review
1405
  * @since v.1.4.0
1406
  */
1407
- $(document).on('click', '.open-tutor-edit-review-modal', function(e){
1408
  e.preventDefault();
1409
 
1410
  var $that = $(this);
@@ -1412,13 +1409,13 @@ jQuery(document).ready(function($){
1412
 
1413
  var nonce_key = _tutorobject.nonce_key;
1414
 
1415
- var json_data = {review_id : review_id, action: 'tutor_load_edit_review_modal'};
1416
  json_data[nonce_key] = _tutorobject[nonce_key];
1417
 
1418
  $.ajax({
1419
- url : _tutorobject.ajaxurl,
1420
- type : 'POST',
1421
- data : json_data,
1422
  beforeSend: function () {
1423
  $that.addClass('tutor-updating-message');
1424
  },
@@ -1438,7 +1435,7 @@ jQuery(document).ready(function($){
1438
  * Update the rating
1439
  * @since v.1.4.0
1440
  */
1441
- $(document).on('submit', '#tutor_update_review_form', function(e){
1442
  e.preventDefault();
1443
 
1444
  var $that = $(this);
@@ -1450,18 +1447,18 @@ jQuery(document).ready(function($){
1450
  var review = $that.find('textarea[name="review"]').val();
1451
  review = review.trim();
1452
 
1453
- var json_data = {review_id : review_id, rating : rating, review : review, action: 'tutor_update_review_modal'};
1454
  json_data[nonce_key] = _tutorobject[nonce_key];
1455
 
1456
  $.ajax({
1457
- url : _tutorobject.ajaxurl,
1458
- type : 'POST',
1459
- data : json_data,
1460
  beforeSend: function () {
1461
  $that.find('button[type="submit"]').addClass('tutor-updating-message');
1462
  },
1463
  success: function (data) {
1464
- if (data.success){
1465
  //Close the modal
1466
  $('.tutor-edit-review-modal-wrap').removeClass('show');
1467
  location.reload(true);
@@ -1478,20 +1475,20 @@ jQuery(document).ready(function($){
1478
  * @since v.1.4.5
1479
  */
1480
 
1481
- $(document).on('click', '#tutor_profile_photo_button', function(e){
1482
  e.preventDefault();
1483
 
1484
  $('#tutor_profile_photo_file').trigger('click');
1485
  });
1486
 
1487
- $(document).on('change', '#tutor_profile_photo_file', function(event){
1488
  event.preventDefault();
1489
 
1490
  var $file = this;
1491
  if ($file.files && $file.files[0]) {
1492
  var reader = new FileReader();
1493
- reader.onload = function(e) {
1494
- $( '.tutor-profile-photo-upload-wrap' ).find( 'img' ).attr( 'src', e.target.result);
1495
  }
1496
  reader.readAsDataURL($file.files[0]);
1497
  }
@@ -1504,24 +1501,24 @@ jQuery(document).ready(function($){
1504
  * @since v.1.4.8
1505
  */
1506
 
1507
- $(document).on('click', '.thread-content .subject', function(e){
1508
  var $btn = $(this);
1509
 
1510
  var thread_id = parseInt($btn.closest('.thread-content').attr('data-thread-id'));
1511
 
1512
  var nonce_key = _tutorobject.nonce_key;
1513
- var json_data = {thread_id : thread_id, action: 'tutor_bp_retrieve_user_records_for_thread'};
1514
  json_data[nonce_key] = _tutorobject[nonce_key];
1515
 
1516
  $.ajax({
1517
  type: 'POST',
1518
  url: ajaxurl,
1519
  data: json_data,
1520
- beforeSend: function(){
1521
  $('#tutor-bp-thread-wrap').html('');
1522
  },
1523
  success: function (data) {
1524
- if (data.success){
1525
  $('#tutor-bp-thread-wrap').html(data.data.thread_head_html);
1526
  tutor_bp_setting_enrolled_courses_list();
1527
  }
@@ -1531,23 +1528,23 @@ jQuery(document).ready(function($){
1531
  });
1532
 
1533
 
1534
- function tutor_bp_setting_enrolled_courses_list(){
1535
- $('ul.tutor-bp-enrolled-course-list').each(function(){
1536
  var $that = $(this);
1537
  var $li = $that.find(' > li');
1538
  var itemShow = 3;
1539
 
1540
- if ($li.length > itemShow){
1541
  var plusCourseCount = $li.length - itemShow;
1542
- $li.each(function(liIndex, liItem){
1543
  var $liItem = $(this);
1544
 
1545
- if (liIndex >= itemShow){
1546
  $liItem.hide();
1547
  }
1548
  });
1549
 
1550
- var infoHtml = '<a href="javascript:;" class="tutor_bp_plus_courses"><strong>+'+plusCourseCount+' More </strong></a> Courses';
1551
  $that.closest('.tutor-bp-enrolled-courses-wrap').find('.thread-participant-enrolled-info').html(infoHtml);
1552
  }
1553
 
@@ -1556,7 +1553,7 @@ jQuery(document).ready(function($){
1556
  }
1557
  tutor_bp_setting_enrolled_courses_list();
1558
 
1559
- $(document).on('click', 'a.tutor_bp_plus_courses', function(e){
1560
  e.preventDefault();
1561
 
1562
  var $btn = $(this);
@@ -1565,7 +1562,6 @@ jQuery(document).ready(function($){
1565
  });
1566
 
1567
 
1568
-
1569
  /**
1570
  * Addon, Tutor Certificate
1571
  * Certificate dropdown content and copy link
@@ -1588,17 +1584,46 @@ jQuery(document).ready(function($){
1588
  document.execCommand('copy');
1589
  document.body.removeChild(dummy);
1590
  $btn.html(copied);
1591
- setTimeout( function() {
1592
  $btn.html(copy);
1593
  }, 2500);
1594
  });
1595
- $(document).on('click', function(e) {
1596
  var container = $(".tutor-dropdown");
1597
  var $content = container.find('.tutor-dropdown-content');
1598
  // if the target of the click isn't the container nor a descendant of the container
1599
- if (!container.is(e.target) && container.has(e.target).length === 0) {
1600
  $content.slideUp(100);
1601
  }
1602
  });
1603
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1604
  });
1
+ jQuery(document).ready(function ($) {
2
  'use strict';
3
 
4
  /**
5
  * Initiate Select2
6
  * @since v.1.3.4
7
  */
8
+ if (jQuery().select2) {
9
  $('.tutor_select2').select2({
10
+ escapeMarkup: function (markup) {
11
  return markup;
12
  }
13
  });
25
  * jquery.ui.widget.js
26
  * jquery.ui.mouse.js
27
  */
28
+ !function (a) { function f(a, b) { if (!(a.originalEvent.touches.length > 1)) { a.preventDefault(); var c = a.originalEvent.changedTouches[0], d = document.createEvent("MouseEvents"); d.initMouseEvent(b, !0, !0, window, 1, c.screenX, c.screenY, c.clientX, c.clientY, !1, !1, !1, !1, 0, null), a.target.dispatchEvent(d) } } if (a.support.touch = "ontouchend" in document, a.support.touch) { var e, b = a.ui.mouse.prototype, c = b._mouseInit, d = b._mouseDestroy; b._touchStart = function (a) { var b = this; !e && b._mouseCapture(a.originalEvent.changedTouches[0]) && (e = !0, b._touchMoved = !1, f(a, "mouseover"), f(a, "mousemove"), f(a, "mousedown")) }, b._touchMove = function (a) { e && (this._touchMoved = !0, f(a, "mousemove")) }, b._touchEnd = function (a) { e && (f(a, "mouseup"), f(a, "mouseout"), this._touchMoved || f(a, "click"), e = !1) }, b._mouseInit = function () { var b = this; b.element.bind({ touchstart: a.proxy(b, "_touchStart"), touchmove: a.proxy(b, "_touchMove"), touchend: a.proxy(b, "_touchEnd") }), c.call(b) }, b._mouseDestroy = function () { var b = this; b.element.unbind({ touchstart: a.proxy(b, "_touchStart"), touchmove: a.proxy(b, "_touchMove"), touchend: a.proxy(b, "_touchEnd") }), d.call(b) } } }(jQuery);
29
 
30
  /**
31
  * END jQuery UI Touch Punch
32
  */
33
 
34
 
35
+ $(document).on('change', '.tutor-course-filter-form', function (e) {
36
  e.preventDefault();
37
  $(this).closest('form').submit();
38
  });
40
  const videoPlayer = {
41
  ajaxurl: _tutorobject.ajaxurl,
42
  nonce_key: _tutorobject.nonce_key,
43
+ video_data: function () {
44
  const video_track_data = $('#tutor_video_tracking_information').val();
45
  return video_track_data ? JSON.parse(video_track_data) : {};
46
  },
47
+ track_player: function () {
48
  const that = this;
49
  if (typeof Plyr !== 'undefined') {
50
  const player = new Plyr('#tutorPlayer');
51
  const video_data = that.video_data();
52
+ player.on('ready', function (event) {
53
  const instance = event.detail.plyr;
54
  const { best_watch_time } = video_data;
55
  if (best_watch_time > 0 && instance.duration > Math.round(best_watch_time)) {
60
 
61
  let tempTimeNow = 0;
62
  let intervalSeconds = 30; //Send to tutor backend about video playing time in this interval
63
+ player.on('timeupdate', function (event) {
64
  const instance = event.detail.plyr;
65
  const tempTimeNowInSec = (tempTimeNow / 4); //timeupdate firing 250ms interval
66
+ if (tempTimeNowInSec >= intervalSeconds) {
67
  that.sync_time(instance);
68
  tempTimeNow = 0;
69
  }
70
  tempTimeNow++;
71
  });
72
 
73
+ player.on('ended', function (event) {
74
  const video_data = that.video_data();
75
  const instance = event.detail.plyr;
76
+ const data = { is_ended: true };
77
  that.sync_time(instance, data);
78
+ if (video_data.autoload_next_course_content) {
79
  that.autoload_content();
80
  }
81
  });
82
  }
83
  },
84
+ sync_time: function (instance, options) {
85
  const post_id = this.video_data().post_id;
86
  //TUTOR is sending about video playback information to server.
87
+ let data = { action: 'sync_video_playback', currentTime: instance.currentTime, duration: instance.duration, post_id };
88
  data[this.nonce_key] = _tutorobject[this.nonce_key];
89
  let data_send = data;
90
+ if (options) {
91
  data_send = Object.assign(data, options);
92
  }
93
  $.post(this.ajaxurl, data_send);
94
  },
95
+ autoload_content: function () {
96
  const post_id = this.video_data().post_id;
97
+ const data = { action: 'autoload_next_course_content', post_id };
98
  data[this.nonce_key] = _tutorobject[this.nonce_key];
99
+ $.post(this.ajaxurl, data).done(function (response) {
100
+ if (response.success && response.data.next_url) {
101
  location.href = response.data.next_url;
102
  }
103
  });
104
  },
105
+ init: function () {
106
  this.track_player();
107
  }
108
  };
111
  * Fire TUTOR video
112
  * @since v.1.0.0
113
  */
114
+ if ($('#tutorPlayer').length) {
115
  videoPlayer.init();
116
  }
117
 
118
+ $(document).on('change keyup paste', '.tutor_user_name', function () {
119
  $(this).val(tutor_slugify($(this).val()));
120
  });
121
 
131
  /**
132
  * Hover tutor rating and set value
133
  */
134
+ $(document).on('hover', '.tutor-write-review-box .tutor-star-rating-group i', function () {
135
  $(this).closest('.tutor-star-rating-group').find('i').removeClass('tutor-icon-star-full').addClass('tutor-icon-star-line');
136
  var currentRateValue = $(this).attr('data-rating-value');
137
+ for (var i = 1; i <= currentRateValue; i++) {
138
+ $(this).closest('.tutor-star-rating-group').find('i[data-rating-value="' + i + '"]').removeClass('tutor-icon-star-line').addClass('tutor-icon-star-full');
139
  }
140
  $(this).closest('.tutor-star-rating-group').find('input[name="tutor_rating_gen_input"]').val(currentRateValue);
141
  });
142
 
143
+ $(document).on('click', '.tutor-star-rating-group i', function () {
144
  var rating = $(this).attr('data-rating-value');
145
  $(this).closest('.tutor-star-rating-group').find('input[name="tutor_rating_gen_input"]').val(rating);
146
  });
153
  review = review.trim();
154
 
155
  var course_id = $('input[name="tutor_course_id"]').val();
156
+ var data = { course_id: course_id, rating: rating, review: review, action: 'tutor_place_rating' };
157
 
158
  if (review) {
159
  $.ajax({
166
  success: function (data) {
167
  var review_id = data.data.review_id;
168
  var review = data.data.review;
169
+ $('.tutor-review-' + review_id + ' .review-content').html(review);
170
  location.reload();
171
  }
172
  });
173
  }
174
  });
175
 
176
+ $(document).on('click', '.write-course-review-link-btn', function (e) {
177
  e.preventDefault();
178
  $(this).siblings('.tutor-write-review-form').slideToggle();
179
  });
180
 
181
+ $(document).on('click', '.tutor-ask-question-btn', function (e) {
182
  e.preventDefault();
183
  $('.tutor-add-question-wrap').slideToggle();
184
  });
185
+ $(document).on('click', '.tutor_question_cancel', function (e) {
186
  e.preventDefault();
187
  $('.tutor-add-question-wrap').toggle();
188
  });
189
 
190
+ $(document).on('submit', '#tutor-ask-question-form', function (e) {
191
  e.preventDefault();
192
 
193
  var $form = $(this);
194
+ var data = $(this).serialize() + '&action=tutor_ask_question';
195
 
196
  $.ajax({
197
  url: _tutorobject.ajaxurl,
201
  $form.find('.tutor_ask_question_btn').addClass('updating-icon');
202
  },
203
  success: function (data) {
204
+ if (data.success) {
205
  $('.tutor-add-question-wrap').hide();
206
  window.location.reload();
207
  }
212
  });
213
  });
214
 
215
+ $(document).on('submit', '.tutor-add-answer-form', function (e) {
216
  e.preventDefault();
217
 
218
  var $form = $(this);
219
+ var data = $(this).serialize() + '&action=tutor_add_answer';
220
 
221
  $.ajax({
222
  url: _tutorobject.ajaxurl,
226
  $form.find('.tutor_add_answer_btn').addClass('updating-icon');
227
  },
228
  success: function (data) {
229
+ if (data.success) {
230
  window.location.reload();
231
  }
232
  },
236
  });
237
  });
238
 
239
+ $(document).on('focus', '.tutor_add_answer_textarea', function (e) {
240
  e.preventDefault();
241
 
242
  var question_id = $(this).closest('.tutor_add_answer_wrap').attr('data-question-id');
243
  var conf = {
244
  tinymce: {
245
+ wpautop: true,
246
  //plugins : 'charmap colorpicker compat3x directionality fullscreen hr image lists media paste tabfocus textcolor wordpress wpautoresize wpdialogs wpeditimage wpemoji wpgallery wplink wptextpattern wpview',
247
  toolbar1: 'bold italic underline bullist strikethrough numlist blockquote alignleft aligncenter alignright undo redo link unlink spellchecker fullscreen'
248
  },
249
  };
250
+ wp.editor.initialize('tutor_answer_' + question_id, conf);
251
  });
252
 
253
+ $(document).on('click', '.tutor_cancel_wp_editor', function (e) {
254
  e.preventDefault();
255
  $(this).closest('.tutor_wp_editor_wrap').toggle();
256
  $(this).closest('.tutor_add_answer_wrap').find('.tutor_wp_editor_show_btn_wrap').toggle();
257
  var question_id = $(this).closest('.tutor_add_answer_wrap').attr('data-question-id');
258
+ wp.editor.remove('tutor_answer_' + question_id);
259
  });
260
 
261
+ $(document).on('click', '.tutor_wp_editor_show_btn', function (e) {
262
  e.preventDefault();
263
  $(this).closest('.tutor_add_answer_wrap').find('.tutor_wp_editor_wrap').toggle();
264
  $(this).closest('.tutor_wp_editor_show_btn_wrap').toggle();
269
  */
270
  var $tutor_quiz_time_update = $('#tutor-quiz-time-update');
271
  var attempt_settings = null;
272
+ if ($tutor_quiz_time_update.length) {
273
  attempt_settings = JSON.parse($tutor_quiz_time_update.attr('data-attempt-settings'));
274
  var attempt_meta = JSON.parse($tutor_quiz_time_update.attr('data-attempt-meta'));
275
 
322
 
323
  var quiz_id = $('#tutor_quiz_id').val();
324
  var tutor_quiz_remaining_time_secs = $('#tutor_quiz_remaining_time_secs').val();
325
+ var quiz_timeout_data = { quiz_id: quiz_id, action: 'tutor_quiz_timeout' };
326
 
327
  $.ajax({
328
  url: _tutorobject.ajaxurl,
344
  time_now = time_now + 1000;
345
  $tutor_quiz_time_update.html(countdown_human);
346
  }, 1000);
347
+ } else {
348
  $tutor_quiz_time_update.closest('.time-remaining').remove();
349
  }
350
  }
351
 
352
  var $quiz_start_form = $('#tutor-quiz-body form#tutor-start-quiz');
353
+ if ($quiz_start_form.length) {
354
+ if (_tutorobject.quiz_options.quiz_auto_start === '1') {
355
  $quiz_start_form.submit();
356
  }
357
  }
360
  * Quiz Frontend Review Action
361
  * @since 1.4.0
362
  */
363
+ $(document).on('click', '.quiz-manual-review-action', function (e) {
364
  e.preventDefault();
365
  var $that = $(this),
366
  attempt_id = $that.attr('data-attempt-id'),
368
  mark_as = $that.attr('data-mark-as');
369
 
370
  $.ajax({
371
+ url: _tutorobject.ajaxurl,
372
+ type: 'GET',
373
+ data: { action: 'review_quiz_answer', attempt_id: attempt_id, attempt_answer_id: attempt_answer_id, mark_as: mark_as },
374
  beforeSend: function () {
375
  $that.find('i').addClass('updating-icon');
376
  },
405
  $.ajax({
406
  url: _tutorobject.ajaxurl,
407
  type: 'POST',
408
+ data: { course_id: course_id, 'action': 'tutor_course_add_to_wishlist' },
409
  beforeSend: function () {
410
  $that.addClass('updating-icon');
411
  },
412
  success: function (data) {
413
+ if (data.success) {
414
+ if (data.data.status === 'added') {
415
  $that.addClass('has-wish-listed');
416
+ } else {
417
  $that.removeClass('has-wish-listed');
418
  }
419
+ } else {
420
  window.location = data.data.redirect_to;
421
  }
422
  },
434
  *
435
  * @updated v.1.4.0
436
  */
437
+ if (!_tutorobject.enable_lesson_classic_editor) {
438
 
439
  $(document).on('click', '.tutor-single-lesson-a', function (e) {
440
  e.preventDefault();
446
  $.ajax({
447
  url: _tutorobject.ajaxurl,
448
  type: 'POST',
449
+ data: { lesson_id: lesson_id, 'action': 'tutor_render_lesson_content' },
450
  beforeSend: function () {
451
  var page_title = $that.find('.lesson_title').text();
452
  $('head title').text(page_title);
477
  $.ajax({
478
  url: _tutorobject.ajaxurl,
479
  type: 'POST',
480
+ data: { quiz_id: quiz_id, 'action': 'tutor_render_quiz_content' },
481
  beforeSend: function () {
482
  $('head title').text(page_title);
483
  window.history.pushState('obj', page_title, $that.attr('href'));
501
  * @date 05 Feb, 2019
502
  */
503
 
504
+ $(document).on('click', '.tutor-lesson-sidebar-hide-bar', function (e) {
505
  e.preventDefault();
506
  $('.tutor-lesson-sidebar').toggle();
507
  $('#tutor-single-entry-content').toggleClass("sidebar-hidden");
561
  */
562
 
563
  var validated = tutor_quiz_validation($question_wrap);
564
+ if (!validated) {
565
  return;
566
  }
567
 
579
  * If pagination exists, set active class
580
  */
581
 
582
+ if ($('.tutor-quiz-questions-pagination').length) {
583
  $('.tutor-quiz-question-paginate-item').removeClass('active');
584
+ $('.tutor-quiz-questions-pagination a[href="' + next_question_id + '"]').addClass('active');
585
  }
586
 
587
  }
592
  $(document).on('submit', '#tutor-answering-quiz', function (e) {
593
  var $questions_wrap = $('.quiz-attempt-single-question');
594
  var validated = true;
595
+ if ($questions_wrap.length) {
596
+ $questions_wrap.each(function (index, question) {
597
  validated = tutor_quiz_validation($(question));
598
  });
599
  }
600
 
601
+ if (!validated) {
602
  e.preventDefault();
603
  }
604
 
620
  /**
621
  * Limit Short Answer Question Type
622
  */
623
+ $(document).on('keyup', 'textarea.question_type_short_answer, textarea.question_type_open_ended', function (e) {
624
  var $that = $(this);
625
  var value = $that.val();
626
+ var limit = $that.hasClass('question_type_short_answer') ? _tutorobject.quiz_options.short_answer_characters_limit : _tutorobject.quiz_options.open_ended_answer_characters_limit;
627
  var remaining = limit - value.length;
628
 
629
+ if (remaining < 1) {
630
  $that.val(value.substr(0, limit));
631
  remaining = 0;
632
  }
643
  * @since v.1.4.4
644
  */
645
  var countDraggableAnswers = $('.quiz-draggable-rand-answers').length;
646
+ if (countDraggableAnswers) {
647
+ $('.quiz-draggable-rand-answers').each(function () {
648
  var $that = $(this);
649
  var draggableDivHeight = $that.height();
650
 
651
+ $that.css({ "height": draggableDivHeight });
652
  });
653
  }
654
 
659
  * @since v.1.6.1
660
  */
661
 
662
+ function tutor_quiz_validation($question_wrap) {
663
  var validated = true;
664
 
665
  var $required_answer_wrap = $question_wrap.find('.quiz-answer-required');
666
 
667
 
668
+ if ($required_answer_wrap.length) {
669
 
670
  /**
671
  * Radio field validation
675
  * @since v.1.6.1
676
  */
677
  var $inputs = $required_answer_wrap.find('input');
678
+ if ($inputs.length) {
679
  var $type = $inputs.attr('type');
680
  if ($type === 'radio') {
681
  if ($required_answer_wrap.find('input[type="radio"]:checked').length == 0) {
682
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">Please select an option to answer</p>');
683
  validated = false;
684
  }
685
+ } else if ($type === 'checkbox') {
686
  if ($required_answer_wrap.find('input[type="checkbox"]:checked').length == 0) {
687
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">Please select at least one option to answer.</p>');
688
  validated = false;
689
  }
690
+ } else if ($type === 'text') {
691
  //Fill in the gaps if many, validation all
692
+ $inputs.each(function (index, input) {
693
+ if (!$(input).val().trim().length) {
694
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">The answer for this question is required</p>');
695
  validated = false;
696
  }
699
 
700
  }
701
  if ($required_answer_wrap.find('textarea').length) {
702
+ if ($required_answer_wrap.find('textarea').val().trim().length < 1) {
703
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">The answer for this question is required</p>');
704
  validated = false;
705
  }
709
  * Matching Question
710
  */
711
  var $matchingDropable = $required_answer_wrap.find('.quiz-answer-matching-droppable');
712
+ if ($matchingDropable.length) {
713
 
714
+ $matchingDropable.each(function (index, matching) {
715
+ if (!$(matching).find('.quiz-draggable-answer-item').length) {
716
  $question_wrap.find('.answer-help-block').html('<p style="color: #dc3545">Please match all the items</p>');
717
  validated = false;
718
  }
757
  *
758
  * @since v.1.0.4
759
  */
760
+ if ($.fn.ShareLink) {
761
  var $social_share_wrap = $('.tutor-social-share-wrap');
762
  if ($social_share_wrap.length) {
763
  var share_config = JSON.parse($social_share_wrap.attr('data-social-share-config'));
778
  *
779
  * @since v.1.1.2
780
  */
781
+ if (jQuery.datepicker) {
782
+ $(".tutor_report_datepicker").datepicker({ "dateFormat": 'yy-mm-dd' });
783
  }
784
 
785
 
789
  * @since v.1.1.2
790
  */
791
 
792
+ $(".withdraw-method-select-input").on('change', function (e) {
793
  var $that = $(this);
794
  $('.withdraw-method-form').hide();
795
+ $('#withdraw-method-form-' + $that.closest('.withdraw-method-select').attr('data-withdraw-method')).show();
796
  });
797
 
798
  $('.withdraw-method-select-input').each(function () {
799
  var $that = $(this);
800
+ if ($that.is(":checked")) {
801
  $('.withdraw-method-form').hide();
802
+ $('#withdraw-method-form-' + $that.closest('.withdraw-method-select').attr('data-withdraw-method')).show();
803
  }
804
  });
805
 
810
  *
811
  * @since v.1.2.0
812
  */
813
+ $(document).on('submit', '#tutor-withdraw-account-set-form', function (e) {
814
  e.preventDefault();
815
 
816
  var $form = $(this);
826
  $btn.addClass('updating-icon');
827
  },
828
  success: function (data) {
829
+ if (data.success) {
830
+ var successMsg = '<div class="tutor-success-msg" style="display: none;"><i class="tutor-icon-mark"></i> ' + data.data.msg + ' </div>';
831
  $btn.closest('.withdraw-account-save-btn-wrap').append(successMsg);
832
  if ($form.find('.tutor-success-msg').length) {
833
  $form.find('.tutor-success-msg').slideDown();
849
  * @since v.1.2.0
850
  */
851
 
852
+ $(document).on('click', 'a.open-withdraw-form-btn', function (e) {
853
  e.preventDefault();
854
  $('.tutor-earning-withdraw-form-wrap').slideToggle();
855
  });
856
 
857
+ $(document).on('submit', '#tutor-earning-withdraw-form', function (e) {
858
  e.preventDefault();
859
 
860
  var $form = $(this);
872
  },
873
  success: function (data) {
874
  var Msg;
875
+ if (data.success) {
876
 
877
  if (data.data.available_balance !== 'undefined') {
878
  $('.withdraw-balance-col .available_balance').html(data.data.available_balance);
879
  }
880
+ Msg = '<div class="tutor-success-msg"><i class="tutor-icon-mark"></i> ' + data.data.msg + ' </div>';
881
 
882
+ } else {
883
+ Msg = '<div class="tutor-error-msg"><i class="tutor-icon-line-cross"></i> ' + data.data.msg + ' </div>';
884
  }
885
 
886
  $responseDiv.html(Msg);
898
  frontEndModal.each(function () {
899
  var modal = $(this),
900
  action = $(this).data('popup-rel');
901
+ $('[href="' + action + '"]').on('click', function (e) {
902
  modal.fadeIn();
903
  e.preventDefault();
904
  });
930
  $btn.addClass('updating-icon');
931
  },
932
  success: function (data) {
933
+ if (data.success) {
934
+ $('#tutor-dashboard-course-' + course_id).remove();
935
  }
936
  },
937
  complete: function () {
945
  * Frontend Profile
946
  */
947
 
948
+ if (!$('#tutor_profile_photo_id').val()) {
949
  $('.tutor-profile-photo-delete-btn').hide();
950
  }
951
 
952
+ $(document).on('click', '.tutor-profile-photo-delete-btn', function () {
953
+ $('.tutor-profile-photo-upload-wrap').find('img').attr('src', _tutorobject.placeholder_img_src);
954
+ $('#tutor_profile_photo_id').val('');
955
+ $('.tutor-profile-photo-delete-btn').hide();
956
 
957
  $.ajax({
958
  url: _tutorobject.ajaxurl,
959
  type: 'POST',
960
+ data: { 'action': 'tutor_profile_photo_remove' },
961
  });
962
 
963
  return false;
964
  });
965
 
 
 
966
  /**
967
  * Assignment
968
  *
969
  * @since v.1.3.3
970
  */
971
+ $(document).on('submit', '#tutor_assignment_start_form', function (e) {
 
972
  e.preventDefault();
973
 
974
  var $that = $(this);
975
+ var form_data = $that.serialize() + '&action=tutor_start_assignment';
976
 
977
  $.ajax({
978
  url: _tutorobject.ajaxurl,
982
  $('#tutor_assignment_start_btn').addClass('updating-icon');
983
  },
984
  success: function (data) {
985
+ if (data.success) {
986
  location.reload();
987
  }
988
  },
989
+ complete: function () {
990
  $('#tutor_assignment_start_btn').removeClass('updating-icon');
991
  }
992
  });
995
  /**
996
  * Assignment answer validation
997
  */
998
+ $(document).on('submit', '#tutor_assignment_submit_form', function (e) {
999
  var assignment_answer = $('textarea[name="assignment_answer"]').val();
1000
  if (assignment_answer.trim().length < 1) {
1001
+ $('#form_validation_response').html('<div class="tutor-error-msg">' + _tutorobject.text.assignment_text_validation_msg + '</div>');
1002
  e.preventDefault();
1003
  }
1004
  });
1009
  */
1010
 
1011
 
1012
+ $(document).on('click', '.video_source_upload_wrap_html5 .video_upload_btn', function (event) {
1013
  event.preventDefault();
1014
 
1015
  var $that = $(this);
1016
  var frame;
1017
  // If the media frame already exists, reopen it.
1018
+ if (frame) {
1019
  frame.open();
1020
  return;
1021
  }
1027
  library: { type: 'video' },
1028
  multiple: false // Set to true to allow multiple files to be selected
1029
  });
1030
+ frame.on('select', function () {
1031
  // Get media attachment details from the frame state
1032
  var attachment = frame.state().get('selection').first().toJSON();
1033
  $that.closest('.video_source_upload_wrap_html5').find('span.video_media_id').text(attachment.id).closest('p').show();
1041
  * Course and lesson sorting
1042
  */
1043
 
1044
+ function enable_sorting_topic_lesson() {
1045
  if (jQuery().sortable) {
1046
  $(".course-contents").sortable({
1047
  handle: ".course-move-handle",
1065
  }
1066
  }
1067
  enable_sorting_topic_lesson();
1068
+ function tutor_sorting_topics_and_lesson() {
1069
  var topics = {};
1070
+ $('.tutor-topics-wrap').each(function (index, item) {
1071
  var $topic = $(this);
1072
  var topics_id = parseInt($topic.attr('id').match(/\d+/)[0], 10);
1073
  var lessons = {};
1074
 
1075
+ $topic.find('.course-content-item').each(function (lessonIndex, lessonItem) {
1076
  var $lesson = $(this);
1077
  var lesson_id = parseInt($lesson.attr('id').match(/\d+/)[0], 10);
1078
 
1079
  lessons[lessonIndex] = lesson_id;
1080
  });
1081
+ topics[index] = { 'topic_id': topics_id, 'lesson_ids': lessons };
1082
  });
1083
  $('#tutor_topics_lessons_sorting').val(JSON.stringify(topics));
1084
  }
1086
  /**
1087
  * Lesson Update or Create Modal
1088
  */
1089
+ $(document).on('click', '.update_lesson_modal_btn', function (event) {
1090
  event.preventDefault();
1091
 
1092
  var $that = $(this);
1095
  if (editor) {
1096
  content = editor.getContent();
1097
  } else {
1098
+ content = $('#' + inputid).val();
1099
  }
1100
 
1101
  var form_data = $(this).closest('form').serialize();
1102
+ form_data += '&lesson_content=' + encodeURIComponent(content);
1103
 
1104
  $.ajax({
1105
+ url: ajaxurl,
1106
+ type: 'POST',
1107
+ data: form_data,
1108
  beforeSend: function () {
1109
  $that.addClass('tutor-updating-message');
1110
  },
1111
  success: function (data) {
1112
+ if (data.success) {
1113
  $('#tutor-course-content-wrap').html(data.data.course_contents);
1114
  enable_sorting_topic_lesson();
1115
 
1131
  * Attachment in forntend course builder
1132
  * @since v.1.3.4
1133
  */
1134
+ $(document).on('click', 'a.tutor-delete-attachment', function (e) {
1135
  e.preventDefault();
1136
  $(this).closest('.tutor-added-attachment').remove();
1137
  });
1138
+ $(document).on('click', '.tutorUploadAttachmentBtn', function (e) {
1139
  e.preventDefault();
1140
 
1141
  var $that = $(this);
1142
  var frame;
1143
+ if (frame) {
1144
  frame.open();
1145
  return;
1146
  }
1151
  },
1152
  multiple: true // Set to true to allow multiple files to be selected
1153
  });
1154
+ frame.on('select', function () {
1155
  var attachments = frame.state().get('selection').toJSON();
1156
+ if (attachments.length) {
1157
+ for (var i = 0; i < attachments.length; i++) {
1158
  var attachment = attachments[i];
1159
 
1160
+ var inputHtml = '<div class="tutor-added-attachment"><i class="tutor-icon-archive"></i><a href="javascript:;" class="tutor-delete-attachment tutor-icon-line-cross"></a> <span> <a href="' + attachment.url + '">' + attachment.filename + '</a> </span> <input type="hidden" name="tutor_attachments[]" value="' + attachment.id + '"></div>';
1161
  $that.closest('.tutor-lesson-attachments-metabox').find('.tutor-added-attachments-wrap').append(inputHtml);
1162
  }
1163
  }
1202
  * Tutor Assignments JS
1203
  * @since v.1.3.3
1204
  */
1205
+ $(document).on('click', '.tutor-create-assignments-btn', function (e) {
1206
  e.preventDefault();
1207
 
1208
  var $that = $(this);
1210
  var course_id = $('#post_ID').val();
1211
 
1212
  $.ajax({
1213
+ url: ajaxurl,
1214
+ type: 'POST',
1215
+ data: { topic_id: topic_id, course_id: course_id, action: 'tutor_load_assignments_builder_modal' },
1216
  beforeSend: function () {
1217
  $that.addClass('tutor-updating-message');
1218
  },
1220
  $('.tutor-lesson-modal-wrap .modal-container').html(data.data.output);
1221
  $('.tutor-lesson-modal-wrap').attr('data-topic-id', topic_id).addClass('show');
1222
 
1223
+ $(document).trigger('assignment_modal_loaded', { topic_id: topic_id, course_id: course_id });
1224
 
1225
  tinymce.init(tinyMCEPreInit.mceInit.course_description);
1226
+ tinymce.execCommand('mceRemoveEditor', false, 'tutor_assignments_modal_editor');
1227
  tinyMCE.execCommand('mceAddEditor', false, "tutor_assignments_modal_editor");
1228
  },
1229
  complete: function () {
1230
+ quicktags({ id: "tutor_assignments_modal_editor" });
1231
  $that.removeClass('tutor-updating-message');
1232
  }
1233
  });
1234
  });
1235
 
1236
+ $(document).on('click', '.open-tutor-assignment-modal', function (e) {
1237
  e.preventDefault();
1238
 
1239
  var $that = $(this);
1242
  var course_id = $('#post_ID').val();
1243
 
1244
  $.ajax({
1245
+ url: ajaxurl,
1246
+ type: 'POST',
1247
+ data: { assignment_id: assignment_id, topic_id: topic_id, course_id: course_id, action: 'tutor_load_assignments_builder_modal' },
1248
  beforeSend: function () {
1249
  $that.addClass('tutor-updating-message');
1250
  },
1251
  success: function (data) {
1252
  $('.tutor-lesson-modal-wrap .modal-container').html(data.data.output);
1253
+ $('.tutor-lesson-modal-wrap').attr({ 'data-assignment-id': assignment_id, 'data-topic-id': topic_id }).addClass('show');
1254
 
1255
+ $(document).trigger('assignment_modal_loaded', { assignment_id: assignment_id, topic_id: topic_id, course_id: course_id });
1256
 
1257
  tinymce.init(tinyMCEPreInit.mceInit.course_description);
1258
+ tinymce.execCommand('mceRemoveEditor', false, 'tutor_assignments_modal_editor');
1259
  tinyMCE.execCommand('mceAddEditor', false, "tutor_assignments_modal_editor");
1260
  },
1261
  complete: function () {
1262
+ quicktags({ id: "tutor_assignments_modal_editor" });
1263
  $that.removeClass('tutor-updating-message');
1264
  }
1265
  });
1268
  /**
1269
  * Update Assignment Data
1270
  */
1271
+ $(document).on('click', '.update_assignment_modal_btn', function (event) {
1272
  event.preventDefault();
1273
 
1274
  var $that = $(this);
1277
  if (editor) {
1278
  content = editor.getContent();
1279
  } else {
1280
+ content = $('#' + inputid).val();
1281
  }
1282
 
1283
  var form_data = $(this).closest('form').serialize();
1284
+ form_data += '&assignment_content=' + content;
1285
 
1286
  $.ajax({
1287
+ url: ajaxurl,
1288
+ type: 'POST',
1289
+ data: form_data,
1290
  beforeSend: function () {
1291
  $that.addClass('tutor-updating-message');
1292
  },
1293
  success: function (data) {
1294
+ if (data.success) {
1295
  $('#tutor-course-content-wrap').html(data.data.course_contents);
1296
  enable_sorting_topic_lesson();
1297
 
1308
  /**
1309
  * Add Assignment
1310
  */
1311
+ $(document).on('click', '.add-assignment-attachments', function (event) {
1312
  event.preventDefault();
1313
 
1314
  var $that = $(this);
1315
  var frame;
1316
  // If the media frame already exists, reopen it.
1317
+ if (frame) {
1318
  frame.open();
1319
  return;
1320
  }
1329
  });
1330
 
1331
  // When an image is selected in the media frame...
1332
+ frame.on('select', function () {
1333
  // Get media attachment details from the frame state
1334
  var attachment = frame.state().get('selection').first().toJSON();
1335
 
1336
+ var field_markup = '<div class="tutor-individual-attachment-file"><p class="attachment-file-name">' + attachment.filename + '</p><input type="hidden" name="tutor_assignment_attachments[]" value="' + attachment.id + '"><a href="javascript:;" class="remove-assignment-attachment-a text-muted"> &times; Remove</a></div>';
1337
 
1338
  $('#assignment-attached-file').append(field_markup);
1339
  $that.closest('.video_source_upload_wrap_html5').find('input').val(attachment.id);
1342
  frame.open();
1343
  });
1344
 
1345
+ $(document).on('click', '.remove-assignment-attachment-a', function (event) {
1346
  event.preventDefault();
1347
  $(this).closest('.tutor-individual-attachment-file').remove();
1348
  });
1357
  * @since v.1.3.4
1358
  */
1359
  var tutor_course_builder = $('input[name="tutor_action"]').val();
1360
+ if (tutor_course_builder === 'tutor_add_course_builder') {
1361
  setInterval(auto_draft_save_course_builder, 30000);
1362
  }
1363
 
1364
+ function auto_draft_save_course_builder() {
1365
  var form_data = $('form#tutor-frontend-course-builder').serialize();
1366
  $.ajax({
1367
  //url : _tutorobject.ajaxurl,
1368
+ type: 'POST',
1369
+ data: form_data + '&tutor_ajax_action=tutor_course_builder_draft_save',
1370
  beforeSend: function () {
1371
  $('.tutor-dashboard-builder-draft-btn span').text('Saving...');
1372
  },
1389
  */
1390
 
1391
  $('.tutor-course-builder-section-title').on('click', function () {
1392
+ if ($(this).find('i').hasClass("tutor-icon-up")) {
1393
  $(this).find('i').removeClass('tutor-icon-up').addClass('tutor-icon-down');
1394
+ } else {
1395
  $(this).find('i').removeClass('tutor-icon-down').addClass('tutor-icon-up');
1396
  }
1397
  $(this).next('div').slideToggle();
1401
  * Open Tutor Modal to edit review
1402
  * @since v.1.4.0
1403
  */
1404
+ $(document).on('click', '.open-tutor-edit-review-modal', function (e) {
1405
  e.preventDefault();
1406
 
1407
  var $that = $(this);
1409
 
1410
  var nonce_key = _tutorobject.nonce_key;
1411
 
1412
+ var json_data = { review_id: review_id, action: 'tutor_load_edit_review_modal' };
1413
  json_data[nonce_key] = _tutorobject[nonce_key];
1414
 
1415
  $.ajax({
1416
+ url: _tutorobject.ajaxurl,
1417
+ type: 'POST',
1418
+ data: json_data,
1419
  beforeSend: function () {
1420
  $that.addClass('tutor-updating-message');
1421
  },
1435
  * Update the rating
1436
  * @since v.1.4.0
1437
  */
1438
+ $(document).on('submit', '#tutor_update_review_form', function (e) {
1439
  e.preventDefault();
1440
 
1441
  var $that = $(this);
1447
  var review = $that.find('textarea[name="review"]').val();
1448
  review = review.trim();
1449
 
1450
+ var json_data = { review_id: review_id, rating: rating, review: review, action: 'tutor_update_review_modal' };
1451
  json_data[nonce_key] = _tutorobject[nonce_key];
1452
 
1453
  $.ajax({
1454
+ url: _tutorobject.ajaxurl,
1455
+ type: 'POST',
1456
+ data: json_data,
1457
  beforeSend: function () {
1458
  $that.find('button[type="submit"]').addClass('tutor-updating-message');
1459
  },
1460
  success: function (data) {
1461
+ if (data.success) {
1462
  //Close the modal
1463
  $('.tutor-edit-review-modal-wrap').removeClass('show');
1464
  location.reload(true);
1475
  * @since v.1.4.5
1476
  */
1477
 
1478
+ $(document).on('click', '#tutor_profile_photo_button', function (e) {
1479
  e.preventDefault();
1480
 
1481
  $('#tutor_profile_photo_file').trigger('click');
1482
  });
1483
 
1484
+ $(document).on('change', '#tutor_profile_photo_file', function (event) {
1485
  event.preventDefault();
1486
 
1487
  var $file = this;
1488
  if ($file.files && $file.files[0]) {
1489
  var reader = new FileReader();
1490
+ reader.onload = function (e) {
1491
+ $('.tutor-profile-photo-upload-wrap').find('img').attr('src', e.target.result);
1492
  }
1493
  reader.readAsDataURL($file.files[0]);
1494
  }
1501
  * @since v.1.4.8
1502
  */
1503
 
1504
+ $(document).on('click', '.thread-content .subject', function (e) {
1505
  var $btn = $(this);
1506
 
1507
  var thread_id = parseInt($btn.closest('.thread-content').attr('data-thread-id'));
1508
 
1509
  var nonce_key = _tutorobject.nonce_key;
1510
+ var json_data = { thread_id: thread_id, action: 'tutor_bp_retrieve_user_records_for_thread' };
1511
  json_data[nonce_key] = _tutorobject[nonce_key];
1512
 
1513
  $.ajax({
1514
  type: 'POST',
1515
  url: ajaxurl,
1516
  data: json_data,
1517
+ beforeSend: function () {
1518
  $('#tutor-bp-thread-wrap').html('');
1519
  },
1520
  success: function (data) {
1521
+ if (data.success) {
1522
  $('#tutor-bp-thread-wrap').html(data.data.thread_head_html);
1523
  tutor_bp_setting_enrolled_courses_list();
1524
  }
1528
  });
1529
 
1530
 
1531
+ function tutor_bp_setting_enrolled_courses_list() {
1532
+ $('ul.tutor-bp-enrolled-course-list').each(function () {
1533
  var $that = $(this);
1534
  var $li = $that.find(' > li');
1535
  var itemShow = 3;
1536
 
1537
+ if ($li.length > itemShow) {
1538
  var plusCourseCount = $li.length - itemShow;
1539
+ $li.each(function (liIndex, liItem) {
1540
  var $liItem = $(this);
1541
 
1542
+ if (liIndex >= itemShow) {
1543
  $liItem.hide();
1544
  }
1545
  });
1546
 
1547
+ var infoHtml = '<a href="javascript:;" class="tutor_bp_plus_courses"><strong>+' + plusCourseCount + ' More </strong></a> Courses';
1548
  $that.closest('.tutor-bp-enrolled-courses-wrap').find('.thread-participant-enrolled-info').html(infoHtml);
1549
  }
1550
 
1553
  }
1554
  tutor_bp_setting_enrolled_courses_list();
1555
 
1556
+ $(document).on('click', 'a.tutor_bp_plus_courses', function (e) {
1557
  e.preventDefault();
1558
 
1559
  var $btn = $(this);
1562
  });
1563
 
1564
 
 
1565
  /**
1566
  * Addon, Tutor Certificate
1567
  * Certificate dropdown content and copy link
1584
  document.execCommand('copy');
1585
  document.body.removeChild(dummy);
1586
  $btn.html(copied);
1587
+ setTimeout(function () {
1588
  $btn.html(copy);
1589
  }, 2500);
1590
  });
1591
+ $(document).on('click', function (e) {
1592
  var container = $(".tutor-dropdown");
1593
  var $content = container.find('.tutor-dropdown-content');
1594
  // if the target of the click isn't the container nor a descendant of the container
1595
+ if (!container.is(e.target) && container.has(e.target).length === 0) {
1596
  $content.slideUp(100);
1597
  }
1598
  });
1599
 
1600
+
1601
+ /**
1602
+ * Tutor ajax login
1603
+ *
1604
+ * @since v.1.6.3
1605
+ */
1606
+ $(document).on('submit', '.tutor-login-form-wrap #loginform', function (e) {
1607
+ e.preventDefault();
1608
+ var $that = $(this);
1609
+ var $form_wrapper = $('.tutor-login-form-wrap');
1610
+ var form_data = $that.serialize() + '&action=tutor_user_login';
1611
+ $.ajax({
1612
+ url: _tutorobject.ajaxurl,
1613
+ type: 'POST',
1614
+ data: form_data,
1615
+ success: function (response) {
1616
+ if (response.success) {
1617
+ location.href = response.data.redirect;
1618
+ } else {
1619
+ if ($form_wrapper.find('.tutor-alert').length) {
1620
+ $form_wrapper.find('.tutor-alert').html(response.data);
1621
+ } else {
1622
+ $form_wrapper.prepend('<div class="tutor-alert tutor-alert-warning">'+response.data+'</div>');
1623
+ }
1624
+ }
1625
+ },
1626
+ });
1627
+ });
1628
+
1629
  });
assets/js/tutor.js CHANGED
@@ -1197,4 +1197,21 @@ jQuery(document).ready(function($){
1197
  $('.tutor-lesson-modal-wrap .modal-title h1').html('Assignment');
1198
  });
1199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1200
  });
1197
  $('.tutor-lesson-modal-wrap .modal-title h1').html('Assignment');
1198
  });
1199
 
1200
+ /**
1201
+ * Tutor number validation
1202
+ *
1203
+ * @since v.1.6.3
1204
+ */
1205
+ $(document).on('keyup change', '.tutor-number-validation', function(e) {
1206
+ var input = $(this);
1207
+ var val = parseInt(input.val());
1208
+ var min = parseInt(input.attr('data-min'));
1209
+ var max = parseInt(input.attr('data-max'));
1210
+ if ( val < min ) {
1211
+ input.val(min);
1212
+ } else if ( val > max ) {
1213
+ input.val(max);
1214
+ }
1215
+ });
1216
+
1217
  });
classes/Ajax.php CHANGED
@@ -27,6 +27,12 @@ class Ajax{
27
  */
28
  add_action('wp_ajax_tutor_load_edit_review_modal', array($this, 'tutor_load_edit_review_modal'));
29
  add_action('wp_ajax_tutor_update_review_modal', array($this, 'tutor_update_review_modal'));
 
 
 
 
 
 
30
  }
31
 
32
  /**
@@ -273,7 +279,6 @@ class Ajax{
273
  wp_send_json_success();
274
  }
275
 
276
-
277
  /**
278
  * Load review edit form
279
  * @since v.1.4.0
@@ -315,4 +320,60 @@ class Ajax{
315
  wp_send_json_error();
316
  }
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  }
27
  */
28
  add_action('wp_ajax_tutor_load_edit_review_modal', array($this, 'tutor_load_edit_review_modal'));
29
  add_action('wp_ajax_tutor_update_review_modal', array($this, 'tutor_update_review_modal'));
30
+
31
+ /**
32
+ * Ajax login
33
+ * @since v.1.6.3
34
+ */
35
+ add_action('wp_ajax_nopriv_tutor_user_login', array($this, 'process_ajax_login'));
36
  }
37
 
38
  /**
279
  wp_send_json_success();
280
  }
281
 
 
282
  /**
283
  * Load review edit form
284
  * @since v.1.4.0
320
  wp_send_json_error();
321
  }
322
 
323
+ /**
324
+ * Process ajax login
325
+ * @since v.1.6.3
326
+ */
327
+ public function process_ajax_login(){
328
+ tutils()->checking_nonce();
329
+
330
+ $username = tutils()->array_get('log', $_POST);
331
+ $password = tutils()->array_get('pwd', $_POST);
332
+ $redirect_to = tutils()->array_get('redirect_to', $_POST);
333
+
334
+ try {
335
+ $creds = array(
336
+ 'user_login' => trim( wp_unslash( $username ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
337
+ 'user_password' => $password, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
338
+ 'remember' => isset( $_POST['rememberme'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
339
+ );
340
+
341
+ $validation_error = new \WP_Error();
342
+ $validation_error = apply_filters( 'tutor_process_login_errors', $validation_error, $creds['user_login'], $creds['user_password'] );
343
+
344
+ if ( $validation_error->get_error_code() ) {
345
+ wp_send_json_error( '<strong>' . __( 'ERROR:', 'tutor' ) . '</strong> ' . $validation_error->get_error_message() );
346
+ }
347
+
348
+ if ( empty( $creds['user_login'] ) ) {
349
+ wp_send_json_error( '<strong>' . __( 'ERROR:', 'tutor' ) . '</strong> ' . __( 'Username is required.', 'tutor' ) );
350
+ }
351
+
352
+ // On multisite, ensure user exists on current site, if not add them before allowing login.
353
+ if ( is_multisite() ) {
354
+ $user_data = get_user_by( is_email( $creds['user_login'] ) ? 'email' : 'login', $creds['user_login'] );
355
+
356
+ if ( $user_data && ! is_user_member_of_blog( $user_data->ID, get_current_blog_id() ) ) {
357
+ add_user_to_blog( get_current_blog_id(), $user_data->ID, 'customer' );
358
+ }
359
+ }
360
+
361
+ // Perform the login.
362
+ $user = wp_signon( apply_filters( 'tutor_login_credentials', $creds ), is_ssl() );
363
+
364
+ if ( is_wp_error( $user ) ) {
365
+ $message = $user->get_error_message();
366
+ $message = str_replace( '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', $message );
367
+
368
+ wp_send_json_error( $message );
369
+ } else {
370
+ wp_send_json_success([
371
+ 'redirect' => apply_filters('tutor_login_redirect_url', $redirect_to)
372
+ ]);
373
+ }
374
+ } catch ( \Exception $e ) {
375
+ wp_send_json_error( apply_filters( 'login_errors', $e->getMessage()) );
376
+ do_action( 'tutor_login_failed' );
377
+ }
378
+ }
379
  }
classes/Course.php CHANGED
@@ -87,7 +87,13 @@ class Course extends Tutor_Base {
87
  * @since v.1.6.1
88
  */
89
  add_filter('tutor_course/single/complete_form', array($this, 'tutor_lms_hide_course_complete_btn'));
90
- add_filter('get_gradebook_generate_form_html', array($this, 'get_generate_greadbook'));
 
 
 
 
 
 
91
  }
92
 
93
  /**
@@ -1103,7 +1109,25 @@ class Course extends Tutor_Base {
1103
  return '';
1104
  }
1105
  return $html;
1106
- }
1107
-
1108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1109
  }
87
  * @since v.1.6.1
88
  */
89
  add_filter('tutor_course/single/complete_form', array($this, 'tutor_lms_hide_course_complete_btn'));
90
+ add_filter('get_gradebook_generate_form_html', array($this, 'get_generate_greadbook'));
91
+
92
+ /**
93
+ * Add social share content in header
94
+ * @since v.1.6.3
95
+ */
96
+ add_action('wp_head', array($this, 'social_share_content'));
97
  }
98
 
99
  /**
1109
  return '';
1110
  }
1111
  return $html;
1112
+ }
 
1113
 
1114
+ /**
1115
+ * Add social share content in header
1116
+ * @since v.1.6.3
1117
+ */
1118
+ public function social_share_content(){
1119
+ global $wp_query, $post;
1120
+ if ($wp_query->is_single && ! empty($wp_query->query_vars['post_type']) && $wp_query->query_vars['post_type'] === $this->course_post_type) { ?>
1121
+ <!--Facebook-->
1122
+ <meta property="og:type" content="website"/>
1123
+ <meta property="og:image" content="<?php echo get_tutor_course_thumbnail_src(); ?>" />
1124
+ <meta property="og:description" content="<?php echo esc_html($post->post_content); ?>" />
1125
+ <!--Twitter-->
1126
+ <meta name="twitter:image" content="<?php echo get_tutor_course_thumbnail_src(); ?>">
1127
+ <meta name="twitter:description" content="<?php echo esc_html($post->post_content); ?>">
1128
+ <!--Google+-->
1129
+ <meta itemprop="image" content="<?php echo get_tutor_course_thumbnail_src(); ?>">
1130
+ <meta itemprop="description" content="<?php echo esc_html($post->post_content); ?>"> <?php
1131
+ }
1132
+ }
1133
  }
classes/FormHandler.php CHANGED
@@ -18,7 +18,6 @@ if ( ! defined( 'ABSPATH' ) )
18
  class FormHandler {
19
 
20
  public function __construct() {
21
- add_action('tutor_action_tutor_user_login', array($this, 'process_login'));
22
  add_action('tutor_action_tutor_retrieve_password', array($this, 'tutor_retrieve_password'));
23
  add_action('tutor_action_tutor_process_reset_password', array($this, 'tutor_process_reset_password'));
24
 
@@ -26,55 +25,6 @@ class FormHandler {
26
  add_filter( 'tutor_lostpassword_url', array( $this, 'lostpassword_url' ) );
27
  }
28
 
29
- public function process_login(){
30
- tutils()->checking_nonce();
31
-
32
- $username = tutils()->array_get('log', $_POST);
33
- $password = tutils()->array_get('pwd', $_POST);
34
-
35
- try {
36
- $creds = array(
37
- 'user_login' => trim( wp_unslash( $username ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
38
- 'user_password' => $password, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
39
- 'remember' => isset( $_POST['rememberme'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
40
- );
41
-
42
- $validation_error = new \WP_Error();
43
- $validation_error = apply_filters( 'tutor_process_login_errors', $validation_error, $creds['user_login'], $creds['user_password'] );
44
-
45
- if ( $validation_error->get_error_code() ) {
46
- throw new \Exception( '<strong>' . __( 'Error:', 'tutor' ) . '</strong> ' . $validation_error->get_error_message() );
47
- }
48
-
49
- if ( empty( $creds['user_login'] ) ) {
50
- throw new \Exception( '<strong>' . __( 'Error:', 'tutor' ) . '</strong> ' . __( 'Username is required.', 'tutor' ) );
51
- }
52
-
53
- // On multisite, ensure user exists on current site, if not add them before allowing login.
54
- if ( is_multisite() ) {
55
- $user_data = get_user_by( is_email( $creds['user_login'] ) ? 'email' : 'login', $creds['user_login'] );
56
-
57
- if ( $user_data && ! is_user_member_of_blog( $user_data->ID, get_current_blog_id() ) ) {
58
- add_user_to_blog( get_current_blog_id(), $user_data->ID, 'customer' );
59
- }
60
- }
61
-
62
- // Perform the login.
63
- $user = wp_signon( apply_filters( 'tutor_login_credentials', $creds ), is_ssl() );
64
-
65
- if ( is_wp_error( $user ) ) {
66
- $message = $user->get_error_message();
67
- $message = str_replace( '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', $message );
68
- throw new \Exception( $message );
69
- } else {
70
- tutor_redirect_back(apply_filters('tutor_login_redirect_url', tutils()->tutor_dashboard_url()));
71
- }
72
- } catch ( \Exception $e ) {
73
- tutor_flash_set('warning', apply_filters( 'login_errors', $e->getMessage()) );
74
- do_action( 'tutor_login_failed' );
75
- }
76
- }
77
-
78
  public function tutor_retrieve_password(){
79
  tutils()->checking_nonce();
80
 
18
  class FormHandler {
19
 
20
  public function __construct() {
 
21
  add_action('tutor_action_tutor_retrieve_password', array($this, 'tutor_retrieve_password'));
22
  add_action('tutor_action_tutor_process_reset_password', array($this, 'tutor_process_reset_password'));
23
 
25
  add_filter( 'tutor_lostpassword_url', array( $this, 'lostpassword_url' ) );
26
  }
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  public function tutor_retrieve_password(){
29
  tutils()->checking_nonce();
30
 
classes/Options.php CHANGED
@@ -163,6 +163,12 @@ class Options {
163
  'default' => '0',
164
  'desc' => __('Hide admin bar option allow you to hide WordPress admin bar entirely from the frontend. It will still show to administrator roles user', 'tutor'),
165
  ),
 
 
 
 
 
 
166
  )
167
  )
168
  ),
163
  'default' => '0',
164
  'desc' => __('Hide admin bar option allow you to hide WordPress admin bar entirely from the frontend. It will still show to administrator roles user', 'tutor'),
165
  ),
166
+ 'login_error_message' => array(
167
+ 'type' => 'text',
168
+ 'label' => __('Error message for wrong login credentials', 'tutor'),
169
+ 'default' => 'Incorrect username or password.',
170
+ 'desc' => __('Login error message displayed when the user puts wrong login credentials.', 'tutor'),
171
+ ),
172
  )
173
  )
174
  ),
classes/Tools.php CHANGED
@@ -22,8 +22,25 @@ class Tools {
22
  */
23
  add_filter('tutor_tool_pages', array($this, 'tutor_tool_pages_add_wizard'));
24
  add_action('admin_init', array($this, 'redirect_to_wizard_page'));
 
25
  }
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  /**
28
  * Re-Generate Tutor Missing Pages
29
  * @since v.1.4.3
22
  */
23
  add_filter('tutor_tool_pages', array($this, 'tutor_tool_pages_add_wizard'));
24
  add_action('admin_init', array($this, 'redirect_to_wizard_page'));
25
+ add_filter('login_errors', array($this, 'login_error_message'));
26
  }
27
 
28
+ /**
29
+ * Custom Login Error Message
30
+ * @since v.1.6.0
31
+ */
32
+ public function login_error_message($error) {
33
+ $error_message = get_tutor_option('login_error_message');
34
+ if($error_message){
35
+ $pos = strpos($error, 'incorrect');
36
+ if (is_int($pos)) {
37
+ $error = $error_message;
38
+ }
39
+ }
40
+ return $error;
41
+ }
42
+
43
+
44
  /**
45
  * Re-Generate Tutor Missing Pages
46
  * @since v.1.4.3
classes/Utils.php CHANGED
@@ -1112,7 +1112,7 @@ class Utils {
1112
  */
1113
  public function course_sub_pages(){
1114
  $nav_items = array(
1115
- 'overview' => __('Overview', 'tutor'),
1116
  'questions' => __('Q&A', 'tutor'),
1117
  'announcements' => __('Announcements', 'tutor'),
1118
  );
@@ -2963,12 +2963,13 @@ class Utils {
2963
  *
2964
  * @since v.1.0.0
2965
  */
2966
- public function get_top_question($course_id = 0, $user_id = 0, $offset = 0, $limit = 20){
2967
  $course_id = $this->get_post_id($course_id);
2968
  $user_id = $this->get_user_id($user_id);
2969
 
2970
  global $wpdb;
2971
-
 
2972
  $questions = $wpdb->get_results("select {$wpdb->comments}.comment_ID,
2973
  {$wpdb->comments}.comment_post_ID,
2974
  {$wpdb->comments}.comment_author,
@@ -2984,7 +2985,7 @@ class Utils {
2984
  INNER JOIN {$wpdb->users}
2985
  ON {$wpdb->comments}.user_id = {$wpdb->users}.ID
2986
  WHERE {$wpdb->comments}.comment_post_ID = {$course_id}
2987
- AND {$wpdb->comments}.user_id = {$user_id}
2988
  AND {$wpdb->comments}.comment_type = 'tutor_q_and_a'
2989
  AND meta_key = 'tutor_question_title' ORDER BY comment_ID DESC LIMIT {$offset},{$limit} ;"
2990
  );
1112
  */
1113
  public function course_sub_pages(){
1114
  $nav_items = array(
1115
+ 'overview' => __('Resources', 'tutor'),
1116
  'questions' => __('Q&A', 'tutor'),
1117
  'announcements' => __('Announcements', 'tutor'),
1118
  );
2963
  *
2964
  * @since v.1.0.0
2965
  */
2966
+ public function get_top_question($course_id = 0, $user_id = 0, $offset = 0, $limit = 20, $is_author = false ){
2967
  $course_id = $this->get_post_id($course_id);
2968
  $user_id = $this->get_user_id($user_id);
2969
 
2970
  global $wpdb;
2971
+ $author_sql = $is_author ? "" : "AND {$wpdb->comments}.user_id = {$user_id}";
2972
+
2973
  $questions = $wpdb->get_results("select {$wpdb->comments}.comment_ID,
2974
  {$wpdb->comments}.comment_post_ID,
2975
  {$wpdb->comments}.comment_author,
2985
  INNER JOIN {$wpdb->users}
2986
  ON {$wpdb->comments}.user_id = {$wpdb->users}.ID
2987
  WHERE {$wpdb->comments}.comment_post_ID = {$course_id}
2988
+ {$author_sql}
2989
  AND {$wpdb->comments}.comment_type = 'tutor_q_and_a'
2990
  AND meta_key = 'tutor_question_title' ORDER BY comment_ID DESC LIMIT {$offset},{$limit} ;"
2991
  );
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: lms, course, elearning, education, learning management system
5
  Requires at least: 4.5
6
  Tested up to: 5.4
7
  Requires PHP: 5.4.0
8
- Stable tag: 1.6.2
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
11
 
@@ -241,6 +241,18 @@ Tutor enables you to use any third party plugins without facing any compatibilit
241
 
242
  == Changelog ==
243
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  = 1.6.2 - 14 May, 2020 =
245
 
246
  * Added: Display Name preference on the certificate, added settings to Dashboard > Settings > Profile
5
  Requires at least: 4.5
6
  Tested up to: 5.4
7
  Requires PHP: 5.4.0
8
+ Stable tag: 1.6.3
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
11
 
241
 
242
  == Changelog ==
243
 
244
+ = 1.6.3 - 20 May, 2020 =
245
+
246
+ New: Now students can answer Q&A from the frontend
247
+ New: Set answer word limit for Open-Ended and Essay type questions
248
+ New: Replace the login error message for an incorrect password with your own custom message
249
+ Update: Course single page overview tab renamed to resources page.
250
+ Fix: Randomize feature not working for answering options
251
+ Fix: Close login popup without show any error message.
252
+ Fix: Password/username validation messages and UX
253
+ Fix: Social share content issue.
254
+ Fix: Course duration and video playback time validation.
255
+
256
  = 1.6.2 - 14 May, 2020 =
257
 
258
  * Added: Display Name preference on the certificate, added settings to Dashboard > Settings > Profile
templates/single/lesson/sidebar_question_and_answer.php CHANGED
@@ -29,7 +29,15 @@ $course_id = tutils()->get_course_id_by_content($post);
29
 
30
  <div class="tutor_question_answer_wrap">
31
  <?php
32
- $questions = tutor_utils()->get_top_question($course_id);
 
 
 
 
 
 
 
 
33
 
34
  if (is_array($questions) && count($questions)){
35
  foreach ($questions as $question){
29
 
30
  <div class="tutor_question_answer_wrap">
31
  <?php
32
+ $current_user = tutor_utils()->get_user_id();
33
+ $all_instructors = tutor_utils()->get_instructors_by_course($course_id);
34
+ $all_instructors = wp_list_pluck( (array)$all_instructors, 'ID' );
35
+
36
+ if( in_array($current_user ,$all_instructors) ) {
37
+ $questions = tutor_utils()->get_top_question($course_id, $current_user, 0 , 20, true);
38
+ } else {
39
+ $questions = tutor_utils()->get_top_question($course_id);
40
+ }
41
 
42
  if (is_array($questions) && count($questions)){
43
  foreach ($questions as $question){
templates/single/quiz/body.php CHANGED
@@ -97,7 +97,16 @@ $attempt_remaining = $attempts_allowed - $attempted_count;
97
 
98
 
99
  $question_type = $question->question_type;
100
- $answers = tutor_utils()->get_answers_by_quiz_question($question->question_id);
 
 
 
 
 
 
 
 
 
101
  $show_question_mark = (bool) tutor_utils()->avalue_dot('show_question_mark', $question_settings);
102
  $answer_required = (bool) tutils()->array_get('answer_required', $question_settings);
103
 
@@ -288,12 +297,24 @@ $attempt_remaining = $attempts_allowed - $attempted_count;
288
  <textarea class="question_type_<?php echo $question_type; ?>" name="attempt[<?php echo
289
  $is_started_quiz->attempt_id; ?>][quiz_question][<?php echo $question->question_id; ?>]"></textarea>
290
  <?php
291
-
292
- if ($question_type === 'short_answer'){
293
- $characters_limit = tutor_utils()->avalue_dot('short_answer_characters_limit', $quiz_attempt_info);
294
- echo '<p class="answer_limit_desc"> characters remaining <span class="characters_remaining">'.$characters_limit.'</span> </p>';
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  }
296
-
297
  }
298
 
299
 
97
 
98
 
99
  $question_type = $question->question_type;
100
+
101
+ $rand_choice = false;
102
+ if($question_type == 'single_choice' || $question_type == 'multiple_choice'){
103
+ $choice = maybe_unserialize($question->question_settings);
104
+ if(isset($choice['randomize_question'])){
105
+ $rand_choice = $choice['randomize_question'] == 1 ? true : false;
106
+ }
107
+ }
108
+
109
+ $answers = tutor_utils()->get_answers_by_quiz_question($question->question_id, $rand_choice);
110
  $show_question_mark = (bool) tutor_utils()->avalue_dot('show_question_mark', $question_settings);
111
  $answer_required = (bool) tutils()->array_get('answer_required', $question_settings);
112
 
297
  <textarea class="question_type_<?php echo $question_type; ?>" name="attempt[<?php echo
298
  $is_started_quiz->attempt_id; ?>][quiz_question][<?php echo $question->question_id; ?>]"></textarea>
299
  <?php
300
+ if ($question_type === 'short_answer') {
301
+ $get_option_meta = tutor_utils()->get_quiz_option($quiz_id);
302
+ if(isset($get_option_meta['short_answer_characters_limit'])){
303
+ if($get_option_meta['short_answer_characters_limit'] != "" ){
304
+ $characters_limit = tutor_utils()->avalue_dot('short_answer_characters_limit', $quiz_attempt_info);
305
+ echo '<p class="answer_limit_desc"> characters remaining <span class="characters_remaining">'.$characters_limit.'</span> </p>';
306
+ }
307
+ }
308
+ }
309
+ if ($question_type === 'open_ended') {
310
+ $get_option_meta = tutor_utils()->get_quiz_option($quiz_id);
311
+ if(isset($get_option_meta['open_ended_answer_characters_limit'])){
312
+ if($get_option_meta['open_ended_answer_characters_limit'] != "" ){
313
+ $characters_limit = $get_option_meta['open_ended_answer_characters_limit'];
314
+ echo '<p class="answer_limit_desc"> characters remaining <span class="characters_remaining">'.$characters_limit.'</span> </p>';
315
+ }
316
+ }
317
  }
 
318
  }
319
 
320
 
tutor.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Tutor LMS
4
  Plugin URI: https://www.themeum.com/product/tutor-lms/
5
  Description: Tutor is a complete solution for creating a Learning Management System in WordPress way. It can help you to create small to large scale online education site very conveniently. Power features like report, certificate, course preview, private file sharing make Tutor a robust plugin for any educational institutes.
6
  Author: Themeum
7
- Version: 1.6.2
8
  Author URI: https://themeum.com
9
  Requires at least: 4.5
10
  Tested up to: 5.3
@@ -17,7 +17,7 @@ if ( ! defined( 'ABSPATH' ) )
17
  /**
18
  * Defined the tutor main file
19
  */
20
- define('TUTOR_VERSION', '1.6.2');
21
  define('TUTOR_FILE', __FILE__);
22
 
23
  /**
4
  Plugin URI: https://www.themeum.com/product/tutor-lms/
5
  Description: Tutor is a complete solution for creating a Learning Management System in WordPress way. It can help you to create small to large scale online education site very conveniently. Power features like report, certificate, course preview, private file sharing make Tutor a robust plugin for any educational institutes.
6
  Author: Themeum
7
+ Version: 1.6.3
8
  Author URI: https://themeum.com
9
  Requires at least: 4.5
10
  Tested up to: 5.3
17
  /**
18
  * Defined the tutor main file
19
  */
20
+ define('TUTOR_VERSION', '1.6.3');
21
  define('TUTOR_FILE', __FILE__);
22
 
23
  /**
views/metabox/course-additional-data.php CHANGED
@@ -24,16 +24,16 @@ $material_includes = get_post_meta($course_id, '_tutor_course_material_includes'
24
  <div class="tutor-lesson-video-runtime">
25
 
26
  <div class="tutor-option-group-field">
27
- <input type="text" value="<?php echo $durationHours ? $durationHours : '00'; ?>" name="course_duration[hours]">
28
  <p class="desc"><?php _e('HH', 'tutor'); ?></p>
29
  </div>
30
  <div class="tutor-option-group-field">
31
- <input type="text" value="<?php echo $durationMinutes ? $durationMinutes : '00'; ?>" name="course_duration[minutes]">
32
  <p class="desc"><?php _e('MM', 'tutor'); ?></p>
33
  </div>
34
 
35
  <div class="tutor-option-group-field">
36
- <input type="text" value="<?php echo $durationSeconds ? $durationSeconds : '00'; ?>" name="course_duration[seconds]">
37
  <p class="desc"><?php _e('SS', 'tutor'); ?></p>
38
  </div>
39
 
24
  <div class="tutor-lesson-video-runtime">
25
 
26
  <div class="tutor-option-group-field">
27
+ <input type="number" value="<?php echo $durationHours ? $durationHours : '00'; ?>" name="course_duration[hours]">
28
  <p class="desc"><?php _e('HH', 'tutor'); ?></p>
29
  </div>
30
  <div class="tutor-option-group-field">
31
+ <input type="number" class="tutor-number-validation" data-min="0" data-max="59" value="<?php echo $durationMinutes ? $durationMinutes : '00'; ?>" name="course_duration[minutes]">
32
  <p class="desc"><?php _e('MM', 'tutor'); ?></p>
33
  </div>
34
 
35
  <div class="tutor-option-group-field">
36
+ <input type="number" class="tutor-number-validation" data-min="0" data-max="59" value="<?php echo $durationSeconds ? $durationSeconds : '00'; ?>" name="course_duration[seconds]">
37
  <p class="desc"><?php _e('SS', 'tutor'); ?></p>
38
  </div>
39
 
views/metabox/video-metabox.php CHANGED
@@ -134,17 +134,17 @@ if ( $post->post_type !== tutor()->course_post_type){
134
  <div class="tutor-option-gorup-fields-wrap">
135
  <div class="tutor-lesson-video-runtime">
136
  <div class="tutor-option-group-field">
137
- <input type="text" value="<?php echo $runtimeHours ? $runtimeHours : '00'; ?>" name="video[runtime][hours]">
138
  <p class="desc"><?php _e('HH', 'tutor'); ?></p>
139
  </div>
140
 
141
  <div class="tutor-option-group-field">
142
- <input type="text" value="<?php echo $runtimeMinutes ? $runtimeMinutes : '00'; ?>" name="video[runtime][minutes]">
143
  <p class="desc"><?php _e('MM', 'tutor'); ?></p>
144
  </div>
145
 
146
  <div class="tutor-option-group-field">
147
- <input type="text" value="<?php echo $runtimeSeconds ? $runtimeSeconds : '00'; ?>" name="video[runtime][seconds]">
148
  <p class="desc"><?php _e('SS', 'tutor'); ?></p>
149
  </div>
150
  </div>
134
  <div class="tutor-option-gorup-fields-wrap">
135
  <div class="tutor-lesson-video-runtime">
136
  <div class="tutor-option-group-field">
137
+ <input type="number" value="<?php echo $runtimeHours ? $runtimeHours : '00'; ?>" name="video[runtime][hours]">
138
  <p class="desc"><?php _e('HH', 'tutor'); ?></p>
139
  </div>
140
 
141
  <div class="tutor-option-group-field">
142
+ <input type="number" class="tutor-number-validation" data-min="0" data-max="59" value="<?php echo $runtimeMinutes ? $runtimeMinutes : '00'; ?>" name="video[runtime][minutes]">
143
  <p class="desc"><?php _e('MM', 'tutor'); ?></p>
144
  </div>
145
 
146
  <div class="tutor-option-group-field">
147
+ <input type="number" class="tutor-number-validation" data-min="0" data-max="59" value="<?php echo $runtimeSeconds ? $runtimeSeconds : '00'; ?>" name="video[runtime][seconds]">
148
  <p class="desc"><?php _e('SS', 'tutor'); ?></p>
149
  </div>
150
  </div>
views/modal/edit_quiz.php CHANGED
@@ -294,6 +294,17 @@ if ( ! $quiz){
294
  <p class="help"><?php _e('Student will place answer in short answer question type within this characters limit.', 'tutor'); ?></p>
295
  </div>
296
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
  <div class="tutor-quiz-builder-modal-control-btn-group">
299
  <div class="quiz-builder-btn-group-left">
294
  <p class="help"><?php _e('Student will place answer in short answer question type within this characters limit.', 'tutor'); ?></p>
295
  </div>
296
 
297
+ <div class="tutor-quiz-builder-group">
298
+ <h4><?php _e('Open-Ended/Essay questions answer character limit', 'tutor'); ?></h4>
299
+ <div class="tutor-quiz-builder-row">
300
+ <div class="tutor-quiz-builder-col">
301
+ <input type="number" name="quiz_option[open_ended_answer_characters_limit]" value="<?php echo tutor_utils()->get_quiz_option
302
+ ($quiz_id, 'open_ended_answer_characters_limit', 500); ?>" >
303
+ </div>
304
+ </div>
305
+ <p class="help"><?php _e('Students will place the answer in the Open-Ended/Essay question type within this character limit.', 'tutor'); ?></p>
306
+ </div>
307
+
308
 
309
  <div class="tutor-quiz-builder-modal-control-btn-group">
310
  <div class="quiz-builder-btn-group-left">