Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/tutor/includes/tutor-general-functions.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
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 +