PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.5
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.5
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
learnpress / inc / class-lp-helper.php

class-lp-helper.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.5, at inc/class-lp-helper.php

649 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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.1
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 foreach ( $data as $key => $value ) {
542 if ( ! is_scalar( $value ) ) {
543 continue;
544 }
545
546 $data[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
547 }
548
549 $data_json = wp_json_encode( $data );
550 $script = '';
551 if ( ! empty( $name_variable_script ) ) {
552 $script = "var {$name_variable_script} = {$data_json};";
553 } elseif ( isset( $tag_args['type'] ) && $tag_args['type'] === 'application/json' ) {
554 $script = $data_json;
555 }
556 wp_print_inline_script_tag( $script, $tag_args );
557 }
558
559 /**
560 * Get translation string single/plural
561 *
562 * @param float $number
563 * @param string $string_value
564 * @param bool $include_number
565 *
566 * @return string
567 * @since 4.2.7.4
568 * @version 1.0.0
569 */
570 public static function get_i18n_string_plural( float $number, string $string_value = '', bool $include_number = true ): string {
571 switch ( $string_value ) {
572 case LP_COURSE_CPT:
573 $plural = sprintf(
574 _n( 'Course', 'Courses', $number, 'learnpress' ),
575 $number
576 );
577 break;
578 case LP_LESSON_CPT:
579 $plural = sprintf(
580 _n( 'Lesson', 'Lessons', $number, 'learnpress' ),
581 $number
582 );
583 break;
584 case LP_QUIZ_CPT:
585 $plural = sprintf(
586 _n( 'Quiz', 'Quizzes', $number, 'learnpress' ),
587 $number
588 );
589 break;
590 case LP_QUESTION_CPT:
591 $plural = sprintf(
592 _n( 'Question', 'Questions', $number, 'learnpress' ),
593 $number
594 );
595 break;
596 case 'lp_assignment':
597 $plural = sprintf(
598 _n( 'Assignment', 'Assignments', $number, 'learnpress' ),
599 $number
600 );
601 break;
602 case 'lp_h5p':
603 $plural = sprintf(
604 _n( 'H5P', 'H5Ps', $number, 'learnpress' ),
605 $number
606 );
607 break;
608 default:
609 $plural = $string_value;
610 break;
611 }
612
613 if ( $include_number ) {
614 $plural = sprintf( '%s %s', $number, $plural );
615 }
616
617 return apply_filters( 'learn-press/i18n/plural', $plural, $number, $string_value, $include_number );
618 }
619
620 /**
621 * Get client ip address
622 *
623 * @return mixed|string
624 * @since 4.2.7.9
625 * @version 1.0.0
626 */
627 public static function get_client_ip(): string {
628 $ipaddress = 'ip-unknown';
629 if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
630 $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
631 } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
632 $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
633 } elseif ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) {
634 $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
635 } elseif ( isset( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
636 $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
637 } elseif ( isset( $_SERVER['HTTP_FORWARDED'] ) ) {
638 $ipaddress = $_SERVER['HTTP_FORWARDED'];
639 } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
640 $ipaddress = $_SERVER['REMOTE_ADDR'];
641 }
642
643 $ipaddress = (string) $ipaddress;
644 $ipaddress = 'gip-' . $ipaddress;
645
646 return $ipaddress;
647 }
648 }
649