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 +308 -227 4.2.04.4.8 View file →
@@ -2,8 +2,11 @@
2 2
3 3 /**
4 4 * Class LP_Helper
5 5 */
6 +
7 +use LearnPress\Helpers\Template;
8 +
6 9 defined( 'ABSPATH' ) || exit;
7 10
8 11 class LP_Helper {
9 12
@@ -61,22 +64,8 @@
61 64 return true;
62 65 }
63 66
64 67 /**
65 - * Sanitize array by removing empty and/or duplicating values.
66 - *
67 - * @param array $array
68 - *
69 - * @return array
70 - */
71 - public static function sanitize_array( $array ) {
72 - $array = array_filter( $array );
73 - $array = array_unique( $array );
74 -
75 - return $array;
76 - }
77 -
78 - /**
79 68 * MD5 an array.
80 69 *
81 70 * @param array $array
82 71 *
@@ -92,9 +81,10 @@
92 81 /**
93 82 * Load posts from database into cache by ids
94 83 *
95 84 * @param array|int $ids
96 - * @Todo: tungnx - need to review code
85 + *
86 + * @Todo: tungnx - need to review code - addon h5p v4.0.3 still use
97 87 * @deprecated 4.1.6.9
98 88 */
99 89 public static function cache_posts( $ids ) {
100 90 //_deprecated_function( __FUNCTION__, '4.1.6.9' );
@@ -100,38 +90,8 @@
100 90 //_deprecated_function( __FUNCTION__, '4.1.6.9' );
101 91 }
102 92
103 93 /**
104 - * Sort an array by a field.
105 - * Having some issue with default PHP usort function.
106 - *
107 - * @param array $array .
108 - * @param string $field .
109 - * @param int $default .
110 - */
111 - public static function sort_by_priority( &$array, $field = 'priority', $default = 10 ) {
112 - foreach ( $array as $k => $item ) {
113 - if ( ! array_key_exists( $field, $item ) ) {
114 - $array[ $k ][ $field ] = $default;
115 - }
116 - }
117 -
118 - $priority = array_unique( wp_list_pluck( $array, $field ) );
119 - sort( $priority );
120 - $priority = array_fill_keys( $priority, array() );
121 -
122 - foreach ( $array as $k => $item ) {
123 - $priority[ $item[ $field ] ][] = $item;
124 - }
125 -
126 - $sorted = array();
127 - foreach ( $priority as $item ) {
128 - $sorted = array_merge( $sorted, $item );
129 - }
130 - $array = $sorted;
131 - }
132 -
133 - /**
134 94 * Merge two or more classes into one.
135 95 *
136 96 * @return array
137 97 */
@@ -261,139 +221,79 @@
261 221
262 222 /**
263 223 * Create LP static page.
264 224 *
265 - * @param string $name
266 - * @param string $assign_to - Optional. Assign to LP page after creating successful.
225 + * @param array $args
226 + * @param string $key_option
267 227 *
268 228 * @return bool|int|WP_Error
269 229 */
270 - public static function create_page( $name, $assign_to = '' ) {
271 - $args = array(
272 - 'post_type' => 'page',
273 - 'post_title' => $name,
274 - 'post_status' => 'publish',
275 - );
230 + public static function create_page( array $args, string $key_option ) {
231 + $page_id = 0;
276 232
277 - $page_id = wp_insert_post( $args );
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 + );
278 253
279 - if ( ! $page_id ) {
280 - return false;
281 - }
254 + if ( ! in_array( $key_option, $key_pages_allow ) ) {
255 + throw new Exception( __( 'Invalid key page', 'learnpress' ) );
256 + }
282 257
283 - update_post_meta( $page_id, '_lp_page', 'yes' );
284 -
285 - if ( $assign_to ) {
286 - $pages = learn_press_static_pages();
287 -
288 - if ( ! empty( $pages[ $assign_to ] ) ) {
289 - update_option( "learn_press_{$assign_to}_page_id", $page_id );
290 -
291 - // Update cache
292 - $page_ids = learn_press_static_page_ids();
293 - $page_ids[ $assign_to ] = $page_id;
294 - LP_Object_Cache::set( 'static-page-ids', $page_ids, 'learnpress' );
258 + if ( ! isset( $args['post_title'] ) ) {
259 + throw new Exception( __( 'Missing post title', 'learnpress' ) );
295 260 }
296 - }
297 261
298 - return $page_id;
299 - }
300 -
301 - /**
302 - * Wrap function ksort of PHP itself and support recursive.
303 - *
304 - * @param array $array
305 - * @param int $sort_flags
306 - *
307 - * @return bool
308 - * @since 3.3.0
309 - */
310 - public static function ksort( &$array, $sort_flags = SORT_REGULAR ) {
311 - if ( ! is_array( $array ) ) {
312 - return false;
313 - }
314 -
315 - ksort( $array, $sort_flags );
316 -
317 - foreach ( $array as &$arr ) {
318 - self::ksort( $arr, $sort_flags );
319 - }
320 -
321 - return true;
322 - }
323 -
324 - /**
325 - * Return new array/object with the keys exists in list of props.
326 - *
327 - * @param array|string $props
328 - * @param array|object $obj
329 - *
330 - * @return array|object
331 - */
332 - public function pick( $props, $obj ) {
333 - $is_array = is_array( $obj );
334 - $new_array = array();
335 - settype( $props, 'array' );
336 -
337 - foreach ( $props as $prop ) {
338 - if ( $is_array && array_key_exists( $prop, $obj ) ) {
339 - $new_array[ $prop ] = $obj[ $prop ];
340 - } elseif ( ! $is_array && property_exists( $obj, $prop ) ) {
341 - $new_array[ $prop ] = $obj->{$prop};
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 -->';
342 268 }
343 - }
344 269
345 - return $is_array ? $new_array : (object) $new_array;
346 - }
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 + );
347 282
348 - public static function list_pluck( $list, $field, $index_key = null ) {
349 - $newlist = array();
350 -
351 - if ( ! $index_key ) {
352 - /*
353 - * This is simple. Could at some point wrap array_column()
354 - * if we knew we had an array of arrays.
355 - */
356 - foreach ( $list as $key => $value ) {
357 - if ( is_callable( array( $value, $field ) ) ) {
358 - $newlist[ $key ] = call_user_func( array( $value, $field ) );
359 - } elseif ( is_object( $value ) ) {
360 - $newlist[ $key ] = $value->$field;
361 - } else {
362 - $newlist[ $key ] = $value[ $field ];
363 - }
283 + $page_id = wp_insert_post( $args );
284 + if ( ! $page_id ) {
285 + return false;
364 286 }
365 287
366 - return $newlist;
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() );
367 293 }
368 294
369 - /*
370 - * When index_key is not set for a particular item, push the value
371 - * to the end of the stack. This is how array_column() behaves.
372 - */
373 - foreach ( $list as $value ) {
374 - if ( is_callable( array( $value, $field ) ) ) {
375 - if ( isset( $value->$index_key ) ) {
376 - $newlist[ $value->$index_key ] = call_user_func( array( $value, $field ) );
377 - } else {
378 - $newlist[] = call_user_func( array( $value, $field ) );
379 - }
380 - } elseif ( is_object( $value ) ) {
381 - if ( isset( $value->$index_key ) ) {
382 - $newlist[ $value->$index_key ] = $value->$field;
383 - } else {
384 - $newlist[] = $value->$field;
385 - }
386 - } else {
387 - if ( isset( $value[ $index_key ] ) ) {
388 - $newlist[ $value[ $index_key ] ] = $value[ $field ];
389 - } else {
390 - $newlist[] = $value[ $field ];
391 - }
392 - }
393 - }
394 -
395 - return $newlist;
295 + return $page_id;
396 296 }
397 297
398 298 /**
399 299 * Get the current url
@@ -403,23 +303,26 @@
403 303 * @author tungnx
404 304 */
405 305 public static function getUrlCurrent(): string {
406 306 $schema = is_ssl() ? 'https://' : 'http://';
407 - $http_host = LP_Helper::sanitize_params_submitted( filter_input( INPUT_SERVER, 'HTTP_HOST' ) ?? '' );
408 - $request_uri = LP_Helper::sanitize_params_submitted( filter_input( INPUT_SERVER, 'REQUEST_URI' ) ?? '' );
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' ) ?? '' );
409 311
410 - return $schema . untrailingslashit( $http_host . $request_uri );
312 + return untrailingslashit( $schema . $http_host . $request_uri );
411 313 }
412 314
413 315 /**
414 316 * Check request is rest api
415 317 *
318 + * @return bool
319 + * @author tungnx
416 320 * @since 4.1.6.6
417 - * @author tungnx
418 - * @return bool|int
419 321 */
420 - public static function isRestApiLP() {
421 - return strpos( self::getUrlCurrent(), '/wp-json/lp/' );
322 + public static function isRestApiLP(): bool {
323 + $restPrefix = '/' . rest_get_url_prefix();
324 + return strpos( self::getUrlCurrent(), $restPrefix . '/lp/' ) || strpos( self::getUrlCurrent(), $restPrefix . '/learnpress/' );
422 325 }
423 326
424 327 /**
425 328 * Sanitize string and array
@@ -424,21 +327,22 @@
424 327 /**
425 328 * Sanitize string and array
426 329 *
427 330 * @param array|string $value
428 - * @param string $type_content
429 - *
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.
430 334 * @return array|string
431 335 * @since 3.2.7.1
432 336 * @author tungnx
433 337 */
434 - public static function sanitize_params_submitted( $value, string $type_content = 'text' ) {
435 - $value = wp_unslash( $value );
338 + public static function sanitize_params_submitted( $value, string $type_content = 'text', $unslash = true ) {
339 + $value = $unslash ? wp_unslash( $value ) : $value;
436 340
437 341 if ( is_string( $value ) ) {
438 342 switch ( $type_content ) {
439 343 case 'html':
440 - $value = wp_kses_post( $value );
344 + $value = Template::sanitize_html_content( $value );
441 345 break;
442 346 case 'textarea':
443 347 $value = sanitize_textarea_field( $value );
444 348 break;
@@ -451,13 +355,18 @@
451 355 case 'float':
452 356 $value = (float) $value;
453 357 break;
454 358 default:
455 - $value = sanitize_text_field( $value );
359 + if ( is_callable( $type_content ) ) {
360 + $value = call_user_func( $type_content, $value );
361 + } else {
362 + $value = sanitize_text_field( $value );
363 + }
456 364 }
457 365 } elseif ( is_array( $value ) ) {
458 366 foreach ( $value as $k => $v ) {
459 - $value[ $k ] = self::sanitize_params_submitted( $v, $type_content );
367 + unset( $value[ $k ] );
368 + $value[ sanitize_text_field( $k ) ] = self::sanitize_params_submitted( $v, $type_content, $unslash );
460 369 }
461 370 }
462 371
463 372 return $value;
@@ -462,55 +371,8 @@
462 371
463 372 return $value;
464 373 }
465 374
466 - /**
467 - * Get wp file system
468 - *
469 - * @return mixed
470 - */
471 - public static function get_wp_filesystem() {
472 - global $wp_filesystem;
473 -
474 - if ( empty( $wp_filesystem ) ) {
475 - require_once( ABSPATH . '/wp-admin/includes/file.php' );
476 - WP_Filesystem();
477 - }
478 -
479 - return $wp_filesystem;
480 - }
481 -
482 - /**
483 - * Wrap function $wpdb->prepare(...) to support arguments as
484 - * array.
485 - *
486 - * @param string $query
487 - * @param array|mixed $args
488 - *
489 - * @return string
490 - * @example
491 - *
492 - * $this->prepare($sql, $one, $two, array($three, $four, $file))
493 - * => $wpdb->prepare($sql, $one, $two, $three, $four, $file)
494 - */
495 - public static function prepare( $query, $args ) {
496 - global $wpdb;
497 -
498 - $args = func_get_args();
499 - array_shift( $args );
500 - $new_args = array();
501 -
502 - foreach ( $args as $arg ) {
503 - if ( is_array( $arg ) ) {
504 - $new_args = array_merge( $new_args, $arg );
505 - } else {
506 - $new_args[] = $arg;
507 - }
508 - }
509 -
510 - return $wpdb->prepare( $query, $new_args );
511 - }
512 -
513 375 public static function db_format_array( array $arr, string $format = '%d' ): string {
514 376 $arr_formatted = array_fill( 0, sizeof( $arr ), $format );
515 377
516 378 return join( ',', $arr_formatted );
@@ -531,9 +393,9 @@
531 393 }
532 394
533 395 $percent = ( $offset + $limit ) * 100 / $total_row;
534 396
535 - $percent = $percent > 100 ? 100 : $percent;
397 + $percent = min( $percent, 100 );
536 398
537 399 return floatval( number_format( $percent, 2 ) );
538 400 }
539 401
@@ -562,12 +424,231 @@
562 424 /**
563 425 * Check string is json
564 426 *
565 427 * @param string $str
428 + * @param null $associative
566 429 *
567 - * @return bool
430 + * @return mixed
431 + * @throws Exception
432 + * @since 4.1.6.4
433 + * @version 1.0.1
568 434 */
569 - public static function is_json( string $str ): bool {
570 - json_decode( $str );
571 - return json_last_error() === JSON_ERROR_NONE;
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;
572 653 }
573 654 }