PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.10
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.10
4.4.10 4.4.9.1 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 All 142 releases
← All changes | inc/class-lp-helper.php +657 -624 4.3.9 → 4.4.10 View file →
@@ -1,624 +1,657 @@
1 -<?php
2 -
3 -/**
4 - * Class LP_Helper
5 - */
6 -
7 -use LearnPress\Helpers\Template;
8 -
9 -defined( 'ABSPATH' ) || exit;
10 -
11 -class LP_Helper {
12 -
13 - /**
14 - * Wrap function unserialize to fix issues with UTF-8 chars when encoding/decoding
15 - * of serialize function.
16 - *
17 - * @param string $string
18 - *
19 - * @return mixed
20 - */
21 - public static function maybe_unserialize( $string ) {
22 - if ( is_string( $string ) ) {
23 -
24 - $unserialized = maybe_unserialize( $string );
25 - if ( ! $unserialized && strlen( $string ) ) {
26 - $string = preg_replace_callback(
27 - '!s:(\d+):"(.*?)";!s',
28 - array( __CLASS__, '_unserialize_replace_callback' ),
29 - $string
30 - );
31 -
32 - $unserialized = maybe_unserialize( $string );
33 - }
34 -
35 - $string = $unserialized;
36 - }
37 -
38 - return $string;
39 - }
40 -
41 - public static function _unserialize_replace_callback( $m ) {
42 - $len = strlen( $m[2] );
43 - $result = "s:$len:\"{$m[2]}\";";
44 -
45 - return $result;
46 - }
47 -
48 - /**
49 - * Shuffle array and keep the keys
50 - *
51 - * @param array $array
52 - *
53 - * @return bool
54 - */
55 - public static function shuffle_assoc( &$array ) {
56 - $keys = array_keys( $array );
57 - shuffle( $keys );
58 - $new = array();
59 - foreach ( $keys as $key ) {
60 - $new[ $key ] = $array[ $key ];
61 - }
62 - $array = $new;
63 -
64 - return true;
65 - }
66 -
67 - /**
68 - * MD5 an array.
69 - *
70 - * @param array $array
71 - *
72 - * @return string
73 - */
74 - public static function array_to_md5( $array ) {
75 - settype( $array, 'array' );
76 - ksort( $array );
77 -
78 - return md5( serialize( $array ) );
79 - }
80 -
81 - /**
82 - * Load posts from database into cache by ids
83 - *
84 - * @param array|int $ids
85 - *
86 - * @Todo: tungnx - need to review code - addon h5p v4.0.3 still use
87 - * @deprecated 4.1.6.9
88 - */
89 - public static function cache_posts( $ids ) {
90 - //_deprecated_function( __FUNCTION__, '4.1.6.9' );
91 - }
92 -
93 - /**
94 - * Merge two or more classes into one.
95 - *
96 - * @return array
97 - */
98 - public static function merge_class() {
99 - if ( func_num_args() == 1 ) {
100 - return func_get_arg( 0 );
101 - } elseif ( func_num_args() == 0 ) {
102 - return null;
103 - }
104 -
105 - $classes = array();
106 - foreach ( func_get_args() as $class ) {
107 - if ( is_string( $class ) ) {
108 - $cls = explode( ' ', $class );
109 - $classes = array_merge( $classes, $cls );
110 - } else {
111 - $classes = array_merge( $classes, $class );
112 - }
113 - }
114 -
115 - $classes = array_filter( $classes );
116 - $classes = array_unique( $classes );
117 -
118 - return $classes;
119 - }
120 -
121 - /**
122 - * Sanitize order statuses.
123 - * Add prefix lp- into each status if it is not exists.
124 - *
125 - * @param $statuses
126 - *
127 - * @return array|mixed
128 - */
129 - public static function sanitize_order_status( &$statuses ) {
130 - if ( is_array( $statuses ) ) {
131 - foreach ( $statuses as $k => $status ) {
132 - if ( false === strpos( $status, 'lp-' ) ) {
133 - $statuses[ $k ] = "lp-{$status}";
134 - }
135 - }
136 - } else {
137 - $statuses = preg_split( '#\s+#', $statuses );
138 - self::sanitize_order_status( $statuses );
139 - if ( sizeof( $statuses ) == 1 ) {
140 - $statuses = reset( $statuses );
141 - }
142 - }
143 -
144 - return $statuses;
145 - }
146 -
147 - /**
148 - * Merge two arrays recursive.
149 - *
150 - * @param array $array1
151 - * @param array $array2
152 - *
153 - * @return array
154 - */
155 - public static function array_merge_recursive( &$array1, &$array2 ) {
156 - $merged = $array1;
157 -
158 - if ( is_array( $array1 ) && is_array( $array2 ) ) {
159 - foreach ( $array2 as $key => & $value ) {
160 - if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
161 - $merged[ $key ] = self::array_merge_recursive( $merged[ $key ], $value );
162 - } elseif ( is_numeric( $key ) ) {
163 - if ( ! in_array( $value, $merged ) ) {
164 - $merged[] = $value;
165 - }
166 - } else {
167 - $merged[ $key ] = $value;
168 - }
169 - }
170 - } elseif ( is_array( $array2 ) ) {
171 - $merged = $array2;
172 - }
173 -
174 - return $merged;
175 - }
176 -
177 - /**
178 - * Encode the object to json format.
179 - * Replace number, "false", "true" in couple of quotes with
180 - * original value.
181 - * Example:
182 - * Input: {
183 - * number: "1234",
184 - * true_value: "true",
185 - * false_value: "false"
186 - * }
187 - * Output: {
188 - * number: 1234,
189 - * true_value: true,
190 - * false_value: false
191 - * }
192 - *
193 - * @param array $data
194 - *
195 - * @return false|mixed|string
196 - */
197 - public static function json_encode( $data ) {
198 - $data = wp_json_encode( $data );
199 - $data = preg_replace_callback(
200 - '~:"(([0-9]+)([.,]?)([0-9]?)|true|false)"~',
201 - array(
202 - __CLASS__,
203 - '_valid_json_value',
204 - ),
205 - $data
206 - );
207 -
208 - return $data;
209 - }
210 -
211 - /**
212 - * Callback function for json_encode method "json_encode".
213 - *
214 - * @param array $m
215 - *
216 - * @return string
217 - */
218 - public static function _valid_json_value( $m ) {
219 - return str_replace( array( ':"', '"' ), array( ':', '' ), $m[0] );
220 - }
221 -
222 - /**
223 - * Create LP static page.
224 - *
225 - * @param array $args
226 - * @param string $key_option
227 - *
228 - * @return bool|int|WP_Error
229 - */
230 - public static function create_page( array $args, string $key_option ) {
231 - $page_id = 0;
232 -
233 - try {
234 - if ( ! isset( $args['post_title'] ) ) {
235 - throw new Exception( __( 'Missing post title', 'learnpress' ) );
236 - }
237 -
238 - if ( preg_match( '#^learn_press_single_instructor_page_id.*#', $key_option ) ) {
239 - $args['post_content'] = '<!-- wp:shortcode -->[learn_press_single_instructor]<!-- /wp:shortcode -->';
240 - } elseif ( preg_match( '#^learn_press_instructors_page_id.*#', $key_option ) ) {
241 - $args['post_content'] = '<!-- wp:shortcode -->[learn_press_instructors]<!-- /wp:shortcode -->';
242 - } elseif ( preg_match( '#^learn_press_profile_page_id.*#', $key_option ) ) {
243 - $args['post_content'] = '<!-- wp:shortcode -->[learn_press_profile]<!-- /wp:shortcode -->';
244 - }
245 -
246 - $args = array_merge(
247 - [
248 - 'post_title' => '',
249 - 'post_name' => '',
250 - 'post_status' => 'publish',
251 - 'post_type' => 'page',
252 - 'comment_status' => 'closed',
253 - 'post_content' => '',
254 - 'post_author' => get_current_user_id(),
255 - ],
256 - $args
257 - );
258 -
259 - $page_id = wp_insert_post( $args );
260 - if ( ! $page_id ) {
261 - return false;
262 - }
263 -
264 - update_option( $key_option, $page_id );
265 - $lp_settings_cache = new LP_Settings_Cache( true );
266 - $lp_settings_cache->clean_lp_settings();
267 - } catch ( Throwable $e ) {
268 - error_log( __METHOD__ . ': ' . $e->getMessage() );
269 - }
270 -
271 - return $page_id;
272 - }
273 -
274 - /**
275 - * Get the current url
276 - *
277 - * @return string
278 - * @since 3.2.6.8
279 - * @author tungnx
280 - */
281 - public static function getUrlCurrent(): string {
282 - $schema = is_ssl() ? 'https://' : 'http://';
283 - $http_host = LP_Helper::sanitize_params_submitted( urldecode( $_SERVER['HTTP_HOST'] ?? '' ) );
284 - $request_uri = LP_Helper::sanitize_params_submitted( urldecode( $_SERVER['REQUEST_URI'] ?? '' ) );
285 - //$http_host = LP_Helper::sanitize_params_submitted( filter_input( INPUT_SERVER, 'HTTP_HOST' ) ?? '' );
286 - //$request_uri = LP_Helper::sanitize_params_submitted( filter_input( INPUT_SERVER, 'REQUEST_URI' ) ?? '' );
287 -
288 - return untrailingslashit( $schema . $http_host . $request_uri );
289 - }
290 -
291 - /**
292 - * Check request is rest api
293 - *
294 - * @return bool
295 - * @author tungnx
296 - * @since 4.1.6.6
297 - */
298 - public static function isRestApiLP(): bool {
299 - $restPrefix = '/' . rest_get_url_prefix();
300 - return strpos( self::getUrlCurrent(), $restPrefix . '/lp/' ) || strpos( self::getUrlCurrent(), $restPrefix . '/learnpress/' );
301 - }
302 -
303 - /**
304 - * Sanitize string and array
305 - *
306 - * @param array|string $value
307 - * @param string $type_content
308 - * @param bool $unslash Set it is false when you don’t want to remove slashes (unslash) from $value
309 - * for example, in cases involving LaTeX math syntax.
310 - * @return array|string
311 - * @since 3.2.7.1
312 - * @author tungnx
313 - */
314 - public static function sanitize_params_submitted( $value, string $type_content = 'text', $unslash = true ) {
315 - $value = $unslash ? wp_unslash( $value ) : $value;
316 -
317 - if ( is_string( $value ) ) {
318 - switch ( $type_content ) {
319 - case 'html':
320 - $value = Template::sanitize_html_content( $value );
321 - break;
322 - case 'textarea':
323 - $value = sanitize_textarea_field( $value );
324 - break;
325 - case 'key':
326 - $value = sanitize_key( $value );
327 - break;
328 - case 'int':
329 - $value = (int) $value;
330 - break;
331 - case 'float':
332 - $value = (float) $value;
333 - break;
334 - default:
335 - if ( is_callable( $type_content ) ) {
336 - $value = call_user_func( $type_content, $value );
337 - } else {
338 - $value = sanitize_text_field( $value );
339 - }
340 - }
341 - } elseif ( is_array( $value ) ) {
342 - foreach ( $value as $k => $v ) {
343 - unset( $value[ $k ] );
344 - $value[ sanitize_text_field( $k ) ] = self::sanitize_params_submitted( $v, $type_content, $unslash );
345 - }
346 - }
347 -
348 - return $value;
349 - }
350 -
351 - public static function db_format_array( array $arr, string $format = '%d' ): string {
352 - $arr_formatted = array_fill( 0, sizeof( $arr ), $format );
353 -
354 - return join( ',', $arr_formatted );
355 - }
356 -
357 - /**
358 - * Calculate percent of progress rows on db
359 - *
360 - * @param int $offset .
361 - * @param int $limit .
362 - * @param int $total_row .
363 - *
364 - * @return float
365 - */
366 - public static function progress_percent( int $offset, int $limit, int $total_row ): float {
367 - if ( $total_row <= 0 ) {
368 - return 0;
369 - }
370 -
371 - $percent = ( $offset + $limit ) * 100 / $total_row;
372 -
373 - $percent = min( $percent, 100 );
374 -
375 - return floatval( number_format( $percent, 2 ) );
376 - }
377 -
378 - /**
379 - * Convert array to string
380 - * Ex: array("publish", "pending") to post_status IN(%s, %s)
381 - *
382 - * @param array $arr
383 - *
384 - * @return string
385 - */
386 - public static function format_query_IN( array $arr ): string {
387 - $format = array_fill( 0, count( $arr ), '%s' );
388 -
389 - return join( ',', $format );
390 - }
391 -
392 - /**
393 - * Get link lp checkout page
394 - * without cache - because some cache(redis) will cache page with user anonymous
395 - */
396 - public static function get_link_no_cache( string $link ): string {
397 - return esc_url_raw( add_query_arg( 'no-cache', uniqid(), $link ) );
398 - }
399 -
400 - /**
401 - * Check string is json
402 - *
403 - * @param string $str
404 - * @param null $associative
405 - *
406 - * @return mixed
407 - * @throws Exception
408 - * @since 4.1.6.4
409 - * @version 1.0.1
410 - */
411 - public static function json_decode( string $str, $associative = null ) {
412 - $obj = json_decode( $str, $associative );
413 - if ( json_last_error() !== JSON_ERROR_NONE ) {
414 - throw new Exception( 'JSON decode: ' . json_last_error_msg() );
415 - }
416 -
417 - return $obj;
418 - }
419 -
420 - /**
421 - * Handle permalink structure for LP
422 - *
423 - * @return string
424 - * @since 4.2.2
425 - * @version 1.0.4
426 - */
427 - public static function handle_lp_permalink_structure( $post_link, $post ) {
428 - if ( false === strpos( $post_link, '%' ) ) {
429 - return $post_link;
430 - }
431 -
432 - $find = [];
433 - $replace = [];
434 - if ( ! empty( $post->post_date_gmt ) ) {
435 - $find = array(
436 - '%year%',
437 - '%monthnum%',
438 - '%day%',
439 - '%hour%',
440 - '%minute%',
441 - '%second%',
442 - '%post_id%',
443 - );
444 -
445 - $time = strtotime( $post->post_date_gmt );
446 -
447 - $replace = array(
448 - date_i18n( 'Y', $time ),
449 - date_i18n( 'm', $time ),
450 - date_i18n( 'd', $time ),
451 - date_i18n( 'H', $time ),
452 - date_i18n( 'i', $time ),
453 - date_i18n( 's', $time ),
454 - $post->ID,
455 - );
456 - }
457 -
458 - if ( strpos( $post_link, '%course_category%' ) && get_post_type( $post ) === LP_COURSE_CPT ) {
459 - // Get the custom taxonomy terms in use by this post
460 - $terms = get_the_terms( $post->ID, 'course_category' );
461 -
462 - if ( ! empty( $terms ) ) {
463 - $terms = wp_list_sort( $terms, 'term_id' );
464 - // order by IDF
465 - $category_object = apply_filters(
466 - 'learn_press_course_post_type_link_course_category',
467 - $terms[0],
468 - $terms,
469 - $post
470 - );
471 - $category_object = get_term( $category_object, 'course_category' );
472 - if ( ! $category_object instanceof WP_Term ) {
473 - return $post_link;
474 - }
475 -
476 - $course_category = $category_object->slug;
477 - $parent = $category_object->parent;
478 - if ( $parent ) {
479 - $ancestors = get_ancestors( $category_object->term_id, 'course_category' );
480 - foreach ( $ancestors as $ancestor ) {
481 - $ancestor_object = get_term( $ancestor, 'course_category' );
482 - if ( $ancestor_object instanceof WP_Term ) {
483 - $course_category = $ancestor_object->slug . '/' . $course_category;
484 - }
485 - }
486 - }
487 - } else {
488 - // If no terms are assigned to this post, use a string instead (can't leave the placeholder there)
489 - $course_category = _x( 'uncategorized', 'slug', 'learnpress' );
490 - }
491 -
492 - $find[] = '%course_category%';
493 - $replace[] = urldecode( $course_category );
494 - }
495 -
496 - return apply_filters(
497 - 'learn-press/single-course/permalink',
498 - str_replace( $find, $replace, $post_link ),
499 - $post
500 - );
501 - }
502 -
503 - /**
504 - * Print variable script inline script tag.
505 - * If $name_variable_script is empty,
506 - * the script will be print as json with set $tag_args['type'] = application/json.
507 - *
508 - * @param string $name_variable_script
509 - * @param array $data
510 - * @param array $tag_args as ['type' => 'text/javascript', 'id' => '']
511 - *
512 - * @return void
513 - * @version 1.0.1
514 - * @since 4.2.5.5
515 - */
516 - public static function print_inline_script_tag( string $name_variable_script, array $data, array $tag_args = [] ) {
517 - foreach ( $data as $key => $value ) {
518 - if ( ! is_scalar( $value ) ) {
519 - continue;
520 - }
521 -
522 - $data[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
523 - }
524 -
525 - $data_json = wp_json_encode( $data );
526 - $script = '';
527 - if ( ! empty( $name_variable_script ) ) {
528 - $script = "var {$name_variable_script} = {$data_json};";
529 - } elseif ( isset( $tag_args['type'] ) && $tag_args['type'] === 'application/json' ) {
530 - $script = $data_json;
531 - }
532 - wp_print_inline_script_tag( $script, $tag_args );
533 - }
534 -
535 - /**
536 - * Get translation string single/plural
537 - *
538 - * @param float $number
539 - * @param string $string_value
540 - * @param bool $include_number
541 - *
542 - * @return string
543 - * @since 4.2.7.4
544 - * @version 1.0.0
545 - */
546 - public static function get_i18n_string_plural( float $number, string $string_value = '', bool $include_number = true ): string {
547 - switch ( $string_value ) {
548 - case LP_COURSE_CPT:
549 - $plural = sprintf(
550 - _n( 'Course', 'Courses', $number, 'learnpress' ),
551 - $number
552 - );
553 - break;
554 - case LP_LESSON_CPT:
555 - $plural = sprintf(
556 - _n( 'Lesson', 'Lessons', $number, 'learnpress' ),
557 - $number
558 - );
559 - break;
560 - case LP_QUIZ_CPT:
561 - $plural = sprintf(
562 - _n( 'Quiz', 'Quizzes', $number, 'learnpress' ),
563 - $number
564 - );
565 - break;
566 - case LP_QUESTION_CPT:
567 - $plural = sprintf(
568 - _n( 'Question', 'Questions', $number, 'learnpress' ),
569 - $number
570 - );
571 - break;
572 - case 'lp_assignment':
573 - $plural = sprintf(
574 - _n( 'Assignment', 'Assignments', $number, 'learnpress' ),
575 - $number
576 - );
577 - break;
578 - case 'lp_h5p':
579 - $plural = sprintf(
580 - _n( 'H5P', 'H5Ps', $number, 'learnpress' ),
581 - $number
582 - );
583 - break;
584 - default:
585 - $plural = $string_value;
586 - break;
587 - }
588 -
589 - if ( $include_number ) {
590 - $plural = sprintf( '%s %s', $number, $plural );
591 - }
592 -
593 - return apply_filters( 'learn-press/i18n/plural', $plural, $number, $string_value, $include_number );
594 - }
595 -
596 - /**
597 - * Get client ip address
598 - *
599 - * @return mixed|string
600 - * @since 4.2.7.9
601 - * @version 1.0.0
602 - */
603 - public static function get_client_ip(): string {
604 - $ipaddress = 'ip-unknown';
605 - if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
606 - $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
607 - } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
608 - $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
609 - } elseif ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) {
610 - $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
611 - } elseif ( isset( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
612 - $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
613 - } elseif ( isset( $_SERVER['HTTP_FORWARDED'] ) ) {
614 - $ipaddress = $_SERVER['HTTP_FORWARDED'];
615 - } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
616 - $ipaddress = $_SERVER['REMOTE_ADDR'];
617 - }
618 -
619 - $ipaddress = (string) $ipaddress;
620 - $ipaddress = 'gip-' . $ipaddress;
621 -
622 - return $ipaddress;
623 - }
624 -}
1 +<?php
2 +
3 +/**
4 + * Class LP_Helper
5 + */
6 +
7 +use LearnPress\Helpers\Template;
8 +
9 +defined( 'ABSPATH' ) || exit;
10 +
11 +class LP_Helper {
12 +
13 + /**
14 + * Wrap function unserialize to fix issues with UTF-8 chars when encoding/decoding
15 + * of serialize function.
16 + *
17 + * @param string $string
18 + *
19 + * @return mixed
20 + */
21 + public static function maybe_unserialize( $string ) {
22 + if ( is_string( $string ) && is_serialized( $string ) ) {
23 + $string = trim( $string );
24 + $unserialized = @unserialize( $string, array( 'allowed_classes' => false ) );
25 + if ( false === $unserialized && 'b:0;' !== $string ) {
26 + $string = preg_replace_callback(
27 + '!s:(\d+):"(.*?)";!s',
28 + array( __CLASS__, '_unserialize_replace_callback' ),
29 + $string
30 + );
31 +
32 + $unserialized = @unserialize( $string, array( 'allowed_classes' => false ) );
33 + }
34 +
35 + $string = $unserialized;
36 + }
37 +
38 + return $string;
39 + }
40 +
41 + public static function _unserialize_replace_callback( $m ) {
42 + $len = strlen( $m[2] );
43 + $result = "s:$len:\"{$m[2]}\";";
44 +
45 + return $result;
46 + }
47 +
48 + /**
49 + * Shuffle array and keep the keys
50 + *
51 + * @param array $array
52 + *
53 + * @return bool
54 + */
55 + public static function shuffle_assoc( &$array ) {
56 + $keys = array_keys( $array );
57 + shuffle( $keys );
58 + $new = array();
59 + foreach ( $keys as $key ) {
60 + $new[ $key ] = $array[ $key ];
61 + }
62 + $array = $new;
63 +
64 + return true;
65 + }
66 +
67 + /**
68 + * MD5 an array.
69 + *
70 + * @param array $array
71 + *
72 + * @return string
73 + */
74 + public static function array_to_md5( $array ) {
75 + settype( $array, 'array' );
76 + ksort( $array );
77 +
78 + return md5( serialize( $array ) );
79 + }
80 +
81 + /**
82 + * Load posts from database into cache by ids
83 + *
84 + * @param array|int $ids
85 + *
86 + * @Todo: tungnx - need to review code - addon h5p v4.0.3 still use
87 + * @deprecated 4.1.6.9
88 + */
89 + public static function cache_posts( $ids ) {
90 + //_deprecated_function( __FUNCTION__, '4.1.6.9' );
91 + }
92 +
93 + /**
94 + * Merge two or more classes into one.
95 + *
96 + * @return array
97 + */
98 + public static function merge_class() {
99 + if ( func_num_args() == 1 ) {
100 + return func_get_arg( 0 );
101 + } elseif ( func_num_args() == 0 ) {
102 + return null;
103 + }
104 +
105 + $classes = array();
106 + foreach ( func_get_args() as $class ) {
107 + if ( is_string( $class ) ) {
108 + $cls = explode( ' ', $class );
109 + $classes = array_merge( $classes, $cls );
110 + } else {
111 + $classes = array_merge( $classes, $class );
112 + }
113 + }
114 +
115 + $classes = array_filter( $classes );
116 + $classes = array_unique( $classes );
117 +
118 + return $classes;
119 + }
120 +
121 + /**
122 + * Sanitize order statuses.
123 + * Add prefix lp- into each status if it is not exists.
124 + *
125 + * @param $statuses
126 + *
127 + * @return array|mixed
128 + */
129 + public static function sanitize_order_status( &$statuses ) {
130 + if ( is_array( $statuses ) ) {
131 + foreach ( $statuses as $k => $status ) {
132 + if ( false === strpos( $status, 'lp-' ) ) {
133 + $statuses[ $k ] = "lp-{$status}";
134 + }
135 + }
136 + } else {
137 + $statuses = preg_split( '#\s+#', $statuses );
138 + self::sanitize_order_status( $statuses );
139 + if ( sizeof( $statuses ) == 1 ) {
140 + $statuses = reset( $statuses );
141 + }
142 + }
143 +
144 + return $statuses;
145 + }
146 +
147 + /**
148 + * Merge two arrays recursive.
149 + *
150 + * @param array $array1
151 + * @param array $array2
152 + *
153 + * @return array
154 + */
155 + public static function array_merge_recursive( &$array1, &$array2 ) {
156 + $merged = $array1;
157 +
158 + if ( is_array( $array1 ) && is_array( $array2 ) ) {
159 + foreach ( $array2 as $key => & $value ) {
160 + if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
161 + $merged[ $key ] = self::array_merge_recursive( $merged[ $key ], $value );
162 + } elseif ( is_numeric( $key ) ) {
163 + if ( ! in_array( $value, $merged ) ) {
164 + $merged[] = $value;
165 + }
166 + } else {
167 + $merged[ $key ] = $value;
168 + }
169 + }
170 + } elseif ( is_array( $array2 ) ) {
171 + $merged = $array2;
172 + }
173 +
174 + return $merged;
175 + }
176 +
177 + /**
178 + * Encode the object to json format.
179 + * Replace number, "false", "true" in couple of quotes with
180 + * original value.
181 + * Example:
182 + * Input: {
183 + * number: "1234",
184 + * true_value: "true",
185 + * false_value: "false"
186 + * }
187 + * Output: {
188 + * number: 1234,
189 + * true_value: true,
190 + * false_value: false
191 + * }
192 + *
193 + * @param array $data
194 + *
195 + * @return false|mixed|string
196 + */
197 + public static function json_encode( $data ) {
198 + $data = wp_json_encode( $data );
199 + $data = preg_replace_callback(
200 + '~:"(([0-9]+)([.,]?)([0-9]?)|true|false)"~',
201 + array(
202 + __CLASS__,
203 + '_valid_json_value',
204 + ),
205 + $data
206 + );
207 +
208 + return $data;
209 + }
210 +
211 + /**
212 + * Callback function for json_encode method "json_encode".
213 + *
214 + * @param array $m
215 + *
216 + * @return string
217 + */
218 + public static function _valid_json_value( $m ) {
219 + return str_replace( array( ':"', '"' ), array( ':', '' ), $m[0] );
220 + }
221 +
222 + /**
223 + * Create LP static page.
224 + *
225 + * @param array $args
226 + * @param string $key_option
227 + *
228 + * @return bool|int|WP_Error
229 + */
230 + public static function create_page( array $args, string $key_option ) {
231 + $page_id = 0;
232 +
233 + try {
234 + /**
235 + * Whitelist of allowed page option keys for security.
236 + * Prevents arbitrary options from being set via the key_option parameter.
237 + */
238 + $key_pages_allow = apply_filters(
239 + 'learn-press/pages/create-allow',
240 + array(
241 + 'learn_press_checkout_page_id',
242 + 'learn_press_profile_page_id',
243 + 'learn_press_courses_page_id',
244 + 'learn_press_instructors_page_id',
245 + 'learn_press_single_instructor_page_id',
246 + 'learn_press_become_a_teacher_page_id',
247 + 'learn_press_term_conditions_page_id',
248 + 'learn_press_exams_page_id', // Must update Addon Exam v4.0.0
249 + 'learn_press_collections_page_id', // Must update Addon Collections v4.0.5
250 + 'learn_press_packages_page_id', // Must update Addon UpSell v4.0.9
251 + )
252 + );
253 +
254 + if ( ! in_array( $key_option, $key_pages_allow ) ) {
255 + throw new Exception( __( 'Invalid key page', 'learnpress' ) );
256 + }
257 +
258 + if ( ! isset( $args['post_title'] ) ) {
259 + throw new Exception( __( 'Missing post title', 'learnpress' ) );
260 + }
261 +
262 + if ( preg_match( '#^learn_press_single_instructor_page_id.*#', $key_option ) ) {
263 + $args['post_content'] = '<!-- wp:shortcode -->[learn_press_single_instructor]<!-- /wp:shortcode -->';
264 + } elseif ( preg_match( '#^learn_press_instructors_page_id.*#', $key_option ) ) {
265 + $args['post_content'] = '<!-- wp:shortcode -->[learn_press_instructors]<!-- /wp:shortcode -->';
266 + } elseif ( preg_match( '#^learn_press_profile_page_id.*#', $key_option ) ) {
267 + $args['post_content'] = '<!-- wp:shortcode -->[learn_press_profile]<!-- /wp:shortcode -->';
268 + }
269 +
270 + $args = array_merge(
271 + [
272 + 'post_title' => '',
273 + 'post_name' => '',
274 + 'post_status' => 'publish',
275 + 'post_type' => 'page',
276 + 'comment_status' => 'closed',
277 + 'post_content' => '',
278 + 'post_author' => get_current_user_id(),
279 + ],
280 + $args
281 + );
282 +
283 + $page_id = wp_insert_post( $args );
284 + if ( ! $page_id ) {
285 + return false;
286 + }
287 +
288 + update_option( $key_option, $page_id );
289 + $lp_settings_cache = new LP_Settings_Cache( true );
290 + $lp_settings_cache->clean_lp_settings();
291 + } catch ( Throwable $e ) {
292 + error_log( __METHOD__ . ': ' . $e->getMessage() );
293 + }
294 +
295 + return $page_id;
296 + }
297 +
298 + /**
299 + * Get the current url
300 + *
301 + * @return string
302 + * @since 3.2.6.8
303 + * @author tungnx
304 + */
305 + public static function getUrlCurrent(): string {
306 + $schema = is_ssl() ? 'https://' : 'http://';
307 + $http_host = LP_Helper::sanitize_params_submitted( urldecode( $_SERVER['HTTP_HOST'] ?? '' ) );
308 + $request_uri = LP_Helper::sanitize_params_submitted( urldecode( $_SERVER['REQUEST_URI'] ?? '' ) );
309 + //$http_host = LP_Helper::sanitize_params_submitted( filter_input( INPUT_SERVER, 'HTTP_HOST' ) ?? '' );
310 + //$request_uri = LP_Helper::sanitize_params_submitted( filter_input( INPUT_SERVER, 'REQUEST_URI' ) ?? '' );
311 +
312 + return untrailingslashit( $schema . $http_host . $request_uri );
313 + }
314 +
315 + /**
316 + * Check request is rest api
317 + *
318 + * @return bool
319 + * @author tungnx
320 + * @since 4.1.6.6
321 + */
322 + public static function isRestApiLP(): bool {
323 + $restPrefix = '/' . rest_get_url_prefix();
324 + return strpos( self::getUrlCurrent(), $restPrefix . '/lp/' ) || strpos( self::getUrlCurrent(), $restPrefix . '/learnpress/' );
325 + }
326 +
327 + /**
328 + * Sanitize string and array
329 + *
330 + * @param array|string $value
331 + * @param string $type_content
332 + * @param bool $unslash Set it is false when you don’t want to remove slashes (unslash) from $value
333 + * for example, in cases involving LaTeX math syntax.
334 + * @return array|string
335 + * @since 3.2.7.1
336 + * @author tungnx
337 + */
338 + public static function sanitize_params_submitted( $value, string $type_content = 'text', $unslash = true ) {
339 + $value = $unslash ? wp_unslash( $value ) : $value;
340 +
341 + if ( is_string( $value ) ) {
342 + switch ( $type_content ) {
343 + case 'html':
344 + $value = Template::sanitize_html_content( $value );
345 + break;
346 + case 'textarea':
347 + $value = sanitize_textarea_field( $value );
348 + break;
349 + case 'key':
350 + $value = sanitize_key( $value );
351 + break;
352 + case 'int':
353 + $value = (int) $value;
354 + break;
355 + case 'float':
356 + $value = (float) $value;
357 + break;
358 + case 'email':
359 + $value = sanitize_email( $value );
360 + break;
361 + default:
362 + if ( is_callable( $type_content ) ) {
363 + $value = call_user_func( $type_content, $value );
364 + } else {
365 + $value = sanitize_text_field( $value );
366 + }
367 + }
368 + } elseif ( is_array( $value ) ) {
369 + foreach ( $value as $k => $v ) {
370 + unset( $value[ $k ] );
371 + $value[ sanitize_text_field( $k ) ] = self::sanitize_params_submitted( $v, $type_content, $unslash );
372 + }
373 + }
374 +
375 + return $value;
376 + }
377 +
378 + public static function db_format_array( array $arr, string $format = '%d' ): string {
379 + $arr_formatted = array_fill( 0, sizeof( $arr ), $format );
380 +
381 + return join( ',', $arr_formatted );
382 + }
383 +
384 + /**
385 + * Calculate percent of progress rows on db
386 + *
387 + * @param int $offset .
388 + * @param int $limit .
389 + * @param int $total_row .
390 + *
391 + * @return float
392 + */
393 + public static function progress_percent( int $offset, int $limit, int $total_row ): float {
394 + if ( $total_row <= 0 ) {
395 + return 0;
396 + }
397 +
398 + $percent = ( $offset + $limit ) * 100 / $total_row;
399 +
400 + $percent = min( $percent, 100 );
401 +
402 + return floatval( number_format( $percent, 2 ) );
403 + }
404 +
405 + /**
406 + * Convert array to string
407 + * Ex: array("publish", "pending") to post_status IN(%s, %s)
408 + *
409 + * @param array $arr
410 + *
411 + * @return string
412 + */
413 + public static function format_query_IN( array $arr ): string {
414 + $format = array_fill( 0, count( $arr ), '%s' );
415 +
416 + return join( ',', $format );
417 + }
418 +
419 + /**
420 + * Get link lp checkout page
421 + * without cache - because some cache(redis) will cache page with user anonymous
422 + */
423 + public static function get_link_no_cache( string $link ): string {
424 + return esc_url_raw( add_query_arg( 'no-cache', uniqid(), $link ) );
425 + }
426 +
427 + /**
428 + * Check string is json
429 + *
430 + * @param string $str
431 + * @param null $associative
432 + *
433 + * @return mixed
434 + * @throws Exception
435 + * @since 4.1.6.4
436 + * @version 1.0.1
437 + */
438 + public static function json_decode( string $str, $associative = null ) {
439 + $obj = json_decode( $str, $associative );
440 + if ( json_last_error() !== JSON_ERROR_NONE ) {
441 + throw new Exception( 'JSON decode: ' . json_last_error_msg() );
442 + }
443 +
444 + return $obj;
445 + }
446 +
447 + /**
448 + * Handle permalink structure for LP
449 + *
450 + * @return string
451 + * @since 4.2.2
452 + * @version 1.0.4
453 + */
454 + public static function handle_lp_permalink_structure( $post_link, $post ) {
455 + if ( false === strpos( $post_link, '%' ) ) {
456 + return $post_link;
457 + }
458 +
459 + $find = [];
460 + $replace = [];
461 + if ( ! empty( $post->post_date_gmt ) ) {
462 + $find = array(
463 + '%year%',
464 + '%monthnum%',
465 + '%day%',
466 + '%hour%',
467 + '%minute%',
468 + '%second%',
469 + '%post_id%',
470 + );
471 +
472 + $time = strtotime( $post->post_date_gmt );
473 +
474 + $replace = array(
475 + date_i18n( 'Y', $time ),
476 + date_i18n( 'm', $time ),
477 + date_i18n( 'd', $time ),
478 + date_i18n( 'H', $time ),
479 + date_i18n( 'i', $time ),
480 + date_i18n( 's', $time ),
481 + $post->ID,
482 + );
483 + }
484 +
485 + if ( strpos( $post_link, '%course_category%' ) && get_post_type( $post ) === LP_COURSE_CPT ) {
486 + // Get the custom taxonomy terms in use by this post
487 + $terms = get_the_terms( $post->ID, 'course_category' );
488 +
489 + if ( ! empty( $terms ) ) {
490 + $terms = wp_list_sort( $terms, 'term_id' );
491 + // order by IDF
492 + $category_object = apply_filters(
493 + 'learn_press_course_post_type_link_course_category',
494 + $terms[0],
495 + $terms,
496 + $post
497 + );
498 + $category_object = get_term( $category_object, 'course_category' );
499 + if ( ! $category_object instanceof WP_Term ) {
500 + return $post_link;
501 + }
502 +
503 + $course_category = $category_object->slug;
504 + $parent = $category_object->parent;
505 + if ( $parent ) {
506 + $ancestors = get_ancestors( $category_object->term_id, 'course_category' );
507 + foreach ( $ancestors as $ancestor ) {
508 + $ancestor_object = get_term( $ancestor, 'course_category' );
509 + if ( $ancestor_object instanceof WP_Term ) {
510 + $course_category = $ancestor_object->slug . '/' . $course_category;
511 + }
512 + }
513 + }
514 + } else {
515 + // If no terms are assigned to this post, use a string instead (can't leave the placeholder there)
516 + $course_category = _x( 'uncategorized', 'slug', 'learnpress' );
517 + }
518 +
519 + $find[] = '%course_category%';
520 + $replace[] = urldecode( $course_category );
521 + }
522 +
523 + return apply_filters(
524 + 'learn-press/single-course/permalink',
525 + str_replace( $find, $replace, $post_link ),
526 + $post
527 + );
528 + }
529 +
530 + /**
531 + * Print variable script inline script tag.
532 + * If $name_variable_script is empty,
533 + * the script will be print as json with set $tag_args['type'] = application/json.
534 + *
535 + * @param string $name_variable_script
536 + * @param array $data
537 + * @param array $tag_args as ['type' => 'text/javascript', 'id' => '']
538 + *
539 + * @return void
540 + * @version 1.0.2
541 + * @since 4.2.5.5
542 + */
543 + public static function print_inline_script_tag( string $name_variable_script, array $data, array $tag_args = [] ) {
544 + /**
545 + * Comment block code reason by security
546 + * wp_json_encode() already produces a valid JavaScript string,
547 + * and there is no reason to decode HTML entities inside data that will be re-inserted with insertAdjacentHTML
548 + * @comment since 4.4.8
549 + */
550 + /*foreach ( $data as $key => $value ) {
551 + if ( ! is_scalar( $value ) ) {
552 + continue;
553 + }
554 +
555 + $data[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
556 + }*/
557 +
558 + $data_json = wp_json_encode( $data );
559 + $script = '';
560 + if ( ! empty( $name_variable_script ) ) {
561 + $script = "var {$name_variable_script} = {$data_json};";
562 + } elseif ( isset( $tag_args['type'] ) && $tag_args['type'] === 'application/json' ) {
563 + $script = $data_json;
564 + }
565 + wp_print_inline_script_tag( $script, $tag_args );
566 + }
567 +
568 + /**
569 + * Get translation string single/plural
570 + *
571 + * @param float $number
572 + * @param string $string_value
573 + * @param bool $include_number
574 + *
575 + * @return string
576 + * @since 4.2.7.4
577 + * @version 1.0.0
578 + */
579 + public static function get_i18n_string_plural( float $number, string $string_value = '', bool $include_number = true ): string {
580 + switch ( $string_value ) {
581 + case LP_COURSE_CPT:
582 + $plural = sprintf(
583 + _n( 'Course', 'Courses', $number, 'learnpress' ),
584 + $number
585 + );
586 + break;
587 + case LP_LESSON_CPT:
588 + $plural = sprintf(
589 + _n( 'Lesson', 'Lessons', $number, 'learnpress' ),
590 + $number
591 + );
592 + break;
593 + case LP_QUIZ_CPT:
594 + $plural = sprintf(
595 + _n( 'Quiz', 'Quizzes', $number, 'learnpress' ),
596 + $number
597 + );
598 + break;
599 + case LP_QUESTION_CPT:
600 + $plural = sprintf(
601 + _n( 'Question', 'Questions', $number, 'learnpress' ),
602 + $number
603 + );
604 + break;
605 + case 'lp_assignment':
606 + $plural = sprintf(
607 + _n( 'Assignment', 'Assignments', $number, 'learnpress' ),
608 + $number
609 + );
610 + break;
611 + case 'lp_h5p':
612 + $plural = sprintf(
613 + _n( 'H5P', 'H5Ps', $number, 'learnpress' ),
614 + $number
615 + );
616 + break;
617 + default:
618 + $plural = $string_value;
619 + break;
620 + }
621 +
622 + if ( $include_number ) {
623 + $plural = sprintf( '%s %s', $number, $plural );
624 + }
625 +
626 + return apply_filters( 'learn-press/i18n/plural', $plural, $number, $string_value, $include_number );
627 + }
628 +
629 + /**
630 + * Get client ip address
631 + *
632 + * @return mixed|string
633 + * @since 4.2.7.9
634 + * @version 1.0.0
635 + */
636 + public static function get_client_ip(): string {
637 + $ipaddress = 'ip-unknown';
638 + if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
639 + $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
640 + } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
641 + $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
642 + } elseif ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) {
643 + $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
644 + } elseif ( isset( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
645 + $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
646 + } elseif ( isset( $_SERVER['HTTP_FORWARDED'] ) ) {
647 + $ipaddress = $_SERVER['HTTP_FORWARDED'];
648 + } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
649 + $ipaddress = $_SERVER['REMOTE_ADDR'];
650 + }
651 +
652 + $ipaddress = (string) $ipaddress;
653 + $ipaddress = 'gip-' . $ipaddress;
654 +
655 + return $ipaddress;
656 + }
657 +}