PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.9.3
Tutor LMS – eLearning and online course solution v3.9.3
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 / includes / tutor-general-functions.php
tutor / includes Last commit date
droip 7 months ago theme-compatibility 4 years ago country.php 1 year ago ecommerce-functions.php 10 months ago tinymce_translate.php 1 year ago translate-text.php 1 year ago tutor-general-functions.php 7 months ago tutor-template-functions.php 9 months ago tutor-template-hook.php 4 years ago
tutor-general-functions.php
1879 lines
1 <?php
2 /**
3 * Tutor general functions
4 *
5 * @package TutorFunctions
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 1.0.0
9 */
10
11 use Tutor\Cache\FlashMessage;
12 use Tutor\Ecommerce\Ecommerce;
13 use Tutor\Ecommerce\OptionKeys;
14 use Tutor\Ecommerce\Settings;
15 use TUTOR\Input;
16 use Tutor\Models\CourseModel;
17
18 defined( 'ABSPATH' ) || exit;
19
20
21 if ( ! function_exists( 'tutor' ) ) {
22 /**
23 * Tutor helper function to get configuration like version, path etc.
24 *
25 * @since 1.0.0
26 * @since 3.7.0 updated with config class.
27 *
28 * @return object
29 */
30 function tutor() {
31 return \TUTOR\Config::get_instance();
32 }
33 }
34
35 if ( ! function_exists( 'tutor_utils' ) ) {
36 /**
37 * Access tutor utils functions
38 *
39 * @since 1.0.0
40 *
41 * @return \TUTOR\Utils
42 */
43 function tutor_utils() {
44 if ( ! isset( $GLOBALS['tutor_utils_object'] ) ) {
45 // Use runtime cache.
46 $GLOBALS['tutor_utils_object'] = new \TUTOR\Utils();
47 }
48
49 return $GLOBALS['tutor_utils_object'];
50 }
51 }
52
53
54 if ( ! function_exists( 'tutils' ) ) {
55 /**
56 * Alias of tutor_utils()
57 *
58 * @since 1.3.4
59 *
60 * @return \TUTOR\Utils
61 */
62 function tutils() {
63 return tutor_utils();
64 }
65 }
66
67 /**
68 * Tutor input sanitization
69 */
70
71 if ( ! function_exists( 'tutor_sanitize_data' ) ) {
72 /**
73 * Escaping for Sanitize data.
74 *
75 * @since 1.9.13
76 *
77 * @param string $input.
78 * @param string $type.
79 * @return string|array|object
80 */
81 function tutor_sanitize_data( $input = null, $type = null ) {
82 $array = array();
83 $object = new stdClass();
84
85 if ( is_string( $input ) ) {
86
87 if ( 'textarea' == $type ) {
88 $input = sanitize_textarea_field( $input );
89 } elseif ( 'kses' == $type ) {
90 $input = wp_kses_post( $input );
91 } else {
92 $input = sanitize_text_field( $input );
93 }
94
95 return $input;
96
97 } elseif ( is_object( $input ) && count( get_object_vars( $input ) ) ) {
98
99 foreach ( $input as $key => $value ) {
100 if ( is_object( $value ) ) {
101 $object->$key = tutor_sanitize_data( $value );
102 } else {
103 $key = sanitize_text_field( $key );
104 $value = sanitize_text_field( $value );
105 $object->$key = $value;
106 }
107 }
108 return $object;
109 } elseif ( is_array( $input ) && count( $input ) ) {
110 foreach ( $input as $key => $value ) {
111 if ( is_array( $value ) ) {
112 $array[ $key ] = tutor_sanitize_data( $value );
113 } else {
114 $key = sanitize_text_field( $key );
115 $value = sanitize_text_field( $value );
116 $array[ $key ] = $value;
117 }
118 }
119
120 return $array;
121 }
122 }
123 }
124
125 if ( ! function_exists( 'tutor_placeholder_img_src' ) ) {
126 function tutor_placeholder_img_src() {
127 $src = tutor()->url . 'assets/images/placeholder.svg';
128 return apply_filters( 'tutor_placeholder_img_src', $src );
129 }
130 }
131
132 /**
133 * @return string
134 *
135 * Get course categories selecting UI
136 *
137 * @since v.1.3.4
138 */
139
140 if ( ! function_exists( 'tutor_course_categories_dropdown' ) ) {
141 function tutor_course_categories_dropdown( $post_ID = 0, $args = array() ) {
142
143 $default = array(
144 'classes' => '',
145 'name' => 'tax_input[course-category]',
146 'multiple' => true,
147 );
148
149 $args = apply_filters( 'tutor_course_categories_dropdown_args', array_merge( $default, $args ) );
150
151 $multiple_select = '';
152
153 if ( tutor_utils()->array_get( 'multiple', $args ) ) {
154 if ( isset( $args['name'] ) ) {
155 $args['name'] = $args['name'] . '[]';
156 }
157 $multiple_select = "multiple='multiple'";
158 }
159
160 extract( $args );
161
162 $classes = (array) $classes;
163 $classes = implode( ' ', $classes );
164
165 $categories = tutor_utils()->get_course_categories();
166
167 $output = '';
168 $output .= '<select name="' . $name . '" ' . $multiple_select . ' class="' . $classes . '" data-placeholder="' . __( 'Search Course Category. ex. Design, Development, Business', 'tutor' ) . '">';
169 $output .= '<option value="">' . __( 'Select a category', 'tutor' ) . '</option>';
170 $output .= _generate_categories_dropdown_option( $post_ID, $categories, $args );
171 $output .= '</select>';
172
173 return $output;
174 }
175 }
176
177 /**
178 * @return string
179 *
180 * Get course tags selecting UI
181 *
182 * @since v.1.3.4
183 */
184
185 if ( ! function_exists( 'tutor_course_tags_dropdown' ) ) {
186 function tutor_course_tags_dropdown( $post_ID = 0, $args = array() ) {
187
188 $default = array(
189 'classes' => '',
190 'name' => 'tax_input[course-tag]',
191 'multiple' => true,
192 );
193
194 $args = apply_filters( 'tutor_course_tags_dropdown_args', array_merge( $default, $args ) );
195
196 $multiple_select = '';
197
198 if ( tutor_utils()->array_get( 'multiple', $args ) ) {
199 if ( isset( $args['name'] ) ) {
200 $args['name'] = $args['name'] . '[]';
201 }
202 $multiple_select = "multiple='multiple'";
203 }
204
205 extract( $args );
206
207 $classes = (array) $classes;
208 $classes = implode( ' ', $classes );
209
210 $tags = tutor_utils()->get_course_tags();
211
212 $output = '';
213 $output .= '<select name=' . $name . ' ' . $multiple_select . ' class="' . $classes . '" data-placeholder="' . __( 'Search Course Tags. ex. Design, Development, Business', 'tutor' ) . '">';
214 $output .= '<option value="">' . __( 'Select a tag', 'tutor' ) . '</option>';
215 $output .= _generate_tags_dropdown_option( $post_ID, $tags, $args );
216 $output .= '</select>';
217
218 return $output;
219 }
220 }
221
222 /**
223 * @param $categories
224 * @param string $parent_name
225 *
226 * @return string
227 *
228 * Get selecting options, recursive supports
229 *
230 * @since v.1.3.4
231 */
232
233 if ( ! function_exists( '_generate_categories_dropdown_option' ) ) {
234 function _generate_categories_dropdown_option( $post_ID = 0, $categories = array(), $args = array(), $depth = 0 ) {
235 $output = '';
236
237 if ( ! tutor_utils()->count( $categories ) ) {
238 return $output;
239 }
240
241 if ( ! is_numeric( $post_ID ) || $post_ID < 1 ) {
242 return $output;
243 }
244
245 foreach ( $categories as $category_id => $category ) {
246 if ( ! $category->parent ) {
247 $depth = 0;
248 }
249
250 $childrens = tutor_utils()->array_get( 'children', $category );
251 $has_in_term = has_term( $category->term_id, 'course-category', $post_ID );
252
253 $depth_seperator = '';
254 if ( $depth ) {
255 for ( $depth_i = 0; $depth_i < $depth; $depth_i++ ) {
256 $depth_seperator .= '-';
257 }
258 }
259
260 $output .= '<option value="' . $category->term_id . '" ' . selected( $has_in_term, true, false ) . '> ' . $depth_seperator . ' ' . $category->name . '</option>';
261
262 if ( tutor_utils()->count( $childrens ) ) {
263 $depth++;
264 $output .= _generate_categories_dropdown_option( $post_ID, $childrens, $args, $depth );
265 }
266 }
267
268 return $output;
269 }
270 }
271 /**
272 * @param $tags
273 * @param string $parent_name
274 *
275 * @return string
276 *
277 * Get selecting options, recursive supports
278 *
279 * @since v.1.3.4
280 */
281
282 if ( ! function_exists( '_generate_tags_dropdown_option' ) ) {
283 function _generate_tags_dropdown_option( $post_ID = 0, $tags = array(), $args = array(), $depth = 0 ) {
284 $output = '';
285
286 if ( ! tutor_utils()->count( $tags ) ) {
287 return $output;
288 }
289
290 if ( ! is_numeric( $post_ID ) || $post_ID < 1 ) {
291 return $output;
292 }
293
294 foreach ( $tags as $tag ) {
295
296 $has_in_term = has_term( $tag->term_id, CourseModel::COURSE_TAG, $post_ID );
297
298 $output .= '<option value="' . esc_attr( $tag->name ) . '" ' . selected( $has_in_term, true, false ) . '>' . esc_html( $tag->name ) . '</option>';
299
300 }
301
302 return $output;
303 }
304 }
305
306 /**
307 * @param array $args
308 *
309 * @return string
310 *
311 * Generate course categories checkbox
312 * @since v.1.3.4
313 */
314
315 if ( ! function_exists( 'tutor_course_categories_checkbox' ) ) {
316 function tutor_course_categories_checkbox( $post_ID = 0, $args = array() ) {
317 $default = array(
318 'name' => 'tax_input[course-category]',
319 );
320
321 $args = apply_filters( 'tutor_course_categories_checkbox_args', array_merge( $default, $args ) );
322
323 if ( isset( $args['name'] ) ) {
324 $args['name'] = $args['name'] . '[]';
325 }
326
327 extract( $args );
328
329 $categories = tutor_utils()->get_course_categories();
330 $output = '';
331 $output .= __tutor_generate_categories_checkbox( $post_ID, $categories, $args );
332
333 return $output;
334 }
335 }
336
337 /**
338 * @param array $args
339 *
340 * @return string
341 *
342 * Generate course tags checkbox
343 * @since v.1.3.4
344 */
345
346 if ( ! function_exists( 'tutor_course_tags_checkbox' ) ) {
347 function tutor_course_tags_checkbox( $post_ID = 0, $args = array() ) {
348 $default = array(
349 'name' => 'tax_input[course-tag]',
350 );
351
352 $args = apply_filters( 'tutor_course_tags_checkbox_args', array_merge( $default, $args ) );
353
354 if ( isset( $args['name'] ) ) {
355 $args['name'] = $args['name'] . '[]';
356 }
357
358 extract( $args );
359
360 $tags = tutor_utils()->get_course_tags();
361 $output = '';
362 $output .= __tutor_generate_tags_checkbox( $post_ID, $tags, $args );
363
364 return $output;
365 }
366 }
367
368 /**
369 * @param $categories
370 * @param string $parent_name
371 * @param array $args
372 *
373 * @return string
374 *
375 * Internal function to generate course categories checkbox
376 *
377 * @since v.1.3.4
378 */
379 if ( ! function_exists( '__tutor_generate_categories_checkbox' ) ) {
380 function __tutor_generate_categories_checkbox( $post_ID = 0, $categories = array(), $args = array() ) {
381
382 $output = '';
383 $input_name = tutor_utils()->array_get( 'name', $args );
384
385 if ( tutor_utils()->count( $categories ) ) {
386 $output .= '<ul class="tax-input-course-category">';
387 foreach ( $categories as $category_id => $category ) {
388 $childrens = tutor_utils()->array_get( 'children', $category );
389 $has_in_term = has_term( $category->term_id, 'course-category', $post_ID );
390
391 $output .= '<li class="tax-input-course-category-item tax-input-course-category-item-' . $category->term_id . '"><label class="course-category-checkbox"> <input type="checkbox" name="' . $input_name . '" value="' . $category->term_id . '" ' . checked( $has_in_term, true, false ) . '/> <span>' . $category->name . '</span> </label>';
392
393 if ( tutor_utils()->count( $childrens ) ) {
394 $output .= __tutor_generate_categories_checkbox( $post_ID, $childrens, $args );
395 }
396 $output .= ' </li>';
397 }
398 $output .= '</ul>';
399 }
400
401 return $output;
402
403 }
404 }
405 /**
406 * @param $tags
407 * @param string $parent_name
408 * @param array $args
409 *
410 * @return string
411 *
412 * Internal function to generate course tags checkbox
413 *
414 * @since v.1.3.4
415 */
416 if ( ! function_exists( '__tutor_generate_tags_checkbox' ) ) {
417 function __tutor_generate_tags_checkbox( $post_ID = 0, $tags = array(), $args = array() ) {
418
419 $output = '';
420 $input_name = tutor_utils()->array_get( 'name', $args );
421
422 if ( tutor_utils()->count( $tags ) ) {
423 $output .= '<ul class="tax-input-course-tag">';
424 foreach ( $tags as $tag ) {
425 $has_in_term = has_term( $tag->term_id, CourseModel::COURSE_TAG, $post_ID );
426
427 $output .= '<li class="tax-input-course-tag-item tax-input-course-tag-item-' . $tag->term_id . '"><label class="course-tag-checkbox"> <input type="checkbox" name="' . $input_name . '" value="' . $tag->term_id . '" ' . checked( $has_in_term, true, false ) . ' /> <span>' . $tag->name . '</span> </label>';
428
429 $output .= ' </li>';
430 }
431 $output .= '</ul>';
432 }
433
434 return $output;
435 }
436 }
437
438 /**
439 * @param string $content
440 * @param string $title
441 *
442 * @return string
443 *
444 * Wrap course builder sections within div for frontend
445 *
446 * @since v.1.3.4
447 */
448
449 if ( ! function_exists( 'course_builder_section_wrap' ) ) {
450 function course_builder_section_wrap( $content = '', $title = '', $echo = true ) {
451 $template = trailingslashit( tutor()->path . 'templates' ) . 'metabox-wrapper.php';
452 if ( $echo ) {
453 if ( file_exists( $template ) ) {
454 include $template;
455 } else {
456 echo esc_html( $template ) . esc_html__( 'file not exists', 'tutor' );
457 }
458 } else {
459 ob_start();
460 if ( file_exists( $template ) ) {
461 include $template;
462 } else {
463 echo esc_html( $template ) . esc_html__( 'file not exists', 'tutor' );
464 }
465 $html = ob_get_clean();
466 return $html;
467 }
468 }
469 }
470
471
472 if ( ! function_exists( 'get_tutor_header' ) ) {
473 function get_tutor_header( $fullScreen = false ) {
474 $enable_spotlight_mode = tutor_utils()->get_option( 'enable_spotlight_mode' );
475
476 if ( $enable_spotlight_mode || $fullScreen ) {
477 ?>
478 <!doctype html>
479 <html <?php language_attributes(); ?>>
480
481 <head>
482 <meta charset="<?php bloginfo( 'charset' ); ?>" />
483 <meta name="viewport" content="width=device-width, initial-scale=1" />
484 <link rel="profile" href="https://gmpg.org/xfn/11" />
485 <?php wp_head(); ?>
486 </head>
487
488 <body <?php body_class(); ?>>
489 <div id="tutor-page-wrap" class="tutor-site-wrap site">
490 <?php
491 } else {
492 tutor_utils()->tutor_custom_header();
493 }
494 }
495 }
496
497 if ( ! function_exists( 'get_tutor_footer' ) ) {
498 function get_tutor_footer( $fullScreen = false ) {
499 $enable_spotlight_mode = tutor_utils()->get_option( 'enable_spotlight_mode' );
500 if ( $enable_spotlight_mode || $fullScreen ) {
501 ?>
502 </div>
503 <?php wp_footer(); ?>
504
505 </body>
506
507 </html>
508 <?php
509 } else {
510 tutor_utils()->tutor_custom_footer();
511 }
512 }
513 }
514
515 /**
516 * @param null $key
517 * @param bool $default
518 *
519 * @return array|bool|mixed
520 *
521 * Get tutor option by this helper function
522 *
523 * @since v.1.3.6
524 */
525 if ( ! function_exists( 'get_tutor_option' ) ) {
526 function get_tutor_option( $key = null, $default = false ) {
527 return tutor_utils()->get_option( $key, $default );
528 }
529 }
530
531 /**
532 * @param null $key
533 * @param bool $value
534 *
535 * Update tutor option by this helper function
536 *
537 * @since v.1.3.6
538 */
539 if ( ! function_exists( 'update_tutor_option' ) ) {
540 function update_tutor_option( $key = null, $value = false ) {
541 tutor_utils()->update_option( $key, $value );
542 }
543 }
544 /**
545 * @param int $course_id
546 * @param null $key
547 * @param bool $default
548 *
549 * @return array|bool|mixed
550 *
551 * Get tutor course settings by course ID
552 *
553 * @since v.1.4.1
554 */
555 if ( ! function_exists( 'get_tutor_course_settings' ) ) {
556 function get_tutor_course_settings( $course_id = 0, $key = null, $default = false ) {
557 return tutor_utils()->get_course_settings( $course_id, $key, $default );
558 }
559 }
560
561 /**
562 * @param int $lesson_id
563 * @param null $key
564 * @param bool $default
565 *
566 * @return array|bool|mixed
567 *
568 * Get lesson content drip settings
569 */
570
571 if ( ! function_exists( 'get_item_content_drip_settings' ) ) {
572 function get_item_content_drip_settings( $lesson_id = 0, $key = null, $default = false ) {
573 return tutor_utils()->get_item_content_drip_settings( $lesson_id, $key, $default );
574 }
575 }
576
577 /**
578 * @param null $msg
579 * @param string $type
580 * @param bool $echo
581 *
582 * @return string
583 *
584 * Print Alert by tutor_alert()
585 *
586 * @since v.1.4.1
587 */
588 if ( ! function_exists( 'tutor_alert' ) ) {
589 function tutor_alert( $msg = null, $type = 'warning', $echo = true ) {
590 if ( ! $msg ) {
591
592 if ( $type === 'any' ) {
593 if ( ! $msg ) {
594 $type = 'warning';
595 $msg = tutor_flash_get( $type );
596 }
597 if ( ! $msg ) {
598 $type = 'danger';
599 $msg = tutor_flash_get( $type );
600 }
601 if ( ! $msg ) {
602 $type = 'success';
603 $msg = tutor_flash_get( $type );
604 }
605 } else {
606 $msg = tutor_flash_get( $type );
607 }
608 }
609 if ( ! $msg ) {
610 return $msg;
611 }
612
613 $html = '<div class="tutor-alert tutor-' . esc_attr( $type ) . '">
614 <div class="tutor-alert-text">
615 <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span>
616 <span>' . wp_kses( $msg, array( 'div', 'span' ) ) . '</span>
617 </div>
618 </div>';
619 if ( $echo ) {
620 echo tutor_kses_html( $html ); //phpcs:ignore
621 }
622 return $html;
623 }
624 }
625
626
627 /**
628 * @param bool $echo
629 *
630 * Simply call tutor_nonce_field() to generate nonce field
631 *
632 * @since v.1.4.2
633 */
634
635 if ( ! function_exists( 'tutor_nonce_field' ) ) {
636 function tutor_nonce_field( $echo = true ) {
637 wp_nonce_field( tutor()->nonce_action, tutor()->nonce, $echo );
638 }
639 }
640
641 /**
642 * @param null $key
643 * @param string $message
644 *
645 * Set Flash Message
646 */
647
648 if ( ! function_exists( 'tutor_flash_set' ) ) {
649 function tutor_flash_set( $key = null, $message = '' ) {
650 if ( ! $key ) {
651 return;
652 }
653 // ensure session is started
654 if ( session_status() !== PHP_SESSION_ACTIVE ) {
655 session_start();
656 }
657 $_SESSION[ $key ] = $message;
658 }
659 }
660
661 /**
662 * @param null $key
663 *
664 * @return array|bool|mixed|null
665 *
666 * @since v.1.4.2
667 *
668 * Get flash message
669 */
670
671 if ( ! function_exists( 'tutor_flash_get' ) ) {
672 function tutor_flash_get( $key = null ) {
673 if ( $key ) {
674 // ensure session is started
675 if ( session_status() !== PHP_SESSION_ACTIVE ) {
676 @session_start();
677 }
678 if ( empty( $_SESSION ) ) {
679 return null;
680 }
681 $message = tutor_utils()->array_get( $key, $_SESSION );
682 if ( $message ) {
683 unset( $_SESSION[ $key ] );
684 }
685 return $message;
686 }
687 return $key;
688 }
689 }
690
691 if ( ! function_exists( 'tutor_redirect_back' ) ) {
692 /**
693 * @param null $url
694 *
695 * Redirect to back or a specific URL and terminate
696 *
697 * @since v.1.4.3
698 */
699 function tutor_redirect_back( $url = null ) {
700 if ( ! $url ) {
701 $url = tutor_utils()->referer();
702 }
703 wp_safe_redirect( $url );
704 exit();
705 }
706 }
707
708 /**
709 * @param string $action
710 * @param bool $echo
711 *
712 * @return string
713 *
714 * @since v.1.4.3
715 */
716
717 if ( ! function_exists( 'tutor_action_field' ) ) {
718 function tutor_action_field( $action = '', $echo = true ) {
719 $output = '';
720 if ( $action ) {
721 $output = '<input type="hidden" name="tutor_action" value="' . esc_attr( $action ) . '">';
722 }
723
724 if ( $echo ) {
725 echo wp_kses(
726 $output,
727 array(
728 'input' => array(
729 'type' => true,
730 'name' => true,
731 'value' => true,
732 ),
733 )
734 );
735 } else {
736 return $output;
737 }
738 }
739 }
740
741
742 if ( ! function_exists( 'tutor_time' ) ) {
743 /**
744 * Return current Time from WordPress time
745 *
746 * @return int|string
747 * @since v.1.4.3
748 */
749 function tutor_time() {
750 $gmt_offset = get_option( 'gmt_offset' );
751 return time() + ( $gmt_offset * HOUR_IN_SECONDS );
752 }
753 }
754
755 /**
756 * Toggle maintenance mode for the site.
757 *
758 * Creates/deletes the maintenance file to enable/disable maintenance mode.
759 *
760 * @since v.1.4.6
761 *
762 * @global WP_Filesystem_Base $wp_filesystem Subclass
763 *
764 * @param bool $enable True to enable maintenance mode, false to disable.
765 */
766 if ( ! function_exists( 'tutor_maintenance_mode' ) ) {
767 function tutor_maintenance_mode( $enable = false ) {
768 $file = ABSPATH . '.tutor_maintenance';
769 if ( $enable ) {
770 // Create maintenance file to signal that we are upgrading
771 $maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
772
773 if ( ! file_exists( $file ) ) {
774 file_put_contents( $file, $maintenance_string );
775 }
776 } else {
777 if ( file_exists( $file ) ) {
778 unlink( $file );
779 }
780 }
781 }
782 }
783
784 /**
785 * @return bool
786 *
787 * Check if the current page is course single page
788 *
789 * @since v.1.6.0
790 */
791
792 if ( ! function_exists( 'is_single_course' ) ) {
793 function is_single_course( $check_spotlight = false ) {
794 global $wp_query;
795 $course_post_type = tutor()->course_post_type;
796
797 $post_types = array( $course_post_type );
798 if ( $check_spotlight ) {
799 $post_types = array_merge(
800 $post_types,
801 array(
802 'lesson',
803 'tutor_quiz',
804 'tutor_assignments',
805 'tutor_zoom_meeting',
806 )
807 );
808 }
809
810 if ( is_single() && ! empty( $wp_query->query['post_type'] ) && in_array( $wp_query->query['post_type'], $post_types ) ) {
811 return true;
812 }
813 return false;
814 }
815 }
816
817 /**
818 * Require wp_date form return js date format.
819 * this is helpful for date picker
820 *
821 * @return string
822 *
823 * @since 1.9.7
824 */
825 if ( ! function_exists( 'tutor_js_date_format_against_wp' ) ) {
826 function tutor_js_date_format_against_wp() {
827 $wp_date_format = get_option( 'date_format' );
828 $default_format = 'Y-M-d';
829
830 $formats = array(
831 'Y-m-d' => 'Y-M-d',
832 'm/d/Y' => 'M-d-Y',
833 'd/m/Y' => 'd-M-Y',
834 'F j, Y' => 'MMMM d, yyyy',
835 'j F Y' => 'MMMM d, yyyy',
836 );
837 return isset( $formats[ $wp_date_format ] ) ? $formats[ $wp_date_format ] : $default_format;
838 }
839 }
840
841 if ( ! function_exists( 'tutor_get_formated_date' ) ) {
842 /**
843 * Convert date to desire format
844 *
845 * NOTE: mysql query use formated date from here
846 * that's why date_i18n need to be ignore
847 *
848 * @param string $require_format string If empty Y-m-d is used.
849 * @param string $user_date string Date.
850 *
851 * @return string ( date )
852 */
853 function tutor_get_formated_date( string $require_format = '', string $user_date = '' ) {
854 $require_format = $require_format ?: 'Y-m-d';
855
856 $date = date_create( str_replace( '/', '-', $user_date ) );
857 if ( is_a( $date, 'DateTime' ) ) {
858 $formatted_date = date_format( $date, $require_format );
859 } else {
860 $formatted_date = gmdate( $require_format, strtotime( $user_date ) );
861 }
862 return $formatted_date;
863 }
864 }
865
866 /**
867 * Get translated date
868 *
869 * @since v2.0.2
870 *
871 * @param string $date date in string from to translate & format.
872 * @param string $format optional date format, default is wp date time format.
873 *
874 * @return string translated date
875 */
876 if ( ! function_exists( 'tutor_i18n_get_formated_date' ) ) {
877 function tutor_i18n_get_formated_date( string $date, string $format = '' ) {
878 if ( '' === $format ) {
879 $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
880 }
881 return date_i18n( $format, strtotime( $date ) );
882 }
883 }
884
885 if ( ! function_exists( '_tutor_search_by_title_only' ) ) {
886 /**
887 * Search SQL filter for matching against post title only.
888 *
889 * @link http://wordpress.stackexchange.com/a/11826/1685
890 *
891 * @param string $search
892 * @param WP_Query $wp_query
893 */
894 function _tutor_search_by_title_only( $search, $wp_query ) {
895 if ( ! empty( $search ) && ! empty( $wp_query->query_vars['search_terms'] ) ) {
896 global $wpdb;
897
898 $q = $wp_query->query_vars;
899 $n = ! empty( $q['exact'] ) ? '' : '%';
900
901 $search = array();
902
903 foreach ( (array) $q['search_terms'] as $term ) {
904 $search[] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $n . $wpdb->esc_like( $term ) . $n );
905 }
906
907 if ( ! is_user_logged_in() ) {
908 $search[] = "$wpdb->posts.post_password = ''";
909 }
910
911 $search = ' AND ' . implode( ' AND ', $search );
912 }
913
914 return $search;
915 }
916 }
917
918 if ( ! function_exists( 'get_request' ) ) {
919 /**
920 * Function to get_request
921 *
922 * @param array $var .
923 * @return array
924 */
925 function get_request( $var ) {
926 return isset( $_REQUEST[ $var ] ) ? sanitize_text_field( $_REQUEST[ $var ] ) : false;
927
928 }
929 }
930
931 if ( ! function_exists( 'tutor_kses_allowed_html' ) ) {
932 function tutor_kses_allowed_html( $allowed_tags, $context ) {
933 $tags = array( 'input', 'style', 'script', 'select', 'form', 'option', 'optgroup', 'iframe', 'bdi', 'source', 'a' );
934 $atts = array( 'min', 'max', 'maxlength', 'type', 'method', 'enctype', 'action', 'selected', 'class', 'id', 'disabled', 'checked', 'readonly', 'name', 'aria-*', 'style', 'role', 'placeholder', 'value', 'data-*', 'src', 'width', 'height', 'frameborder', 'allow', 'fullscreen', 'title', 'multiple', 'tutor-hide-course-single-sidebar', 'href' );
935
936 foreach ( $tags as $tag ) {
937 $tag_attrs = array();
938
939 foreach ( $atts as $att ) {
940 $tag_attrs[ $att ] = true;
941 }
942
943 $allowed_tags[ $tag ] = $tag_attrs;
944 }
945
946 return $allowed_tags;
947 }
948 }
949
950 if ( ! function_exists( 'tutor_kses_allowed_css' ) ) {
951 function tutor_kses_allowed_css( $styles ) {
952 $styles[] = 'display';
953 $styles[] = '--progress-value';
954 return $styles;
955 }
956 }
957
958 if ( ! function_exists( 'tutor_kses_html' ) ) {
959 function tutor_kses_html( $content ) {
960
961 return $content;
962 add_filter( 'wp_kses_allowed_html', 'tutor_kses_allowed_html', 10, 2 );
963 add_filter( 'safe_style_css', 'tutor_kses_allowed_css' );
964
965 $content = preg_replace( '/<!--(.|\s)*?-->/', '', $content );
966 $content = wp_kses_post( $content );
967 $content = str_replace( '&amp;', '&', $content );
968
969 remove_filter( 'safe_style_css', 'tutor_kses_allowed_css' );
970 remove_filter( 'wp_kses_allowed_html', 'tutor_kses_allowed_html' );
971
972 return $content;
973 }
974 }
975
976 /**
977 * @return array
978 *
979 * Get all Withdraw Methods available on this system
980 *
981 * @since v.1.5.7
982 */
983 if ( ! function_exists( 'get_tutor_all_withdrawal_methods' ) ) {
984 function get_tutor_all_withdrawal_methods() {
985 return apply_filters( 'tutor_withdrawal_methods_all', array() );
986 }
987 }
988
989
990 if ( ! function_exists( 'tutor_log' ) ) {
991 /**
992 * Logging data.
993 *
994 * @since 1.0.0
995 * @since 3.0.0 exception logging support added.
996 *
997 * @return void
998 */
999 function tutor_log() {
1000 $arg_list = func_get_args();
1001
1002 foreach ( $arg_list as $data ) {
1003 ob_start();
1004
1005 if ( $data instanceof Exception ) {
1006 var_dump( $data->getMessage() );
1007 var_dump( $data->getTraceAsString() );
1008 } else {
1009 var_dump( $data );
1010 }
1011
1012 error_log( ob_get_clean() );
1013 }
1014 }
1015 }
1016
1017 if ( ! function_exists( 'tutor_wc_price_currency_format' ) ) {
1018 function tutor_wc_price_currency_format( $amount ) {
1019
1020 $symbol = get_woocommerce_currency_symbol();
1021 $position = get_option( 'woocommerce_currency_pos', 'left' );
1022
1023 switch ( $position ) {
1024 case 'left':
1025 $amount = $symbol . $amount;
1026 break;
1027 case 'left_space':
1028 $amount = $symbol . ' ' . $amount;
1029 break;
1030
1031 case 'right':
1032 $amount = $amount . $symbol;
1033 break;
1034 case 'right_space':
1035 $amount = $amount . ' ' . $symbol;
1036 break;
1037
1038 default:
1039 $amount = $symbol . $amount;
1040 break;
1041 }
1042
1043 return $amount;
1044 }
1045 }
1046
1047 if ( ! function_exists( 'tutor_meta_box_wrapper' ) ) {
1048 /**
1049 * Tutor meta box wrapper
1050 *
1051 * @since v2.0.2
1052 *
1053 * @param string $id id of meta box.
1054 * @param string $title meta box title.
1055 * @param mixed $callback callback function that meta box will call.
1056 * @param string $screen which screen meta box should appear.
1057 * @param string $context optional param. Where meta box should appear.
1058 * @param string $priority optional.
1059 * @param string $custom_class optional. If provide it will add this class along
1060 * with div id.
1061 *
1062 * @return void if class provided then filter hook will return class.
1063 */
1064 function tutor_meta_box_wrapper(
1065 $id,
1066 $title,
1067 $callback,
1068 $screen,
1069 $context = 'advanced',
1070 $priority = 'default',
1071 $custom_class = ''
1072 ) {
1073 add_meta_box(
1074 $id,
1075 $title,
1076 $callback,
1077 $screen,
1078 $context,
1079 $priority
1080 );
1081 if ( '' !== $custom_class ) {
1082 $post_type = tutor()->course_post_type;
1083 add_filter(
1084 "postbox_classes_{$post_type}_{$id}",
1085 function( $classes ) use ( $custom_class ) {
1086 if ( ! in_array( $custom_class, $classes ) ) {
1087 $classes[] = $custom_class;
1088 }
1089 return $classes;
1090 }
1091 );
1092 }
1093 }
1094 }
1095
1096 if ( ! function_exists( 'tutor_closeable_alert_msg' ) ) {
1097 /**
1098 * Create a close-able alert message
1099 *
1100 * @since 2.1.9
1101 * @since 3.7.1 param css_class added to pass any custom css class.
1102 *
1103 * @param string $message alert message.
1104 * @param string $alert alert key like: success, warning, danger, etc.
1105 * @param array $allowed_tags allowed tags to use with WP_KSES.
1106 * @param string $css_class custom css class.
1107 *
1108 * @return void
1109 */
1110 function tutor_closeable_alert_msg( string $message, string $alert = 'success', $allowed_tags = array(), $css_class = '' ) {
1111 ?>
1112 <div class="tutor-alert tutor-<?php echo esc_attr( $alert ); ?> <?php echo esc_attr( $css_class ); ?> tutor-mb-12 tutor-alert tutor-success tutor-mb-12 tutor-d-flex tutor-align-center tutor-justify-between">
1113 <span>
1114 <?php echo is_array( $allowed_tags ) && count( $allowed_tags ) ? wp_kses( $message, $allowed_tags ) : esc_html( $message ); ?>
1115 </span>
1116 <span class="tutor-icon-times" area-hidden="true" onclick="this.closest('div').remove()" style="cursor: pointer;"></span>
1117 </div>
1118 <?php
1119 }
1120 }
1121
1122 if ( ! function_exists( 'tutor_set_flash_message' ) ) {
1123 /**
1124 * Utility API Set flash message to show somewhere
1125 *
1126 * It will call set_cache method of FlashMessage class to set cache
1127 *
1128 * @param mixed $message message to show.
1129 * @param string $alert alert type as FlashMessage::$alert_types.
1130 *
1131 * @return void
1132 */
1133 function tutor_set_flash_message( $message = '', $alert = 'success' ) {
1134 $flash_msg = new FlashMessage( $message, $alert );
1135 $flash_msg->set_cache();
1136 }
1137 }
1138
1139
1140 if ( ! function_exists( 'tutor_snackbar' ) ) {
1141 /**
1142 * Reuseable snackbar to show on the frontend
1143 *
1144 * Create a snackbar based on title, action buttons
1145 *
1146 * @since 2.2.0
1147 *
1148 * @param string $title title to show.
1149 * @param array $action_buttons 2 dimensional array of action buttons to show.
1150 * Supported attrs: [ [title => title, id => '', class => '' url => '', target => ''] ].
1151 * @param string $title_icon_class title icon to show before title.
1152 *
1153 * @return void
1154 */
1155 function tutor_snackbar( string $title, array $action_buttons = array(), $title_icon_class = '' ) {
1156 ?>
1157 <div id="tutor-reuseable-snackbar" class="tutor-snackbar-wrapper">
1158 <div class="tutor-snackbar">
1159 <p>
1160 <?php if ( ! empty( $title_icon_class ) ) : ?>
1161 <i class="tutor-snackbar-title-icon <?php echo esc_attr( $title_icon_class ); ?>"></i>
1162 <?php endif; ?>
1163 <?php echo esc_html( $title ); ?>
1164 </p>
1165 <div>
1166 <?php foreach ( $action_buttons as $attr => $button ) : ?>
1167 <a
1168 <?php foreach ( $button as $attr => $value ) : ?>
1169 <?php if ( ! empty( $value ) ) : ?>
1170 <?php echo esc_attr( $attr ) . '="' . esc_attr( $value ) . '" '; ?>
1171 <?php endif; ?>
1172 <?php endforeach; ?>
1173 >
1174 <?php echo esc_html( isset( $button['title'] ) ? $button['title'] : '' ); ?>
1175 </a>
1176 <?php endforeach; ?>
1177 <span class="tutor-icon-times" area-hidden="true" onclick="this.closest('#tutor-reuseable-snackbar').remove()" style="cursor: pointer;"></span>
1178 </div>
1179 </div>
1180 </div>
1181 <?php
1182 }
1183 }
1184
1185 if ( ! function_exists( 'tutor_is_rest' ) ) {
1186 /**
1187 * Checks if the current request is a WP REST API request.
1188 *
1189 * @since 2.6.0
1190 *
1191 * Case #1: After WP_REST_Request initialisation
1192 * Case #2: Support "plain" permalink settings and check if `rest_route` starts with `/`
1193 * Case #3: It can happen that WP_Rewrite is not yet initialized,
1194 * so do this (wp-settings.php)
1195 * Case #4: URL Path begins with wp-json/ (your REST prefix)
1196 * Also supports WP installations in subfolders
1197 *
1198 * @see https://wordpress.stackexchange.com/questions/221202/does-something-like-is-rest-exist
1199 * @returns boolean
1200 */
1201 function tutor_is_rest() {
1202 $rest_route = Input::get( 'rest_route' );
1203 if ( defined( 'REST_REQUEST' ) && REST_REQUEST || $rest_route && strpos( $rest_route, '/', 0 ) === 0 ) {
1204 return true;
1205 }
1206
1207 // (#3)
1208 global $wp_rewrite;
1209 if ( null === $wp_rewrite ) {
1210 $wp_rewrite = new WP_Rewrite();
1211 }
1212
1213 // (#4)
1214 $rest_url = wp_parse_url( trailingslashit( rest_url() ) );
1215 $current_url = wp_parse_url( add_query_arg( array() ) );
1216 return strpos( $current_url['path'] ?? '/', $rest_url['path'], 0 ) === 0;
1217 }
1218 }
1219
1220 if ( ! function_exists( 'tutor_getallheaders' ) ) {
1221 /**
1222 * Wrapper of PHP getallheaders with a fallback if getallheaders not available
1223 *
1224 * @since 2.6.0
1225 *
1226 * @see https://www.php.net/manual/en/function.getallheaders.php
1227 *
1228 * @return array of headers
1229 */
1230 function tutor_getallheaders() {
1231 $headers = array();
1232 if ( function_exists( 'getallheaders' ) ) {
1233 $headers = getallheaders();
1234 }
1235
1236 if ( ! $headers ) {
1237 foreach ( $_SERVER as $name => $value ) {
1238 if ( substr( $name, 0, 5 ) == 'HTTP_' ) {
1239 $headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value;
1240 }
1241 }
1242 }
1243
1244 return $headers;
1245 }
1246 }
1247
1248 if ( ! function_exists( 'tutor_entry_box_buttons' ) ) {
1249 /**
1250 * Tutor conditional buttons for the enrollment box
1251 *
1252 * @since 2.6.0
1253 *
1254 * @param int $course_id course id.
1255 * @param int $user_id user id.
1256 *
1257 * @return object
1258 */
1259 function tutor_entry_box_buttons( int $course_id = 0, int $user_id = 0 ) {
1260 $conditional_buttons = (object) array(
1261 'show_enroll_btn' => false,
1262 'show_add_to_cart_btn' => false,
1263 'show_view_cart_btn' => false,
1264 'show_start_learning_btn' => false,
1265 'show_continue_learning_btn' => false,
1266 'show_complete_course_btn' => false,
1267 'show_retake_course_btn' => false,
1268 'show_certificate_view_btn' => false,
1269 'show_course_fully_booked_btn' => false,
1270 );
1271
1272 $course_id = tutor_utils()->get_post_id( $course_id );
1273 $user_id = tutor_utils()->get_user_id( $user_id );
1274
1275 $has_course_access = tutor_utils()->has_user_course_content_access( $user_id, $course_id );
1276
1277 $is_public_course = get_post_meta( $course_id, '_tutor_is_public_course', true );
1278
1279 $is_enabled_retake = tutor_utils()->get_option( 'course_retake_feature' );
1280
1281 $is_enrolled = tutor_utils()->is_enrolled( $course_id, $user_id );
1282
1283 if ( 'yes' === $is_public_course ) {
1284 $conditional_buttons->show_start_learning_btn = true;
1285 } else {
1286 // Admin & instructor can manage posts.
1287 if ( $is_enrolled || $has_course_access ) {
1288 $can_complete_course = CourseModel::can_complete_course( $course_id, $user_id );
1289 $is_completed_course = tutor_utils()->is_completed_course( $course_id, $user_id );
1290 $course_progress = (int) tutor_utils()->get_course_completed_percent( $course_id, $user_id );
1291
1292 if ( $course_progress > 0 && $course_progress < 100 ) {
1293 $conditional_buttons->show_continue_learning_btn = true;
1294 }
1295
1296 if ( 0 === $course_progress ) {
1297 $conditional_buttons->show_start_learning_btn = true;
1298 }
1299
1300 if ( $can_complete_course ) {
1301 $conditional_buttons->show_complete_course_btn = true;
1302 }
1303
1304 if ( $is_completed_course ) {
1305 $conditional_buttons->show_certificate_view_btn = true;
1306 }
1307
1308 if ( $is_enabled_retake && $is_completed_course ) {
1309 $conditional_buttons->show_retake_course_btn = true;
1310 }
1311 } else {
1312 $is_paid_course = tutor_utils()->is_course_purchasable( $course_id );
1313 if ( $is_paid_course ) {
1314 if ( tutor_is_item_in_cart( $course_id ) ) {
1315 $conditional_buttons->show_view_cart_btn = true;
1316 } else {
1317 $conditional_buttons->show_add_to_cart_btn = true;
1318 }
1319 } else {
1320 $conditional_buttons->show_enroll_btn = true;
1321 }
1322 }
1323 }
1324
1325 if ( ! $is_public_course && ! ( $is_enrolled || $has_course_access ) ) {
1326 $is_fully_booked = tutor_utils()->is_course_fully_booked( $course_id );
1327 if ( $is_fully_booked ) {
1328 $conditional_buttons->show_course_fully_booked_btn = true;
1329 }
1330 }
1331
1332 return apply_filters( 'tutor_enrollment_buttons', $conditional_buttons, $course_id, $user_id );
1333 }
1334 }
1335
1336 if ( ! function_exists( 'tutor_global_timezone_lists' ) ) {
1337 /**
1338 * Get list of global timezones
1339 *
1340 * @return array
1341 */
1342 function tutor_global_timezone_lists() {
1343 return array(
1344 'Pacific/Midway' => '(GMT-11:00) Midway Island, Samoa ',
1345 'Pacific/Pago_Pago' => '(GMT-11:00) Pago Pago ',
1346 'Pacific/Honolulu' => '(GMT-10:00) Hawaii ',
1347 'America/Anchorage' => '(GMT-8:00) Alaska ',
1348 'America/Vancouver' => '(GMT-7:00) Vancouver ',
1349 'America/Los_Angeles' => '(GMT-7:00) Pacific Time (US and Canada) ',
1350 'America/Tijuana' => '(GMT-7:00) Tijuana ',
1351 'America/Phoenix' => '(GMT-7:00) Arizona ',
1352 'America/Edmonton' => '(GMT-6:00) Edmonton ',
1353 'America/Denver' => '(GMT-6:00) Mountain Time (US and Canada) ',
1354 'America/Mazatlan' => '(GMT-6:00) Mazatlan ',
1355 'America/Regina' => '(GMT-6:00) Saskatchewan ',
1356 'America/Guatemala' => '(GMT-6:00) Guatemala ',
1357 'America/El_Salvador' => '(GMT-6:00) El Salvador ',
1358 'America/Managua' => '(GMT-6:00) Managua ',
1359 'America/Costa_Rica' => '(GMT-6:00) Costa Rica ',
1360 'America/Tegucigalpa' => '(GMT-6:00) Tegucigalpa ',
1361 'America/Winnipeg' => '(GMT-5:00) Winnipeg ',
1362 'America/Chicago' => '(GMT-5:00) Central Time (US and Canada) ',
1363 'America/Mexico_City' => '(GMT-5:00) Mexico City ',
1364 'America/Panama' => '(GMT-5:00) Panama ',
1365 'America/Bogota' => '(GMT-5:00) Bogota ',
1366 'America/Lima' => '(GMT-5:00) Lima ',
1367 'America/Caracas' => '(GMT-4:30) Caracas ',
1368 'America/Montreal' => '(GMT-4:00) Montreal ',
1369 'America/New_York' => '(GMT-4:00) Eastern Time (US and Canada) ',
1370 'America/Indianapolis' => '(GMT-4:00) Indiana (East) ',
1371 'America/Puerto_Rico' => '(GMT-4:00) Puerto Rico ',
1372 'America/Santiago' => '(GMT-4:00) Santiago ',
1373 'America/Halifax' => '(GMT-3:00) Halifax ',
1374 'America/Montevideo' => '(GMT-3:00) Montevideo ',
1375 'America/Araguaina' => '(GMT-3:00) Brasilia ',
1376 'America/Argentina/Buenos_Aires' => '(GMT-3:00) Buenos Aires, Georgetown ',
1377 'America/Sao_Paulo' => '(GMT-3:00) Sao Paulo ',
1378 'Canada/Atlantic' => '(GMT-3:00) Atlantic Time (Canada) ',
1379 'America/St_Johns' => '(GMT-2:30) Newfoundland and Labrador ',
1380 'America/Godthab' => '(GMT-2:00) Greenland ',
1381 'Atlantic/Cape_Verde' => '(GMT-1:00) Cape Verde Islands ',
1382 'Atlantic/Azores' => '(GMT+0:00) Azores ',
1383 'UTC' => '(GMT+0:00) Universal Time UTC ',
1384 'Etc/Greenwich' => '(GMT+0:00) Greenwich Mean Time ',
1385 'Atlantic/Reykjavik' => '(GMT+0:00) Reykjavik ',
1386 'Africa/Nouakchott' => '(GMT+0:00) Nouakchott ',
1387 'Europe/Dublin' => '(GMT+1:00) Dublin ',
1388 'Europe/London' => '(GMT+1:00) London ',
1389 'Europe/Lisbon' => '(GMT+1:00) Lisbon ',
1390 'Africa/Casablanca' => '(GMT+1:00) Casablanca ',
1391 'Africa/Bangui' => '(GMT+1:00) West Central Africa ',
1392 'Africa/Algiers' => '(GMT+1:00) Algiers ',
1393 'Africa/Tunis' => '(GMT+1:00) Tunis ',
1394 'Europe/Belgrade' => '(GMT+2:00) Belgrade, Bratislava, Ljubljana ',
1395 'CET' => '(GMT+2:00) Sarajevo, Skopje, Zagreb ',
1396 'Europe/Oslo' => '(GMT+2:00) Oslo ',
1397 'Europe/Copenhagen' => '(GMT+2:00) Copenhagen ',
1398 'Europe/Brussels' => '(GMT+2:00) Brussels ',
1399 'Europe/Berlin' => '(GMT+2:00) Amsterdam, Berlin, Rome, Stockholm, Vienna ',
1400 'Europe/Amsterdam' => '(GMT+2:00) Amsterdam ',
1401 'Europe/Rome' => '(GMT+2:00) Rome ',
1402 'Europe/Stockholm' => '(GMT+2:00) Stockholm ',
1403 'Europe/Vienna' => '(GMT+2:00) Vienna ',
1404 'Europe/Luxembourg' => '(GMT+2:00) Luxembourg ',
1405 'Europe/Paris' => '(GMT+2:00) Paris ',
1406 'Europe/Zurich' => '(GMT+2:00) Zurich ',
1407 'Europe/Madrid' => '(GMT+2:00) Madrid ',
1408 'Africa/Harare' => '(GMT+2:00) Harare, Pretoria ',
1409 'Europe/Warsaw' => '(GMT+2:00) Warsaw ',
1410 'Europe/Prague' => '(GMT+2:00) Prague Bratislava ',
1411 'Europe/Budapest' => '(GMT+2:00) Budapest ',
1412 'Africa/Tripoli' => '(GMT+2:00) Tripoli ',
1413 'Africa/Cairo' => '(GMT+2:00) Cairo ',
1414 'Africa/Johannesburg' => '(GMT+2:00) Johannesburg ',
1415 'Europe/Helsinki' => '(GMT+3:00) Helsinki ',
1416 'Africa/Nairobi' => '(GMT+3:00) Nairobi ',
1417 'Europe/Sofia' => '(GMT+3:00) Sofia ',
1418 'Europe/Istanbul' => '(GMT+3:00) Istanbul ',
1419 'Europe/Athens' => '(GMT+3:00) Athens ',
1420 'Europe/Bucharest' => '(GMT+3:00) Bucharest ',
1421 'Asia/Nicosia' => '(GMT+3:00) Nicosia ',
1422 'Asia/Beirut' => '(GMT+3:00) Beirut ',
1423 'Asia/Damascus' => '(GMT+3:00) Damascus ',
1424 'Asia/Jerusalem' => '(GMT+3:00) Jerusalem ',
1425 'Asia/Amman' => '(GMT+3:00) Amman ',
1426 'Europe/Moscow' => '(GMT+3:00) Moscow ',
1427 'Asia/Baghdad' => '(GMT+3:00) Baghdad ',
1428 'Asia/Kuwait' => '(GMT+3:00) Kuwait ',
1429 'Asia/Riyadh' => '(GMT+3:00) Riyadh ',
1430 'Asia/Bahrain' => '(GMT+3:00) Bahrain ',
1431 'Asia/Qatar' => '(GMT+3:00) Qatar ',
1432 'Asia/Aden' => '(GMT+3:00) Aden ',
1433 'Africa/Khartoum' => '(GMT+3:00) Khartoum ',
1434 'Africa/Djibouti' => '(GMT+3:00) Djibouti ',
1435 'Africa/Mogadishu' => '(GMT+3:00) Mogadishu ',
1436 'Europe/Kiev' => '(GMT+3:00) Kiev ',
1437 'Asia/Dubai' => '(GMT+4:00) Dubai ',
1438 'Asia/Muscat' => '(GMT+4:00) Muscat ',
1439 'Asia/Tehran' => '(GMT+4:30) Tehran ',
1440 'Asia/Kabul' => '(GMT+4:30) Kabul ',
1441 'Asia/Baku' => '(GMT+5:00) Baku, Tbilisi, Yerevan ',
1442 'Asia/Yekaterinburg' => '(GMT+5:00) Yekaterinburg ',
1443 'Asia/Tashkent' => '(GMT+5:00) Tashkent ',
1444 'Asia/Karachi' => '(GMT+5:00) Islamabad, Karachi ',
1445 'Asia/Calcutta' => '(GMT+5:30) India ',
1446 'Asia/Kolkata' => '(GMT+5:30) Mumbai, Kolkata, New Delhi ',
1447 'Asia/Kathmandu' => '(GMT+5:45) Kathmandu ',
1448 'Asia/Novosibirsk' => '(GMT+6:00) Novosibirsk ',
1449 'Asia/Almaty' => '(GMT+6:00) Almaty ',
1450 'Asia/Dacca' => '(GMT+6:00) Dacca ',
1451 'Asia/Dhaka' => '(GMT+6:00) Astana, Dhaka ',
1452 'Asia/Krasnoyarsk' => '(GMT+7:00) Krasnoyarsk ',
1453 'Asia/Bangkok' => '(GMT+7:00) Bangkok ',
1454 'Asia/Saigon' => '(GMT+7:00) Vietnam ',
1455 'Asia/Jakarta' => '(GMT+7:00) Jakarta ',
1456 'Asia/Irkutsk' => '(GMT+8:00) Irkutsk, Ulaanbaatar ',
1457 'Asia/Shanghai' => '(GMT+8:00) Beijing, Shanghai ',
1458 'Asia/Hong_Kong' => '(GMT+8:00) Hong Kong ',
1459 'Asia/Taipei' => '(GMT+8:00) Taipei ',
1460 'Asia/Kuala_Lumpur' => '(GMT+8:00) Kuala Lumpur ',
1461 'Asia/Singapore' => '(GMT+8:00) Singapore ',
1462 'Australia/Perth' => '(GMT+8:00) Perth ',
1463 'Asia/Yakutsk' => '(GMT+9:00) Yakutsk ',
1464 'Asia/Seoul' => '(GMT+9:00) Seoul ',
1465 'Asia/Tokyo' => '(GMT+9:00) Osaka, Sapporo, Tokyo ',
1466 'Australia/Darwin' => '(GMT+9:30) Darwin ',
1467 'Australia/Adelaide' => '(GMT+9:30) Adelaide ',
1468 'Asia/Vladivostok' => '(GMT+10:00) Vladivostok ',
1469 'Pacific/Port_Moresby' => '(GMT+10:00) Guam, Port Moresby ',
1470 'Australia/Brisbane' => '(GMT+10:00) Brisbane ',
1471 'Australia/Sydney' => '(GMT+10:00) Canberra, Melbourne, Sydney ',
1472 'Australia/Hobart' => '(GMT+10:00) Hobart ',
1473 'Asia/Magadan' => '(GMT+10:00) Magadan ',
1474 'SST' => '(GMT+11:00) Solomon Islands ',
1475 'Pacific/Noumea' => '(GMT+11:00) New Caledonia ',
1476 'Asia/Kamchatka' => '(GMT+12:00) Kamchatka ',
1477 'Pacific/Fiji' => '(GMT+12:00) Fiji Islands, Marshall Islands ',
1478 'Pacific/Auckland' => '(GMT+12:00) Auckland, Wellington',
1479 );
1480 }
1481
1482 if ( ! function_exists( 'tutor_get_all_active_payment_gateways' ) ) {
1483 /**
1484 * Get all active payment gateways including manual & automate
1485 *
1486 * @since 3.0.0
1487 *
1488 * @return array
1489 */
1490 function tutor_get_all_active_payment_gateways() {
1491 $payment_settings = Settings::get_payment_settings();
1492 $payment_methods = ! empty( $payment_settings['payment_methods'] ) ? $payment_settings['payment_methods'] : array();
1493
1494 $active_gateways = array();
1495
1496 foreach ( $payment_methods as $method ) {
1497 $is_active = $method['is_active'] ?? false;
1498 $is_manual = $method['is_manual'] ?? false;
1499 if ( ! $is_active ) {
1500 continue;
1501 }
1502
1503 if ( ! Ecommerce::is_payment_gateway_configured( $method['name'] ) ) {
1504 continue;
1505 }
1506
1507 $fields = $method['fields'];
1508 unset( $method['fields'] );
1509
1510 $gateway = $method;
1511 if ( $is_manual ) {
1512 foreach ( $fields as $field ) {
1513 if ( 'payment_instructions' === $field['name'] || 'additional_details' === $field['name'] ) {
1514 $gateway[ $field['name'] ] = $field['value'];
1515 }
1516 }
1517 }
1518
1519 $active_gateways[] = $gateway;
1520 }
1521
1522 return $active_gateways;
1523 }
1524 }
1525
1526 if ( ! function_exists( 'tutor_get_subscription_supported_payment_gateways' ) ) {
1527 /**
1528 * Get all supported gateways
1529 *
1530 * This function will return only subscription supported gateways if
1531 * plan id provided.
1532 *
1533 * @since 3.0.0
1534 * @since 3.4.0 plan_id param removed
1535 *
1536 * @return array
1537 */
1538 function tutor_get_subscription_supported_payment_gateways() {
1539 $payment_gateways = tutor_get_all_active_payment_gateways();
1540
1541 $supported_gateways = array();
1542 foreach ( $payment_gateways as $gateway ) {
1543 $support_subscription = $gateway['support_subscription'] ?? false;
1544
1545 if ( ! $support_subscription ) {
1546 continue;
1547 }
1548
1549 $supported_gateways[] = array(
1550 'name' => $gateway['name'] ?? '',
1551 'label' => $gateway['label'] ?? '',
1552 'icon' => $gateway['icon'] ?? '',
1553 'support_subscription' => $gateway['support_subscription'] ?? '',
1554 'is_manual' => $gateway['is_manual'] ?? '',
1555 'additional_details' => $gateway['additional_details'] ?? '',
1556 'payment_instructions' => $gateway['payment_instructions'] ?? '',
1557 );
1558 }
1559
1560 return $supported_gateways;
1561 }
1562 }
1563
1564 if ( ! function_exists( 'tutor_get_manual_payment_gateways' ) ) {
1565 /**
1566 * Get manual payment gateways
1567 *
1568 * @since 3.0.0
1569 *
1570 * @return array
1571 */
1572 function tutor_get_manual_payment_gateways() {
1573 $payments = tutor_utils()->get_option( 'payment_settings' );
1574 $payments = json_decode( stripslashes( $payments ) );
1575
1576 $manual_methods = array();
1577
1578 if ( $payments ) {
1579 foreach ( $payments->payment_methods as $method ) {
1580 if ( isset( $method->is_manual ) && 1 === (int) $method->is_manual ) {
1581 $manual_methods[] = $method;
1582 }
1583 }
1584 }
1585
1586 return apply_filters( 'tutor_manual_payment_methods', $manual_methods );
1587 }
1588 }
1589 }
1590
1591 if ( ! function_exists( 'tutor_get_course_formatted_price_html' ) ) {
1592 /**
1593 * Get course formatted price
1594 * Only for monetized by tutor.
1595 *
1596 * @since 3.0.0
1597 *
1598 * @param int $course_id Course price.
1599 * @param boolean $echo Whether to echo content.
1600 *
1601 * @return string|void
1602 */
1603 function tutor_get_course_formatted_price_html( $course_id, $echo = true ) {
1604 $price_data = tutor_utils()->get_raw_course_price( $course_id );
1605
1606 if ( ! $price_data->regular_price ) {
1607 return;
1608 }
1609 ob_start();
1610 ?>
1611 <div class="list-item-price tutor-item-price">
1612 <?php if ( $price_data->sale_price ) : ?>
1613 <span><?php tutor_print_formatted_price( $price_data->display_price ); ?></span>
1614 <del><?php tutor_print_formatted_price( $price_data->regular_price ); ?></del>
1615 <?php else : ?>
1616 <span><?php tutor_print_formatted_price( $price_data->display_price ); ?></span>
1617 <?php endif; ?>
1618 </div>
1619 <?php if ( $price_data->show_incl_tax_label ) : ?>
1620 <div class="tutor-course-price-tax tutor-fs-8 tutor-fw-normal tutor-color-black"><?php esc_html_e( 'Incl. tax', 'tutor' ); ?></div>
1621 <?php endif; ?>
1622 <?php
1623 $content = apply_filters( 'tutor_course_formatted_price', ob_get_clean() );
1624 if ( $echo ) {
1625 echo $content; // PHPCS:ignore
1626 } else {
1627 return $content;
1628 }
1629 }
1630 }
1631
1632 if ( ! function_exists( 'tutor_get_formatted_price' ) ) {
1633 /**
1634 * Get course formatted price
1635 *
1636 * Formatting as per ecommerce price settings
1637 *
1638 * @since 3.0.0
1639 *
1640 * @param mixed $price Raw price.
1641 *
1642 * @return string|void
1643 */
1644 function tutor_get_formatted_price( $price ) {
1645 $price = floatval( Input::sanitize( $price ) );
1646 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
1647 if ( Ecommerce::MONETIZE_BY === $monetize_by ) {
1648 $currency_symbol = Settings::get_currency_symbol_by_code( tutor_utils()->get_option( OptionKeys::CURRENCY_CODE, 'USD' ) );
1649 $currency_position = tutor_utils()->get_option( OptionKeys::CURRENCY_POSITION, 'left' );
1650 $thousand_separator = tutor_utils()->get_option( OptionKeys::THOUSAND_SEPARATOR, ',' );
1651 $decimal_separator = tutor_utils()->get_option( OptionKeys::DECIMAL_SEPARATOR, '.' );
1652 $no_of_decimal = tutor_utils()->get_option( OptionKeys::NUMBER_OF_DECIMALS, '2' );
1653
1654 $price = number_format( $price, $no_of_decimal, $decimal_separator, $thousand_separator );
1655 $price = 'left' === $currency_position ? $currency_symbol . $price : $price . $currency_symbol;
1656 } elseif ( 'wc' === $monetize_by ) {
1657 $price = wc_price( $price );
1658 } elseif ( 'edd' === $monetize_by ) {
1659 $price = edd_currency_filter( edd_format_amount( $price ) );
1660 }
1661
1662 return $price;
1663 }
1664 }
1665
1666 if ( ! function_exists( 'tutor_print_formatted_price' ) ) {
1667 /**
1668 * A clone copy of `tutor_get_formatted_price` helper
1669 * To print formated price with output scaping.
1670 *
1671 * @since 3.0.0
1672 *
1673 * @param mixed $price price.
1674 *
1675 * @return void
1676 */
1677 function tutor_print_formatted_price( $price ) {
1678 echo esc_html( tutor_get_formatted_price( $price ) );
1679 }
1680 }
1681
1682 if ( ! function_exists( 'tutor_get_locale_price' ) ) {
1683 /**
1684 * Get price as per locale format
1685 *
1686 * For locale settings currency code will be used
1687 *
1688 * @since 3.0.0
1689 *
1690 * @param mixed $price Raw price.
1691 *
1692 * @return mixed raw price.
1693 */
1694 function tutor_get_locale_price( $price ) {
1695 // TODO: implement price formation.
1696 return $price;
1697 }
1698 }
1699
1700 if ( ! function_exists( 'tutor_is_json' ) ) {
1701 /**
1702 * Check a string is valid JSON.
1703 *
1704 * @param string $string string.
1705 *
1706 * @return boolean
1707 */
1708 function tutor_is_json( $string ) {
1709 json_decode( $string );
1710 return json_last_error() === JSON_ERROR_NONE;
1711 }
1712 }
1713
1714 if ( ! function_exists( 'tutor_is_dev_mode' ) ) {
1715 /**
1716 * Check tutor is in development mode or not.
1717 *
1718 * @since 3.0.0
1719 *
1720 * @return bool True if the current environment is local, false otherwise.
1721 */
1722 function tutor_is_dev_mode() {
1723 return defined( 'TUTOR_DEV_MODE' ) && TUTOR_DEV_MODE;
1724 }
1725 }
1726
1727 if ( ! function_exists( 'tutor_redirect_after_payment' ) ) {
1728 /**
1729 * Redirect after payment with status and message
1730 *
1731 * @since 3.0.0
1732 *
1733 * @param string $status Success or error status of payment.
1734 * @param int $order_id Order ID.
1735 * @param string $message Success/error message to display.
1736 *
1737 * @return void
1738 */
1739 function tutor_redirect_after_payment( $status, $order_id, $message = '' ) {
1740 $query_params = array(
1741 'tutor_order_placement' => $status,
1742 'order_id' => $order_id,
1743 );
1744
1745 if ( $message ) {
1746 if ( 'success' === $status ) {
1747 $query_params['success_message'] = $message;
1748 } else {
1749 $query_params['error_message'] = $message;
1750 }
1751 }
1752
1753 wp_safe_redirect( apply_filters( 'tutor_redirect_url_after_checkout', add_query_arg( $query_params, home_url() ), $status, $order_id ) );
1754 exit();
1755 }
1756 }
1757
1758 if ( ! function_exists( 'tutor_split_amounts' ) ) {
1759 /**
1760 * Split amounts into parts for admin & instructor
1761 *
1762 * Amount split will be proportionally based on
1763 * admin commission rate & instructor commission rate.
1764 *
1765 * @since 3.0.0
1766 *
1767 * @param array $amounts Single amount or list of amount array. For ex: [12,20,100].
1768 *
1769 * @return array
1770 */
1771 function tutor_split_amounts( $amounts ) {
1772 $amounts = is_array( $amounts ) ? $amounts : array( $amounts );
1773
1774 $admin_amount = 0;
1775 $instructor_amount = 0;
1776
1777 $sharing_enabled = tutor_utils()->get_option( 'enable_revenue_sharing' );
1778 $instructor_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_instructor_commission' ) : 0;
1779 $admin_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_admin_commission' ) : 100;
1780
1781 foreach ( $amounts as $amount ) {
1782 $instructor_amount = $instructor_rate > 0 ? ( ( $amount * $instructor_rate ) / 100 ) : 0;
1783 $admin_amount = $admin_rate > 0 ? ( ( $amount * $admin_rate ) / 100 ) : 0;
1784 }
1785
1786 return array(
1787 'admin' => $admin_amount,
1788 'instructor' => $instructor_amount,
1789 );
1790 }
1791 }
1792
1793 if ( ! function_exists( 'tutor_is_local_env' ) ) {
1794 /**
1795 * Check if the current environment is local.
1796 *
1797 * @since 3.2.0
1798 *
1799 * @return bool True if the current environment is local, false otherwise.
1800 */
1801 function tutor_is_local_env() {
1802 $site_url = site_url();
1803 return (
1804 strpos( $site_url, '.local' ) !== false ||
1805 strpos( $site_url, 'localhost' ) !== false
1806 );
1807 }
1808 }
1809
1810
1811
1812 if ( ! function_exists( 'get_tutor_post_types' ) ) {
1813 /**
1814 * Get tutor post type list
1815 *
1816 * @since 3.6.0
1817 *
1818 * @param string $post_type the post type to get single tutor valid post type
1819 *
1820 * @return array|string
1821 */
1822 function get_tutor_post_types( $post_type = '' ) {
1823 $valid_post_types = array(
1824 'course' => tutor()->course_post_type,
1825 'bundle' => tutor()->bundle_post_type,
1826 'lesson' => tutor()->lesson_post_type,
1827 'topics' => tutor()->topics_post_type,
1828 'quiz' => tutor()->quiz_post_type,
1829 'assignment' => tutor()->assignment_post_type,
1830 'zoom' => tutor()->zoom_post_type,
1831 'meet' => tutor()->meet_post_type,
1832 'enrollment' => tutor()->enrollment_post_type,
1833 'announcement' => tutor()->announcement_post_type,
1834 );
1835
1836 if ( $post_type && isset( $valid_post_types[ $post_type ] ) ) {
1837 return $valid_post_types[ $post_type ];
1838 }
1839
1840 return $valid_post_types;
1841 }
1842 }
1843
1844 if ( ! function_exists( 'tutor_decode_unicode_sequences' ) ) {
1845 /**
1846 * Decode Unicode escape sequences in a string to their corresponding UTF-8 characters.
1847 *
1848 * Example:
1849 * decode_unicode_escapes('Hello\\u0020World') => 'Hello World'
1850 * decode_unicode_escapes('\\u{1F600}') => '😀'
1851 *
1852 * Notes:
1853 * - Input and output are expected to be UTF-8 encoded.
1854 * - Behavior for malformed sequences is to preserve the original bytes rather than throw.
1855 *
1856 * @param string $str The input string possibly containing Unicode escape sequences.
1857 * @return string The string with Unicode escape sequences decoded into UTF-8 characters.
1858 */
1859 function tutor_decode_unicode_sequences( $str ) {
1860 if ( empty( $str ) ) {
1861 return '';
1862 }
1863
1864 // Step 1: Decode \uXXXX or uXXXX sequences.
1865 $str = preg_replace_callback(
1866 '/\\\\?u([0-9a-fA-F]{4})/',
1867 function ( $m ) {
1868 return mb_convert_encoding( pack( 'H*', $m[1] ), 'UTF-8', 'UCS-2BE' );
1869 },
1870 $str
1871 );
1872
1873 // Step 2: Decode HTML entities like &lt;, &nbsp;.
1874 $str = html_entity_decode( $str, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
1875
1876 return $str;
1877 }
1878 }
1879