PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.7.5
Tutor LMS – eLearning and online course solution v1.7.5
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / Course.php
tutor / classes Last commit date
Addons.php 5 years ago Admin.php 5 years ago Ajax.php 5 years ago Assets.php 5 years ago Course.php 5 years ago Course_Filter.php 5 years ago Course_Settings_Tabs.php 5 years ago Course_Widget.php 5 years ago Custom_Validation.php 5 years ago Dashboard.php 5 years ago Delete_Enrollment_With_Order.php 5 years ago Email.php 5 years ago FormHandler.php 5 years ago Frontend.php 5 years ago Gutenberg.php 5 years ago Instructor.php 5 years ago Instructors_List.php 5 years ago Lesson.php 5 years ago Options.php 5 years ago Post_types.php 5 years ago Private_Course_Access.php 5 years ago Q_and_A.php 5 years ago Question_Answers_List.php 5 years ago Quiz.php 5 years ago Quiz_Attempts_List.php 5 years ago RestAPI.php 5 years ago Rewrite_Rules.php 5 years ago Shortcode.php 5 years ago Student.php 5 years ago Students_List.php 5 years ago Taxonomies.php 5 years ago Template.php 5 years ago Theme_Compatibility.php 5 years ago Tools.php 5 years ago Tutor.php 5 years ago TutorEDD.php 5 years ago Tutor_Base.php 5 years ago Tutor_List_Table.php 5 years ago Tutor_Setup.php 5 years ago Upgrader.php 5 years ago User.php 5 years ago Utils.php 5 years ago Video_Stream.php 5 years ago Withdraw.php 5 years ago Withdraw_Requests_List.php 5 years ago WooCommerce.php 5 years ago
Course.php
1284 lines
1 <?php
2 namespace TUTOR;
3
4 if ( ! defined( 'ABSPATH' ) )
5 exit;
6
7 class Course extends Tutor_Base {
8
9 private $additional_meta=array(
10 '_tutor_disable_qa',
11 '_tutor_is_public_course'
12 );
13
14 public function __construct() {
15 parent::__construct();
16
17 add_action( 'add_meta_boxes', array($this, 'register_meta_box') );
18 add_action('save_post_'.$this->course_post_type, array($this, 'save_course_meta'), 10, 2);
19 add_action('wp_ajax_tutor_add_course_topic', array($this, 'tutor_add_course_topic'));
20 add_action('wp_ajax_tutor_update_topic', array($this, 'tutor_update_topic'));
21
22 //Add Column
23 add_filter( "manage_{$this->course_post_type}_posts_columns", array($this, 'add_column'), 10,1 );
24 add_action( "manage_{$this->course_post_type}_posts_custom_column" , array($this, 'custom_lesson_column'), 10, 2 );
25
26 add_action('admin_action_tutor_delete_topic', array($this, 'tutor_delete_topic'));
27 add_action('admin_action_tutor_delete_announcement', array($this, 'tutor_delete_announcement'));
28
29 //Frontend Action
30 add_action('template_redirect', array($this, 'enroll_now'));
31 add_action('template_redirect', array($this, 'mark_course_complete'));
32
33 //Modal Perform
34 add_action('wp_ajax_tutor_load_instructors_modal', array($this, 'tutor_load_instructors_modal'));
35 add_action('wp_ajax_tutor_add_instructors_to_course', array($this, 'tutor_add_instructors_to_course'));
36 add_action('wp_ajax_detach_instructor_from_course', array($this, 'detach_instructor_from_course'));
37
38 /**
39 * Frontend Dashboard
40 */
41 add_action('wp_ajax_tutor_delete_dashboard_course', array($this, 'tutor_delete_dashboard_course'));
42
43 /**
44 * Gutenberg author support
45 */
46 add_filter('wp_insert_post_data', array($this, 'tutor_add_gutenberg_author'), '99', 2);
47
48 /**
49 * Frontend metabox supports for course builder
50 * @since v.1.3.4
51 */
52 add_action('tutor/dashboard_course_builder_form_field_after', array($this, 'register_meta_box_in_frontend'));
53
54
55 /**
56 * Do Stuff for the course save from frontend
57 */
58 add_action('save_tutor_course', array($this, 'attach_product_with_course'), 10, 2);
59
60 /**
61 * Add course level to course settings
62 * @since v.1.4.1
63 */
64 add_action('tutor_course/settings_tab_content/after/general', array($this, 'add_course_level_to_settings'));
65
66 /**
67 * Enable Disable Course Details Page Feature
68 * @since v.1.4.8
69 */
70 $this->course_elements_enable_disable();
71
72 /**
73 * @since v.1.4.8
74 * Check if course starting, set meta if starting
75 */
76 add_action('tutor_lesson_load_before', array($this, 'tutor_lesson_load_before'));
77
78 /**
79 * @since v.1.4.9
80 * Filter product in shop page
81 */
82 $this->filter_product_in_shop_page();
83
84 /**
85 * Remove the course price if enrolled
86 * @since 1.5.8
87 */
88 add_filter('tutor_course_price', array($this, 'remove_price_if_enrolled'));
89
90
91 /**
92 * Remove course complete button if course completion is strict mode
93 * @since v.1.6.1
94 */
95 add_filter('tutor_course/single/complete_form', array($this, 'tutor_lms_hide_course_complete_btn'));
96 add_filter('get_gradebook_generate_form_html', array($this, 'get_generate_greadbook'));
97
98 /**
99 * Add social share content in header
100 * @since v.1.6.3
101 */
102 add_action('wp_head', array($this, 'social_share_content'));
103
104 /**
105 * Delete course data after deleted course
106 * @since v.1.6.6
107 */
108 add_action('deleted_post', array($this, 'delete_tutor_course_data'));
109
110
111 add_action('tutor/dashboard_course_builder_form_field_after', array($this, 'tutor_course_setting_metabox_frontend'));
112 }
113
114 /**
115 * Registering metabox
116 */
117 public function register_meta_box(){
118 $coursePostType = tutor()->course_post_type;
119 $course_marketplace = tutor_utils()->get_option('enable_course_marketplace');
120 //add_meta_box( 'tutor-course-levels', __( 'Course Level', 'tutor' ), array($this, 'course_level_metabox'), $coursePostType );
121 add_meta_box( 'tutor-course-topics', __( 'Course Builder', 'tutor' ), array($this, 'course_meta_box'), $coursePostType );
122 add_meta_box( 'tutor-course-additional-data', __( 'Additional Data', 'tutor' ), array($this, 'course_additional_data_meta_box'), $coursePostType );
123 add_meta_box( 'tutor-course-videos', __( 'Video', 'tutor' ), array($this, 'video_metabox'), $coursePostType );
124 if ($course_marketplace) {
125 add_meta_box( 'tutor-instructors', __( 'Instructors', 'tutor' ), array( $this, 'instructors_metabox' ), $coursePostType );
126 }
127 add_meta_box( 'tutor-announcements', __( 'Announcements', 'tutor' ), array($this, 'announcements_metabox'), $coursePostType );
128
129 /**
130 * Tutor course sidebar settings metabox
131 * @since v.1.7.0
132 */
133 add_meta_box( 'tutor-course-sidebar-settings', __( 'Tutor Settings', 'tutor' ), array($this, 'tutor_course_setting_metabox'), $coursePostType, 'side' );
134 }
135
136 public function course_meta_box($echo = true){
137 ob_start();
138 include tutor()->path.'views/metabox/course-topics.php';
139 $content = ob_get_clean();
140
141 if ($echo){
142 echo $content;
143 }else{
144 return $content;
145 }
146 }
147
148 public function course_additional_data_meta_box($echo = true){
149
150 ob_start();
151 include tutor()->path.'views/metabox/course-additional-data.php';
152 $content = ob_get_clean();
153
154 if ($echo){
155 echo $content;
156 }else{
157 return $content;
158 }
159 }
160
161 public function video_metabox($echo = true){
162 ob_start();
163 include tutor()->path.'views/metabox/video-metabox.php';
164 $content = ob_get_clean();
165
166 if ($echo){
167 echo $content;
168 }else{
169 return $content;
170 }
171 }
172
173 public function course_level_metabox($echo = true){
174 ob_start();
175 include tutor()->path.'views/metabox/course-level-metabox.php';
176 $content = ob_get_clean();
177
178 if ($echo){
179 echo $content;
180 }else{
181 return $content;
182 }
183 }
184
185 public function announcements_metabox($echo = true){
186 ob_start();
187 include tutor()->path.'views/metabox/announcements-metabox.php';
188 $content = ob_get_clean();
189
190 if ($echo){
191 echo $content;
192 }else{
193 return $content;
194 }
195 }
196
197 public function instructors_metabox($echo = true){
198 ob_start();
199 include tutor()->path . 'views/metabox/instructors-metabox.php';
200 $content = ob_get_clean();
201
202 if ($echo){
203 echo $content;
204 }else{
205 return $content;
206 }
207 }
208
209 /**
210 * Register metabox in course builder tutor
211 * @since v.1.3.4
212 */
213 public function register_meta_box_in_frontend(){
214 do_action('tutor_course_builder_metabox_before', get_the_ID());
215 course_builder_section_wrap($this->video_metabox($echo = false), __( 'Video', 'tutor' ) );
216 course_builder_section_wrap($this->course_meta_box($echo = false), __( 'Course Builder', 'tutor' ) );
217 course_builder_section_wrap($this->instructors_metabox($echo = false), __( 'Instructors', 'tutor' ) );
218 course_builder_section_wrap($this->course_additional_data_meta_box($echo = false), __( 'Additional Data', 'tutor' ) );
219 course_builder_section_wrap($this->announcements_metabox($echo = false), __( 'Announcements', 'tutor' ) );
220 do_action('tutor_course_builder_metabox_after', get_the_ID());
221 }
222
223 /**
224 * @param $post_ID
225 *
226 * Insert Topic and attached it with Course
227 */
228 public function save_course_meta($post_ID, $post){
229 global $wpdb;
230
231 do_action( "tutor_save_course", $post_ID, $post);
232
233 /**
234 * Insert Topic
235 */
236 /*
237 if ( ! empty($_POST['topic_title'])) {
238 $topic_title = sanitize_text_field( $_POST['topic_title'] );
239 $topic_summery = wp_kses_post( $_POST['topic_summery'] );
240
241 $post_arr = array(
242 'post_type' => 'topics',
243 'post_title' => $topic_title,
244 'post_content' => $topic_summery,
245 'post_status' => 'publish',
246 'post_author' => get_current_user_id(),
247 'post_parent' => $post_ID,
248 );
249 wp_insert_post( $post_arr );
250 }*/
251
252 /**
253 * Save course price type
254 */
255 $price_type = tutils()->array_get('tutor_course_price_type', $_POST);
256 if ($price_type){
257 update_post_meta($post_ID, '_tutor_course_price_type', $price_type);
258 }
259
260 //Course Duration
261 if ( ! empty($_POST['course_duration'])){
262 $video = tutils()->sanitize_array($_POST['course_duration']);
263 update_post_meta($post_ID, '_course_duration', $video);
264 }
265
266 if ( ! empty($_POST['course_level'])){
267 $course_level = sanitize_text_field($_POST['course_level']);
268 update_post_meta($post_ID, '_tutor_course_level', $course_level);
269 }
270
271 $additional_data_edit = tutils()->avalue_dot('_tutor_course_additional_data_edit', $_POST);
272 if ($additional_data_edit) {
273 if (!empty($_POST['course_benefits'])) {
274 $course_benefits = wp_kses_post($_POST['course_benefits']);
275 update_post_meta($post_ID, '_tutor_course_benefits', $course_benefits);
276 } else {
277 delete_post_meta($post_ID, '_tutor_course_benefits');
278 }
279
280 if (!empty($_POST['course_requirements'])) {
281 $requirements = wp_kses_post($_POST['course_requirements']);
282 update_post_meta($post_ID, '_tutor_course_requirements', $requirements);
283 } else {
284 delete_post_meta($post_ID, '_tutor_course_requirements');
285 }
286
287 if (!empty($_POST['course_target_audience'])) {
288 $target_audience = wp_kses_post($_POST['course_target_audience']);
289 update_post_meta($post_ID, '_tutor_course_target_audience', $target_audience);
290 } else {
291 delete_post_meta($post_ID, '_tutor_course_target_audience');
292 }
293
294 if (!empty($_POST['course_material_includes'])) {
295 $material_includes = wp_kses_post($_POST['course_material_includes']);
296 update_post_meta($post_ID, '_tutor_course_material_includes', $material_includes);
297 } else {
298 delete_post_meta($post_ID, '_tutor_course_material_includes');
299 }
300 }
301
302
303 /**
304 * Sorting Topics and lesson
305 */
306 if ( ! empty($_POST['tutor_topics_lessons_sorting'])){
307 $new_order = sanitize_text_field(stripslashes($_POST['tutor_topics_lessons_sorting']));
308 $order = json_decode($new_order, true);
309
310 if (is_array($order) && count($order)){
311 $i = 0;
312 foreach ($order as $topic ){
313 $i++;
314 $wpdb->update(
315 $wpdb->posts,
316 array('menu_order' => $i),
317 array('ID' => $topic['topic_id'])
318 );
319
320 /**
321 * Removing All lesson with topic
322 */
323
324 $wpdb->update(
325 $wpdb->posts,
326 array('post_parent' => 0),
327 array('post_parent' => $topic['topic_id'])
328 );
329
330 /**
331 * Lesson Attaching with topic ID
332 * sorting lesson
333 */
334 if (isset($topic['lesson_ids'])){
335 $lesson_ids = $topic['lesson_ids'];
336 }else{
337 $lesson_ids = array();
338 }
339 if (count($lesson_ids)){
340 foreach ($lesson_ids as $lesson_key => $lesson_id ){
341 $wpdb->update(
342 $wpdb->posts,
343 array('post_parent' => $topic['topic_id'], 'menu_order' => $lesson_key),
344 array('ID' => $lesson_id)
345 );
346 }
347 }
348 }
349 }
350 }
351
352 if ($additional_data_edit) {
353 if ( ! empty($_POST['video']['source'])) { //Video
354 $video = tutor_utils()->array_get('video', $_POST);
355 update_post_meta($post_ID, '_video', $video);
356 }else{
357 delete_post_meta($post_ID, '_video');
358 }
359 }
360
361 /**
362 * Adding author to instructor automatically
363 */
364
365 $author_id = $post->post_author;
366 $attached = (int) $wpdb->get_var(" SELECT COUNT(umeta_id) FROM {$wpdb->usermeta} WHERE user_id = {$author_id} AND meta_key = '_tutor_instructor_course_id' AND meta_value = {$post_ID} ");
367 if ( ! $attached){
368 add_user_meta($author_id, '_tutor_instructor_course_id', $post_ID);
369 }
370
371 //Announcements
372 if ( ! wp_doing_ajax()) {
373 $announcement_title = tutor_utils()->avalue_dot( 'announcements.title', $_POST );
374 if ( ! empty( $announcement_title ) ) {
375 $title = sanitize_text_field( tutor_utils()->avalue_dot( 'announcements.title', $_POST ) );
376 $content = wp_kses_post( tutor_utils()->avalue_dot( 'announcements.content', $_POST ) );
377
378 $post_arr = array(
379 'post_type' => 'tutor_announcements',
380 'post_title' => $title,
381 'post_content' => $content,
382 'post_status' => 'publish',
383 'post_author' => get_current_user_id(),
384 'post_parent' => $post_ID,
385 );
386 $announcement_id = wp_insert_post( $post_arr );
387
388 if ($announcement_id) {
389 $announcement = (object) $post_arr;
390 do_action('tutor_announcements/after/save', $announcement_id, $announcement);
391 }
392 }
393 }
394
395 /**
396 * Disable question and answer for this course
397 * @since 1.7.0
398 */
399 if ($additional_data_edit) {
400
401 foreach($this->additional_meta as $key){
402 update_post_meta($post_ID, $key, (isset($_POST[$key]) ? 'yes' : 'no'));
403 }
404 }
405
406 do_action( "tutor_save_course_after", $post_ID, $post);
407 }
408
409 /**
410 * Tutor add course topic
411 */
412 public function tutor_add_course_topic(){
413 if (empty($_POST['topic_title']) ) {
414 wp_send_json_error();
415 }
416 $course_id = (int) tutor_utils()->avalue_dot('tutor_topic_course_ID', $_POST);
417 $next_topic_order_id = tutor_utils()->get_next_topic_order_id($course_id);
418
419 $topic_title = sanitize_text_field( $_POST['topic_title'] );
420 $topic_summery = wp_kses_post( $_POST['topic_summery'] );
421
422 $post_arr = array(
423 'post_type' => 'topics',
424 'post_title' => $topic_title,
425 'post_content' => $topic_summery,
426 'post_status' => 'publish',
427 'post_author' => get_current_user_id(),
428 'post_parent' => $course_id,
429 'menu_order' => $next_topic_order_id,
430 );
431 $current_topic_id = wp_insert_post( $post_arr );
432
433 ob_start();
434 include tutor()->path.'views/metabox/course-contents.php';
435 $course_contents = ob_get_clean();
436
437 wp_send_json_success(array('course_contents' => $course_contents));
438 }
439
440 /**
441 * Update the topic
442 */
443 public function tutor_update_topic(){
444 $topic_id = (int) sanitize_text_field($_POST['topic_id']);
445 $topic_title = sanitize_text_field($_POST['topic_title']);
446 $topic_summery = wp_kses_post($_POST['topic_summery']);
447
448 $topic_attr = array(
449 'ID' => $topic_id,
450 'post_title' => $topic_title,
451 'post_content' => $topic_summery,
452 );
453 wp_update_post( $topic_attr );
454
455 wp_send_json_success(array('msg' => __('Topic has been updated', 'tutor') ));
456 }
457
458
459 /**
460 * @param $columns
461 *
462 * @return mixed
463 *
464 * Add Lesson column
465 */
466 public function add_column($columns){
467 $date_col = $columns['date'];
468 unset($columns['date']);
469 $columns['lessons'] = __('Lessons', 'tutor');
470 $columns['students'] = __('Students', 'tutor');
471 $columns['price'] = __('Price', 'tutor');
472 $columns['date'] = $date_col;
473
474 return $columns;
475 }
476
477 /**
478 * @param $column
479 * @param $post_id
480 *
481 */
482 public function custom_lesson_column($column, $post_id ){
483 if ($column === 'lessons'){
484 echo tutor_utils()->get_lesson_count_by_course($post_id);
485 }
486
487 if ($column === 'students'){
488 echo tutor_utils()->count_enrolled_users_by_course($post_id);
489 }
490
491 if ($column === 'price'){
492 $price = tutor_utils()->get_course_price($post_id);
493 if ($price){
494 $monetize_by = tutils()->get_option('monetize_by');
495 if (function_exists('wc_price') && $monetize_by === 'wc'){
496 echo '<span class="tutor-label-success">'.wc_price($price).'</span>';
497 }else{
498 echo '<span class="tutor-label-success">'.$price.'</span>';
499 }
500 }else{
501 echo apply_filters('tutor-loop-default-price', 'free');
502 }
503 }
504 }
505
506
507 public function tutor_delete_topic(){
508 if (!isset($_GET[tutor()->nonce]) || !wp_verify_nonce($_GET[tutor()->nonce], tutor()->nonce_action)) {
509 exit();
510 }
511 if ( ! isset($_GET['topic_id'])){
512 exit();
513 }
514
515 global $wpdb;
516
517 $topic_id = (int) sanitize_text_field($_GET['topic_id']);
518 $wpdb->update(
519 $wpdb->posts,
520 array('post_parent' => 0),
521 array('post_parent' => $topic_id)
522 );
523
524 $wpdb->delete(
525 $wpdb->postmeta,
526 array('post_id' => $topic_id)
527 );
528
529 wp_delete_post($topic_id);
530 wp_safe_redirect(wp_get_referer());
531 }
532
533 public function tutor_delete_announcement(){
534 tutor_utils()->checking_nonce('get');
535
536 $announcement_id = (int) sanitize_text_field($_GET['topic_id']);
537
538 wp_delete_post($announcement_id);
539 wp_safe_redirect(wp_get_referer());
540 }
541
542 public function enroll_now(){
543
544 //Checking if action comes from Enroll form
545 if (tutor_utils()->array_get('tutor_course_action', $_POST) !== '_tutor_course_enroll_now' || ! isset($_POST['tutor_course_id']) ){
546 return;
547 }
548 //Checking Nonce
549 tutor_utils()->checking_nonce();
550
551 $user_id = get_current_user_id();
552 if ( ! $user_id){
553 exit(__('Please Sign In first', 'tutor'));
554 }
555
556 $course_id = (int) sanitize_text_field($_POST['tutor_course_id']);
557 $user_id = get_current_user_id();
558
559 /**
560 * TODO: need to check purchase information
561 */
562
563 $is_purchasable = tutor_utils()->is_course_purchasable($course_id);
564
565 /**
566 * If is is not purchasable, it's free, and enroll right now
567 *
568 * if purchasable, then process purchase.
569 *
570 * @since: v.1.0.0
571 */
572 if ($is_purchasable){
573 //process purchase
574
575 }else{
576 //Free enroll
577 tutor_utils()->do_enroll($course_id);
578 }
579
580 $referer_url = wp_get_referer();
581 wp_redirect($referer_url);
582 }
583
584 /**
585 *
586 * Mark complete completed
587 *
588 * @since v.1.0.0
589 */
590 public function mark_course_complete(){
591 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_complete_course' ){
592 return;
593 }
594 //Checking nonce
595 tutor_utils()->checking_nonce();
596
597 $user_id = get_current_user_id();
598
599 //TODO: need to show view if not signed_in
600 if ( ! $user_id){
601 die(__('Please Sign-In', 'tutor'));
602 }
603
604 $course_id = (int) sanitize_text_field($_POST['course_id']);
605
606 do_action('tutor_course_complete_before', $course_id);
607 /**
608 * Marking course completed at Comment
609 */
610
611 global $wpdb;
612
613 $date = date("Y-m-d H:i:s", tutor_time());
614
615 //Making sure that, hash is unique
616 do{
617 $hash = substr(md5(wp_generate_password(32).$date.$course_id.$user_id), 0, 16);
618 $hasHash = (int) $wpdb->get_var("SELECT COUNT(comment_ID) from {$wpdb->comments} WHERE comment_agent = 'TutorLMSPlugin' AND comment_type = 'course_completed' AND comment_content = '{$hash}' ");
619 }while($hasHash > 0);
620
621 $data = array(
622 'comment_post_ID' => $course_id,
623 'comment_author' => $user_id,
624 'comment_date' => $date,
625 'comment_date_gmt' => get_gmt_from_date($date),
626 'comment_content' => $hash, //Identification Hash
627 'comment_approved' => 'approved',
628 'comment_agent' => 'TutorLMSPlugin',
629 'comment_type' => 'course_completed',
630 'user_id' => $user_id,
631 );
632
633 $wpdb->insert($wpdb->comments, $data);
634
635 do_action('tutor_course_complete_after', $course_id, $user_id);
636
637 wp_redirect(get_the_permalink($course_id));
638 }
639
640
641 public function tutor_load_instructors_modal(){
642 global $wpdb;
643
644 $course_id = (int) sanitize_text_field($_POST['course_id']);
645 $search_terms = sanitize_text_field(tutor_utils()->avalue_dot('search_terms', $_POST));
646
647 $saved_instructors = tutor_utils()->get_instructors_by_course($course_id);
648
649 $instructors = array();
650
651
652 $not_in_sql = apply_filters('tutor_instructor_query_when_exists', " AND ID <1 ");
653
654 if ($saved_instructors){
655 $saved_instructors_ids = wp_list_pluck($saved_instructors, 'ID');
656 $instructor_not_in_ids = implode(',', $saved_instructors_ids);
657 $not_in_sql .= "AND ID NOT IN($instructor_not_in_ids) ";
658 }
659
660 $search_sql = '';
661 if ($search_terms){
662 $search_sql = "AND (user_login like '%{$search_terms}%' or user_nicename like '%{$search_terms}%' or display_name like '%{$search_terms}%') ";
663 }
664
665 $instructors = $wpdb->get_results("select ID, display_name from {$wpdb->users}
666 INNER JOIN {$wpdb->usermeta} ON ID = user_id AND meta_key = '_tutor_instructor_status' AND meta_value = 'approved'
667 WHERE 1=1 {$not_in_sql} {$search_sql} limit 10 ");
668
669 $output = '';
670 if (is_array($instructors) && count($instructors)){
671 $instructor_output = '';
672 foreach ($instructors as $instructor){
673 $instructor_output .= "<p><label><input type='radio' name='tutor_instructor_ids[]' value='{$instructor->ID}' > {$instructor->display_name} </label></p>";
674 }
675
676 $output .= apply_filters('tutor_course_instructors_html', $instructor_output, $instructors);
677
678 }else{
679 $output .= __('<p>No instructor available or you have already added maximum instructors</p>', 'tutor');
680 }
681
682
683 if ( ! defined('TUTOR_MT_VERSION')){
684 $output .= '<p class="tutor-notice-warning" style="margin-top: 50px; font-size: 14px;">'. sprintf( __('To add unlimited multiple instructors in your course, get %sTutor LMS Pro%s', 'tutor'), '<a href="https://www.themeum.com/product/tutor-lms" target="_blank">', "</a>" ) .'</p>';
685 }
686
687 wp_send_json_success(array('output' => $output));
688 }
689
690 public function tutor_add_instructors_to_course(){
691 $course_id = (int) sanitize_text_field($_POST['course_id']);
692 $instructor_ids = tutor_utils()->avalue_dot('tutor_instructor_ids', $_POST);
693
694 if (is_array($instructor_ids) && count($instructor_ids)){
695 foreach ($instructor_ids as $instructor_id){
696 add_user_meta($instructor_id, '_tutor_instructor_course_id', $course_id);
697 }
698 }
699
700 $saved_instructors = tutor_utils()->get_instructors_by_course($course_id);
701 $output = '';
702
703 if ($saved_instructors){
704 foreach ($saved_instructors as $t){
705
706 $output .= '<div id="added-instructor-id-'.$t->ID.'" class="added-instructor-item added-instructor-item-'.$t->ID.'" data-instructor-id="'.$t->ID.'">
707 <span class="instructor-icon">'.get_avatar($t->ID, 30).'</span>
708 <span class="instructor-name"> '.$t->display_name.' </span>
709 <span class="instructor-control">
710 <a href="javascript:;" class="tutor-instructor-delete-btn"><i class="tutor-icon-line-cross"></i></a>
711 </span>
712 </div>';
713 }
714 }
715
716 wp_send_json_success(array('output' => $output));
717 }
718
719 public function detach_instructor_from_course(){
720 global $wpdb;
721
722 $instructor_id = (int) sanitize_text_field($_POST['instructor_id']);
723 $course_id = (int) sanitize_text_field($_POST['course_id']);
724
725 $wpdb->delete($wpdb->usermeta, array('user_id' => $instructor_id, 'meta_key' => '_tutor_instructor_course_id', 'meta_value' => $course_id) );
726 wp_send_json_success();
727 }
728
729 public function tutor_delete_dashboard_course(){
730 $course_id = intval(sanitize_text_field($_POST['course_id']));
731 wp_trash_post($course_id);
732 wp_send_json_success(['element'=>'course']);
733 }
734
735
736 public function tutor_add_gutenberg_author($data , $postarr){
737 global $wpdb;
738
739 $courses_post_type = tutor()->course_post_type;
740 $post_type = tutils()->array_get('post_type', $postarr);
741
742 /*
743 $post_author = (int) tutor_utils()->avalue_dot('post_author', $data);
744
745 if ( ! $post_author){
746 $user_ID = (int) tutor_utils()->avalue_dot('user_ID', $postarr);
747 if ($user_ID){
748 $data['post_author'] = $user_ID;
749 }else{
750 $post_ID = (int) tutor_utils()->avalue_dot('ID', $postarr);
751 $post_author = (int) $wpdb->get_var("SELECT post_author FROM {$wpdb->posts} WHERE ID = {$post_ID} ");
752
753 $data['post_author'] = $post_author;
754 }
755 }*/
756
757 if ($courses_post_type === $post_type){
758 $post_ID = (int) tutor_utils()->avalue_dot('ID', $postarr);
759 $post_author = (int) $wpdb->get_var("SELECT post_author FROM {$wpdb->posts} WHERE ID = {$post_ID} ");
760
761 if ($post_author > 0){
762 $data['post_author'] = $post_author;
763 }else{
764 $data['post_author'] = get_current_user_id();
765 }
766 }
767
768 return $data;
769 }
770
771
772 /**
773 * @param $post_ID
774 * @param $postData
775 *
776 * Attach product during save course from the frontend course dashboard.
777 *
778 * @return string
779 *
780 * @since v.1.3.4
781 */
782 public function attach_product_with_course($post_ID, $postData){
783 $attached_product_id = tutor_utils()->get_course_product_id($post_ID);
784 $course_price = sanitize_text_field(tutor_utils()->array_get('course_price', $_POST));
785
786 if ( ! $course_price){
787 return;
788 }
789
790 $monetize_by = tutor_utils()->get_option('monetize_by');
791 $course = get_post($post_ID);
792
793 if ($monetize_by === 'wc'){
794
795 $is_update = false;
796 if ($attached_product_id){
797 $wc_product = get_post_meta($attached_product_id, '_product_version', true);
798 if ($wc_product){
799 $is_update = true;
800 }
801 }
802
803 if ($is_update){
804
805 $productObj = new \WC_Product($attached_product_id);
806 $productObj->set_price($course_price); // set product price
807 $productObj->set_regular_price($course_price); // set product regular price
808 $product_id = $productObj->save();
809
810 }else{
811
812 $productObj = new \WC_Product();
813 $productObj->set_name($course->post_title);
814 $productObj->set_status('publish');
815 $productObj->set_price($course_price); // set product price
816 $productObj->set_regular_price($course_price); // set product regular price
817
818 $product_id = $productObj->save();
819 if ($product_id) {
820 update_post_meta( $post_ID, '_tutor_course_product_id', $product_id );
821 //Mark product for woocommerce
822 update_post_meta( $product_id, '_virtual', 'yes' );
823 update_post_meta( $product_id, '_tutor_product', 'yes' );
824
825 $coursePostThumbnail = get_post_meta( $post_ID, '_thumbnail_id', true );
826 if ( $coursePostThumbnail ) {
827 set_post_thumbnail( $product_id, $coursePostThumbnail );
828 }
829 }
830 }
831
832 }elseif ($monetize_by === 'edd'){
833
834 $is_update = false;
835
836 if ($attached_product_id){
837 $edd_price = get_post_meta($attached_product_id, 'edd_price', true);
838 if ($edd_price){
839 $is_update = true;
840 }
841 }
842
843 if ($is_update){
844 //Update the product
845 update_post_meta( $attached_product_id, 'edd_price', $course_price );
846 }else{
847 //Create new product
848
849 $post_arr = array(
850 'post_type' => 'download',
851 'post_title' => $course->post_title,
852 'post_status' => 'publish',
853 'post_author' => get_current_user_id(),
854 );
855 $download_id = wp_insert_post( $post_arr );
856 if ($download_id ) {
857 //edd_price
858 update_post_meta( $download_id, 'edd_price', $course_price );
859
860 update_post_meta( $post_ID, '_tutor_course_product_id', $download_id );
861 //Mark product for EDD
862 update_post_meta( $download_id, '_tutor_product', 'yes' );
863
864 $coursePostThumbnail = get_post_meta( $post_ID, '_thumbnail_id', true );
865 if ( $coursePostThumbnail ) {
866 set_post_thumbnail( $download_id, $coursePostThumbnail );
867 }
868
869 }
870
871 }
872
873
874 }
875
876 }
877
878
879 /**
880 * Add Course level to course settings
881 * @since v.1.4.1
882 */
883 public function add_course_level_to_settings(){
884 include tutor()->path.'views/metabox/course-level-metabox.php';
885 }
886
887 /**
888 * Check if course starting
889 *
890 * @since v.1.4.8
891 */
892 public function tutor_lesson_load_before(){
893 $course_id = tutils()->get_course_id_by_content(get_the_ID());
894 $completed_lessons = tutor_utils()->get_completed_lesson_count_by_course($course_id);
895 if (is_user_logged_in()){
896 $is_course_started = get_post_meta($course_id, '_tutor_course_started', true);
897 if ( ! $completed_lessons && ! $is_course_started){
898 update_post_meta($course_id, '_tutor_course_started', tutor_time());
899 do_action('tutor/course/started', $course_id);
900 }
901 }
902 }
903
904 /**
905 * Add Course level to course settings
906 * @since v.1.4.8
907 */
908 public function course_elements_enable_disable(){
909 add_filter('tutor_course/single/completing-progress-bar', array($this, 'enable_disable_course_progress_bar') );
910 add_filter('tutor_course/single/material_includes', array($this, 'enable_disable_material_includes') );
911 add_filter('tutor_course/single/content', array($this, 'enable_disable_course_content') );
912 add_filter('tutor_course/single/benefits_html', array($this, 'enable_disable_course_benefits') );
913 add_filter('tutor_course/single/requirements_html', array($this, 'enable_disable_course_requirements') );
914 add_filter('tutor_course/single/audience_html', array($this, 'enable_disable_course_target_audience') );
915 add_filter('tutor_course/single/enrolled/nav_items', array($this, 'enable_disable_course_nav_items') );
916 }
917
918 /**
919 * Enable disable course progress bar
920 * @since v.1.4.8
921 */
922 public function enable_disable_course_progress_bar($html){
923 $disable_option = (bool) get_tutor_option('disable_course_progress_bar');
924 if($disable_option){
925 return '';
926 }
927 return $html;
928 }
929
930 /**
931 * Enable disable material includes
932 * @since v.1.4.8
933 */
934 public function enable_disable_material_includes($html){
935 $disable_option = (bool) get_tutor_option('disable_course_material');
936 if($disable_option){
937 return '';
938 }
939 return $html;
940 }
941
942 /**
943 * Enable disable course content
944 * @since v.1.4.8
945 */
946 public function enable_disable_course_content($html){
947 $disable_option = (bool) get_tutor_option('disable_course_description');
948 if($disable_option){
949 return '';
950 }
951 return $html;
952 }
953
954 /**
955 * Enable disable course benefits
956 * @since v.1.4.8
957 */
958 public function enable_disable_course_benefits($html){
959 $disable_option = (bool) get_tutor_option('disable_course_benefits');
960 if($disable_option){
961 return '';
962 }
963 return $html;
964 }
965
966 /**
967 * Enable disable course requirements
968 * @since v.1.4.8
969 */
970 public function enable_disable_course_requirements($html){
971 $disable_option = (bool) get_tutor_option('disable_course_requirements');
972 if($disable_option){
973 return '';
974 }
975 return $html;
976 }
977
978 /**
979 * Enable disable course target audience
980 * @since v.1.4.8
981 */
982 public function enable_disable_course_target_audience($html){
983 $disable_option = (bool) get_tutor_option('disable_course_target_audience');
984 if($disable_option){
985 return '';
986 }
987 return $html;
988 }
989
990 /**
991 * Enable disable course nav items
992 * @since v.1.4.8
993 */
994 public function enable_disable_course_nav_items($items){
995 global $wp_query, $post;
996 $enable_q_and_a_on_course = (bool) get_tutor_option('enable_q_and_a_on_course');
997 $disable_course_announcements = (bool) get_tutor_option('disable_course_announcements');
998
999 $disable_qa_for_this_course = ($wp_query->is_single && !empty($post)) ? get_post_meta($post->ID, '_tutor_disable_qa', true) : '';
1000
1001 if(!$enable_q_and_a_on_course || $disable_qa_for_this_course == 'yes') {
1002 if(tutils()->array_get('questions', $items)) {
1003 unset($items['questions']);
1004 }
1005 }
1006 if($disable_course_announcements){
1007 if(tutils()->array_get('announcements', $items)) {
1008 unset($items['announcements']);
1009 }
1010 }
1011 return $items;
1012 }
1013
1014 /**
1015 * Filter product in shop page
1016 * @since v.1.4.9
1017 */
1018 public function filter_product_in_shop_page(){
1019 $hide_course_from_shop_page = (bool) get_tutor_option('hide_course_from_shop_page');
1020 if(!$hide_course_from_shop_page){
1021 return;
1022 }
1023 add_action('woocommerce_product_query', array($this, 'filter_woocommerce_product_query'));
1024 add_filter('edd_downloads_query', array($this, 'filter_edd_downloads_query'), 10, 2);
1025 add_action('pre_get_posts', array($this, 'filter_archive_meta_query'), 1);
1026 }
1027
1028 /**
1029 * Tutor product meta query
1030 * @since v.1.4.9
1031 */
1032 public function tutor_product_meta_query(){
1033 $meta_query = array(
1034 'key' => '_tutor_product',
1035 'compare' => 'NOT EXISTS'
1036 );
1037 return $meta_query;
1038 }
1039
1040 /**
1041 * Filter product in woocommerce shop page
1042 * @since v.1.4.9
1043 */
1044 public function filter_woocommerce_product_query($wp_query){
1045 $wp_query->set('meta_query', array($this->tutor_product_meta_query()));
1046 return $wp_query;
1047 }
1048
1049 /**
1050 * Filter product in edd downloads shortcode page
1051 * @since v.1.4.9
1052 */
1053 public function filter_edd_downloads_query($query){
1054 $query['meta_query'][] = $this->tutor_product_meta_query();
1055 return $query;
1056 }
1057
1058 /**
1059 * Filter product in edd downloads archive page
1060 * @since v.1.4.9
1061 */
1062 public function filter_archive_meta_query($wp_query){
1063 if(!is_admin() && $wp_query->is_archive && $wp_query->get('post_type') === 'download'){
1064 $wp_query->set('meta_query', array($this->tutor_product_meta_query()));
1065 }
1066 return $wp_query;
1067 }
1068
1069 /**
1070 * @param $html
1071 * @return string
1072 *
1073 * Removed course price if already enrolled at single course
1074 *
1075 * @since v.1.5.8
1076 */
1077 public function remove_price_if_enrolled($html){
1078 $should_removed = apply_filters('should_remove_price_if_enrolled', true);
1079
1080 if ($should_removed){
1081 $course_id = get_the_ID();
1082 $enrolled = tutils()->is_enrolled($course_id);
1083 if ($enrolled){
1084 $html = '';
1085 }
1086 }
1087 return $html;
1088 }
1089
1090 /**
1091 * @param $html
1092 * @return string
1093 *
1094 * Check if all lessons and quizzes done before mark course complete.
1095 */
1096 function tutor_lms_hide_course_complete_btn($html){
1097
1098 $completion_mode = tutils()->get_option('course_completion_process');
1099 if ($completion_mode !== 'strict'){
1100 return $html;
1101 }
1102
1103 $completed_lesson = tutils()->get_completed_lesson_count_by_course();
1104 $lesson_count = tutils()->get_lesson_count_by_course();
1105
1106 if ($completed_lesson < $lesson_count){
1107 return '<p class="suggestion-before-course-complete">'.__('complete all lessons to mark this course as complete', 'tutor').'</p>';
1108 }
1109
1110 $quizzes = array();
1111
1112 $course_contents = tutils()->get_course_contents_by_id();
1113 if (tutils()->count($course_contents)){
1114 foreach ($course_contents as $content){
1115 if ($content->post_type === 'tutor_quiz'){
1116 $quizzes[] = $content;
1117 }
1118 }
1119 }
1120
1121 $is_pass = true;
1122 $required_quiz_pass = 0;
1123
1124 if (tutils()->count($quizzes)){
1125 foreach ($quizzes as $quiz){
1126
1127 $attempt = tutils()->get_quiz_attempt($quiz->ID);
1128 if ($attempt) {
1129 $passing_grade = tutor_utils()->get_quiz_option($quiz->ID, 'passing_grade', 0);
1130 $earned_percentage = $attempt->earned_marks > 0 ? (number_format(($attempt->earned_marks * 100) / $attempt->total_marks)) : 0;
1131
1132 if ($earned_percentage < $passing_grade) {
1133 $required_quiz_pass++;
1134 $is_pass = false;
1135 }
1136 }else{
1137 $required_quiz_pass++;
1138 $is_pass = false;
1139 }
1140 }
1141 }
1142
1143 if ( ! $is_pass){
1144 return '<p class="suggestion-before-course-complete">'.sprintf(__('You have to pass %s quizzes to complete this course.', 'tutor'), $required_quiz_pass).'</p>';
1145 }
1146
1147 return $html;
1148 }
1149
1150 public function get_generate_greadbook($html){
1151 if ( ! tutils()->is_completed_course()){
1152 return '';
1153 }
1154 return $html;
1155 }
1156
1157 /**
1158 * Add social share content in header
1159 * @since v.1.6.3
1160 */
1161 public function social_share_content(){
1162 global $wp_query, $post;
1163 if ($wp_query->is_single && ! empty($wp_query->query_vars['post_type']) && $wp_query->query_vars['post_type'] === $this->course_post_type) { ?>
1164 <!--Facebook-->
1165 <meta property="og:type" content="website"/>
1166 <meta property="og:image" content="<?php echo get_tutor_course_thumbnail_src(); ?>" />
1167 <meta property="og:description" content="<?php echo esc_html($post->post_content); ?>" />
1168 <!--Twitter-->
1169 <meta name="twitter:image" content="<?php echo get_tutor_course_thumbnail_src(); ?>">
1170 <meta name="twitter:description" content="<?php echo esc_html($post->post_content); ?>">
1171 <!--Google+-->
1172 <meta itemprop="image" content="<?php echo get_tutor_course_thumbnail_src(); ?>">
1173 <meta itemprop="description" content="<?php echo esc_html($post->post_content); ?>"> <?php
1174 }
1175 }
1176
1177 /**
1178 * Get posts by type and parent
1179 * @since v.1.6.6
1180 */
1181 public function tutor_get_post_ids($post_type, $post_parent) {
1182 $args = array(
1183 'fields' => 'ids',
1184 'post_type' => $post_type,
1185 'post_parent' => $post_parent,
1186 'post_status' => 'any',
1187 'posts_per_page' => -1,
1188 );
1189 return get_posts($args);
1190 }
1191
1192 /**
1193 * Delete course data when permanently deleting a course.
1194 * @since v.1.6.6
1195 */
1196 function delete_tutor_course_data( $post_id ) {
1197 $course_post_type = tutor()->course_post_type;
1198 $lesson_post_type = tutor()->lesson_post_type;
1199
1200 if (get_post_type($post_id) == $course_post_type) {
1201 global $wpdb;
1202 $topic_ids = $this->tutor_get_post_ids('topics', $post_id);
1203 if ( !empty($topic_ids) ) {
1204 foreach ($topic_ids as $topic_id) {
1205 $content_post_type = apply_filters('tutor_course_contents_post_types', array($lesson_post_type, 'tutor_quiz'));
1206 $topic_content_ids = $this->tutor_get_post_ids($content_post_type, $topic_id);
1207
1208 foreach ($topic_content_ids as $content_id) {
1209 if( get_post_type($content_id) == 'tutor_quiz') {
1210 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $content_id));
1211 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $content_id));
1212
1213 $questions_ids = $wpdb->get_col("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = {$content_id} ");
1214 if (is_array($questions_ids) && count($questions_ids)){
1215 $in_question_ids = "'".implode("','", $questions_ids)."'";
1216 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
1217 }
1218 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $content_id));
1219 }
1220 wp_delete_post($content_id, true);
1221 }
1222 wp_delete_post($topic_id, true);
1223 }
1224 }
1225 $child_post_ids = $this->tutor_get_post_ids(array('tutor_announcements', 'tutor_enrolled'), $post_id);
1226 if ( !empty($child_post_ids) ) {
1227 foreach ($child_post_ids as $child_post_id) {
1228 wp_delete_post($child_post_id, true);
1229 }
1230 }
1231 }
1232 }
1233
1234 /**
1235 * tutor course setting metabox
1236 * @since v.1.7.0
1237 */
1238 function tutor_course_setting_metabox( $post ) {
1239
1240 $disable_qa = $this->additional_meta[0];
1241 $is_public = $this->additional_meta[1];
1242
1243 $disable_qa_checked = get_post_meta($post->ID, $disable_qa, true)=='yes' ? 'checked="checked"' : '';
1244 $is_public_checked = get_post_meta($post->ID, $is_public, true)=='yes' ? 'checked="checked"' : '';
1245
1246 do_action('tutor_before_course_sidebar_settings_metabox', $post);
1247 ?>
1248 <div class="tutor-course-sidebar-settings-item" id="_tutor_is_course_public_meta_checkbox" style="display:none">
1249 <label for="<?php echo $is_public; ?>">
1250 <input id="<?php echo $is_public; ?>" type="checkbox" name="<?php echo $is_public; ?>" value="yes" <?php echo $is_public_checked; ?> />
1251 <?php _e('Make This Course Public', 'tutor'); ?>
1252 <small style="display:block;padding-left:24px">
1253 <?php _e('No enrollment required.', 'tutor'); ?>
1254 </small>
1255 </label>
1256 </div>
1257 <div class="tutor-course-sidebar-settings-item">
1258 <label for="<?php echo $disable_qa; ?>">
1259 <input type="hidden" name="_tutor_course_additional_data_edit" value="true" />
1260 <input id="<?php echo $disable_qa; ?>" type="checkbox" name="<?php echo $disable_qa; ?>" value="yes" <?php echo $disable_qa_checked; ?> />
1261 <?php _e('Disable Q&A', 'tutor'); ?>
1262 </label>
1263 </div>
1264 <?php
1265 do_action('tutor_after_course_sidebar_settings_metabox', $post);
1266 }
1267
1268 function tutor_course_setting_metabox_frontend( $post ){
1269 ?>
1270 <div class="tutor-course-builder-section tutor-course-builder-info">
1271 <div class="tutor-course-builder-section-title">
1272 <h3><i class="tutor-icon-down"></i><span><?php esc_html_e('Tutor Settings', 'tutor'); ?></span></h3>
1273 </div>
1274 <div class="tutor-course-builder-section-content">
1275 <div class="tutor-frontend-builder-item-scope">
1276 <div class="tutor-form-group">
1277 <?php $this->tutor_course_setting_metabox($post); ?>
1278 </div>
1279 </div>
1280 </div>
1281 </div>
1282 <?php
1283 }
1284 }