PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
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 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
← All changes | inc/class-lp-helper.php +654 -624 4.3.84.4.8 View file →
@@ -1,624 +1,654 @@
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 ) ) {
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 + /**
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 + default:
359 + if ( is_callable( $type_content ) ) {
360 + $value = call_user_func( $type_content, $value );
361 + } else {
362 + $value = sanitize_text_field( $value );
363 + }
364 + }
365 + } elseif ( is_array( $value ) ) {
366 + foreach ( $value as $k => $v ) {
367 + unset( $value[ $k ] );
368 + $value[ sanitize_text_field( $k ) ] = self::sanitize_params_submitted( $v, $type_content, $unslash );
369 + }
370 + }
371 +
372 + return $value;
373 + }
374 +
375 + public static function db_format_array( array $arr, string $format = '%d' ): string {
376 + $arr_formatted = array_fill( 0, sizeof( $arr ), $format );
377 +
378 + return join( ',', $arr_formatted );
379 + }
380 +
381 + /**
382 + * Calculate percent of progress rows on db
383 + *
384 + * @param int $offset .
385 + * @param int $limit .
386 + * @param int $total_row .
387 + *
388 + * @return float
389 + */
390 + public static function progress_percent( int $offset, int $limit, int $total_row ): float {
391 + if ( $total_row <= 0 ) {
392 + return 0;
393 + }
394 +
395 + $percent = ( $offset + $limit ) * 100 / $total_row;
396 +
397 + $percent = min( $percent, 100 );
398 +
399 + return floatval( number_format( $percent, 2 ) );
400 + }
401 +
402 + /**
403 + * Convert array to string
404 + * Ex: array("publish", "pending") to post_status IN(%s, %s)
405 + *
406 + * @param array $arr
407 + *
408 + * @return string
409 + */
410 + public static function format_query_IN( array $arr ): string {
411 + $format = array_fill( 0, count( $arr ), '%s' );
412 +
413 + return join( ',', $format );
414 + }
415 +
416 + /**
417 + * Get link lp checkout page
418 + * without cache - because some cache(redis) will cache page with user anonymous
419 + */
420 + public static function get_link_no_cache( string $link ): string {
421 + return esc_url_raw( add_query_arg( 'no-cache', uniqid(), $link ) );
422 + }
423 +
424 + /**
425 + * Check string is json
426 + *
427 + * @param string $str
428 + * @param null $associative
429 + *
430 + * @return mixed
431 + * @throws Exception
432 + * @since 4.1.6.4
433 + * @version 1.0.1
434 + */
435 + public static function json_decode( string $str, $associative = null ) {
436 + $obj = json_decode( $str, $associative );
437 + if ( json_last_error() !== JSON_ERROR_NONE ) {
438 + throw new Exception( 'JSON decode: ' . json_last_error_msg() );
439 + }
440 +
441 + return $obj;
442 + }
443 +
444 + /**
445 + * Handle permalink structure for LP
446 + *
447 + * @return string
448 + * @since 4.2.2
449 + * @version 1.0.4
450 + */
451 + public static function handle_lp_permalink_structure( $post_link, $post ) {
452 + if ( false === strpos( $post_link, '%' ) ) {
453 + return $post_link;
454 + }
455 +
456 + $find = [];
457 + $replace = [];
458 + if ( ! empty( $post->post_date_gmt ) ) {
459 + $find = array(
460 + '%year%',
461 + '%monthnum%',
462 + '%day%',
463 + '%hour%',
464 + '%minute%',
465 + '%second%',
466 + '%post_id%',
467 + );
468 +
469 + $time = strtotime( $post->post_date_gmt );
470 +
471 + $replace = array(
472 + date_i18n( 'Y', $time ),
473 + date_i18n( 'm', $time ),
474 + date_i18n( 'd', $time ),
475 + date_i18n( 'H', $time ),
476 + date_i18n( 'i', $time ),
477 + date_i18n( 's', $time ),
478 + $post->ID,
479 + );
480 + }
481 +
482 + if ( strpos( $post_link, '%course_category%' ) && get_post_type( $post ) === LP_COURSE_CPT ) {
483 + // Get the custom taxonomy terms in use by this post
484 + $terms = get_the_terms( $post->ID, 'course_category' );
485 +
486 + if ( ! empty( $terms ) ) {
487 + $terms = wp_list_sort( $terms, 'term_id' );
488 + // order by IDF
489 + $category_object = apply_filters(
490 + 'learn_press_course_post_type_link_course_category',
491 + $terms[0],
492 + $terms,
493 + $post
494 + );
495 + $category_object = get_term( $category_object, 'course_category' );
496 + if ( ! $category_object instanceof WP_Term ) {
497 + return $post_link;
498 + }
499 +
500 + $course_category = $category_object->slug;
501 + $parent = $category_object->parent;
502 + if ( $parent ) {
503 + $ancestors = get_ancestors( $category_object->term_id, 'course_category' );
504 + foreach ( $ancestors as $ancestor ) {
505 + $ancestor_object = get_term( $ancestor, 'course_category' );
506 + if ( $ancestor_object instanceof WP_Term ) {
507 + $course_category = $ancestor_object->slug . '/' . $course_category;
508 + }
509 + }
510 + }
511 + } else {
512 + // If no terms are assigned to this post, use a string instead (can't leave the placeholder there)
513 + $course_category = _x( 'uncategorized', 'slug', 'learnpress' );
514 + }
515 +
516 + $find[] = '%course_category%';
517 + $replace[] = urldecode( $course_category );
518 + }
519 +
520 + return apply_filters(
521 + 'learn-press/single-course/permalink',
522 + str_replace( $find, $replace, $post_link ),
523 + $post
524 + );
525 + }
526 +
527 + /**
528 + * Print variable script inline script tag.
529 + * If $name_variable_script is empty,
530 + * the script will be print as json with set $tag_args['type'] = application/json.
531 + *
532 + * @param string $name_variable_script
533 + * @param array $data
534 + * @param array $tag_args as ['type' => 'text/javascript', 'id' => '']
535 + *
536 + * @return void
537 + * @version 1.0.2
538 + * @since 4.2.5.5
539 + */
540 + public static function print_inline_script_tag( string $name_variable_script, array $data, array $tag_args = [] ) {
541 + /**
542 + * Comment block code reason by security
543 + * wp_json_encode() already produces a valid JavaScript string,
544 + * and there is no reason to decode HTML entities inside data that will be re-inserted with insertAdjacentHTML
545 + * @comment since 4.4.8
546 + */
547 + /*foreach ( $data as $key => $value ) {
548 + if ( ! is_scalar( $value ) ) {
549 + continue;
550 + }
551 +
552 + $data[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
553 + }*/
554 +
555 + $data_json = wp_json_encode( $data );
556 + $script = '';
557 + if ( ! empty( $name_variable_script ) ) {
558 + $script = "var {$name_variable_script} = {$data_json};";
559 + } elseif ( isset( $tag_args['type'] ) && $tag_args['type'] === 'application/json' ) {
560 + $script = $data_json;
561 + }
562 + wp_print_inline_script_tag( $script, $tag_args );
563 + }
564 +
565 + /**
566 + * Get translation string single/plural
567 + *
568 + * @param float $number
569 + * @param string $string_value
570 + * @param bool $include_number
571 + *
572 + * @return string
573 + * @since 4.2.7.4
574 + * @version 1.0.0
575 + */
576 + public static function get_i18n_string_plural( float $number, string $string_value = '', bool $include_number = true ): string {
577 + switch ( $string_value ) {
578 + case LP_COURSE_CPT:
579 + $plural = sprintf(
580 + _n( 'Course', 'Courses', $number, 'learnpress' ),
581 + $number
582 + );
583 + break;
584 + case LP_LESSON_CPT:
585 + $plural = sprintf(
586 + _n( 'Lesson', 'Lessons', $number, 'learnpress' ),
587 + $number
588 + );
589 + break;
590 + case LP_QUIZ_CPT:
591 + $plural = sprintf(
592 + _n( 'Quiz', 'Quizzes', $number, 'learnpress' ),
593 + $number
594 + );
595 + break;
596 + case LP_QUESTION_CPT:
597 + $plural = sprintf(
598 + _n( 'Question', 'Questions', $number, 'learnpress' ),
599 + $number
600 + );
601 + break;
602 + case 'lp_assignment':
603 + $plural = sprintf(
604 + _n( 'Assignment', 'Assignments', $number, 'learnpress' ),
605 + $number
606 + );
607 + break;
608 + case 'lp_h5p':
609 + $plural = sprintf(
610 + _n( 'H5P', 'H5Ps', $number, 'learnpress' ),
611 + $number
612 + );
613 + break;
614 + default:
615 + $plural = $string_value;
616 + break;
617 + }
618 +
619 + if ( $include_number ) {
620 + $plural = sprintf( '%s %s', $number, $plural );
621 + }
622 +
623 + return apply_filters( 'learn-press/i18n/plural', $plural, $number, $string_value, $include_number );
624 + }
625 +
626 + /**
627 + * Get client ip address
628 + *
629 + * @return mixed|string
630 + * @since 4.2.7.9
631 + * @version 1.0.0
632 + */
633 + public static function get_client_ip(): string {
634 + $ipaddress = 'ip-unknown';
635 + if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
636 + $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
637 + } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
638 + $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
639 + } elseif ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) {
640 + $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
641 + } elseif ( isset( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
642 + $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
643 + } elseif ( isset( $_SERVER['HTTP_FORWARDED'] ) ) {
644 + $ipaddress = $_SERVER['HTTP_FORWARDED'];
645 + } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
646 + $ipaddress = $_SERVER['REMOTE_ADDR'];
647 + }
648 +
649 + $ipaddress = (string) $ipaddress;
650 + $ipaddress = 'gip-' . $ipaddress;
651 +
652 + return $ipaddress;
653 + }
654 +}