diff --git a/assets/images/images-v2/certificate-step-bg.jpg b/assets/images/images-v2/certificate-step-bg.jpg deleted file mode 100644 index d56f3393ae..0000000000 Binary files a/assets/images/images-v2/certificate-step-bg.jpg and /dev/null differ diff --git a/assets/images/images-v2/email-heading.svg b/assets/images/images-v2/email-heading.svg deleted file mode 100644 index 397b0f4425..0000000000 --- a/assets/images/images-v2/email-heading.svg +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/assets/images/images-v2/email.png b/assets/images/images-v2/email.png deleted file mode 100644 index ce2ad2d0ad..0000000000 Binary files a/assets/images/images-v2/email.png and /dev/null differ diff --git a/assets/images/images-v2/signature-demo.svg b/assets/images/images-v2/signature-demo.svg deleted file mode 100644 index 0e99f2b387..0000000000 --- a/assets/images/images-v2/signature-demo.svg +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/assets/images/instructor-layout/pp-cp.jpg b/assets/images/instructor-layout/pp-cp.jpg deleted file mode 100644 index 0ec3f908d7..0000000000 Binary files a/assets/images/instructor-layout/pp-cp.jpg and /dev/null differ diff --git a/assets/images/instructor-layout/pp-left-full.jpg b/assets/images/instructor-layout/pp-left-full.jpg deleted file mode 100644 index d1c9e2f06d..0000000000 Binary files a/assets/images/instructor-layout/pp-left-full.jpg and /dev/null differ diff --git a/assets/images/instructor-layout/pp-left-middle.jpg b/assets/images/instructor-layout/pp-left-middle.jpg deleted file mode 100644 index 18ee6d5eea..0000000000 Binary files a/assets/images/instructor-layout/pp-left-middle.jpg and /dev/null differ diff --git a/assets/images/instructor-layout/pp-top-full.jpg b/assets/images/instructor-layout/pp-top-full.jpg deleted file mode 100644 index 667ba240e3..0000000000 Binary files a/assets/images/instructor-layout/pp-top-full.jpg and /dev/null differ diff --git a/assets/images/instructor-layout/pp-top-left.jpg b/assets/images/instructor-layout/pp-top-left.jpg deleted file mode 100644 index 439d6e091e..0000000000 Binary files a/assets/images/instructor-layout/pp-top-left.jpg and /dev/null differ diff --git a/assets/images/public-profile/no-cp.jpg b/assets/images/public-profile/no-cp.jpg deleted file mode 100644 index db3ef851cc..0000000000 Binary files a/assets/images/public-profile/no-cp.jpg and /dev/null differ diff --git a/assets/images/public-profile/pp-circle.jpg b/assets/images/public-profile/pp-circle.jpg deleted file mode 100644 index ed129b75d2..0000000000 Binary files a/assets/images/public-profile/pp-circle.jpg and /dev/null differ diff --git a/assets/images/public-profile/pp-rectangle.jpg b/assets/images/public-profile/pp-rectangle.jpg deleted file mode 100644 index b5fe4153de..0000000000 Binary files a/assets/images/public-profile/pp-rectangle.jpg and /dev/null differ diff --git a/assets/images/whats-new.png b/assets/images/whats-new.png deleted file mode 100644 index bc865f4c99..0000000000 Binary files a/assets/images/whats-new.png and /dev/null differ diff --git a/assets/react/admin-dashboard/quiz-attempts.js b/assets/react/admin-dashboard/quiz-attempts.js index a1d28a0b47..a9b743e99e 100644 --- a/assets/react/admin-dashboard/quiz-attempts.js +++ b/assets/react/admin-dashboard/quiz-attempts.js @@ -17,10 +17,12 @@ document.addEventListener('DOMContentLoaded', async function() { const response = await post.json(); if (response.success && response.data) { const navItems = document.querySelectorAll('.tutor-nav-item .tutor-ml-4'); - let i = 0; - for (let [key, value] of Object.entries(response.data)) { - navItems[i].innerHTML = `(${value})`; - i++; + if (navItems.length) { + let i = 0; + for (let [key, value] of Object.entries(response.data)) { + navItems[i].innerHTML = `(${value})`; + i++; + } } } } diff --git a/assets/react/lib/tutor.js b/assets/react/lib/tutor.js index 91f8dfb079..88303c2fcb 100644 --- a/assets/react/lib/tutor.js +++ b/assets/react/lib/tutor.js @@ -342,8 +342,15 @@ jQuery(document).ready(function($) { beforeSend: function() { $that.find('button').attr('disabled', 'disabled').addClass('is-loading'); }, - success: function() { - tutor_toast(__('Success', 'tutor'), $that.data('toast_success_message'), 'success'); + success: function(response) { + if (response.success) { + tutor_toast(__('Success', 'tutor'), $that.data('toast_success_message'), 'success'); + } else { + tutor_toast(__('Error!', 'tutor'), response.data, 'error'); + } + }, + error: function(response) { + tutor_toast(__('Error!', 'tutor'), response.statusText, 'error'); }, complete: function() { $that.find('button').removeAttr('disabled').removeClass('is-loading'); diff --git a/assets/scss/front/_theme.scss b/assets/scss/front/_theme.scss index b00f975d30..70974f348f 100644 --- a/assets/scss/front/_theme.scss +++ b/assets/scss/front/_theme.scss @@ -7,6 +7,10 @@ body { box-sizing: border-box; } } + + .media-modal * { + box-sizing: content-box; + } } //common and reset css diff --git a/classes/Q_and_A.php b/classes/Q_and_A.php index 50ef94cab3..037ee41625 100644 --- a/classes/Q_and_A.php +++ b/classes/Q_and_A.php @@ -51,6 +51,26 @@ public function __construct() { add_action( 'wp_ajax_tutor_q_and_a_load_more', __CLASS__ . '::load_more' ); } + /** + * Check user has access to QnA. + * + * @since 2.6.1 + * + * @param int $user_id user id. + * @param int $course_id course id. + * + * @return boolean + */ + public static function has_qna_access( $user_id, $course_id ) { + $is_public_course = Course_List::is_public( $course_id ); + + $has_access = $is_public_course + || User::is_admin() + || tutor_utils()->is_instructor_of_this_course( $user_id, $course_id ) + || tutor_utils()->is_enrolled( $course_id ); + return $has_access; + } + /** * Undocumented function * @@ -60,8 +80,17 @@ public function __construct() { */ public function tutor_qna_create_update() { tutor_utils()->checking_nonce(); + + $user_id = get_current_user_id(); + $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); + + if ( ! $this->has_qna_access( $user_id, $course_id ) ) { + wp_send_json_error( array( 'message' => tutor_utils()->error_message() ) ); + } + global $wpdb; - $qna_text = Input::post( 'answer', '', Input::TYPE_KSES_POST ); + $qna_text = Input::post( 'answer', '', tutor()->has_pro ? Input::TYPE_KSES_POST : Input::TYPE_TEXTAREA ); + if ( ! $qna_text ) { // Content validation. wp_send_json_error( array( 'message' => __( 'Empty Content Not Allowed!', 'tutor' ) ) ); @@ -73,9 +102,8 @@ public function tutor_qna_create_update() { $context = Input::post( 'context' ); // Prepare user info. - $user_id = get_current_user_id(); - $user = get_userdata( $user_id ); - $date = gmdate( 'Y-m-d H:i:s', tutor_time() ); + $user = get_userdata( $user_id ); + $date = gmdate( 'Y-m-d H:i:s', tutor_time() ); // Insert data prepare. $data = apply_filters( diff --git a/classes/Quiz.php b/classes/Quiz.php index fc5a35d1a3..b7711f8927 100644 --- a/classes/Quiz.php +++ b/classes/Quiz.php @@ -195,11 +195,11 @@ public static function quiz_question_layouts() { */ public static function quiz_question_orders() { $orders = array( - 'rand' => __( 'Random', 'tutor' ), - 'sorting' => __( 'Sorting', 'tutor' ), - 'asc' => __( 'Ascending', 'tutor' ), - 'desc' => __( 'Descending', 'tutor' ), - ); + 'rand' => __( 'Random', 'tutor' ), + 'sorting' => __( 'Sorting', 'tutor' ), + 'asc' => __( 'Ascending', 'tutor' ), + 'desc' => __( 'Descending', 'tutor' ), + ); return apply_filters( 'tutor_quiz_layouts', $orders ); } @@ -317,8 +317,6 @@ public function start_the_quiz() { die( 'Please sign in to do this operation' ); } - global $wpdb; - $user_id = get_current_user_id(); $user = get_userdata( $user_id ); @@ -326,7 +324,27 @@ public function start_the_quiz() { $quiz = get_post( $quiz_id ); $course = CourseModel::get_course_by_quiz( $quiz_id ); - if ( empty( $course->ID ) ) { + + self::quiz_attempt( $course->ID, $quiz_id, $user_id ); + wp_safe_redirect( get_permalink( $quiz_id ) ); + die(); + } + + /** + * Manage quiz attempt + * + * @since 2.6.1 + * + * @param integer $course_id course id. + * @param integer $quiz_id quiz id. + * @param integer $user_id user id. + * + * @return int inserted id|0 + */ + public static function quiz_attempt( int $course_id, int $quiz_id, int $user_id, $attempt_status = 'attempt_started' ) { + global $wpdb; + + if ( ! $course_id ) { die( 'There is something went wrong with course, please check if quiz attached with a course' ); } @@ -366,13 +384,13 @@ public function start_the_quiz() { $tutor_quiz_option['time_limit']['time_limit_seconds'] = $time_limit_seconds; $attempt_data = array( - 'course_id' => $course->ID, + 'course_id' => $course_id, 'quiz_id' => $quiz_id, 'user_id' => $user_id, 'total_questions' => $max_question_allowed, 'total_answered_questions' => 0, 'attempt_info' => maybe_serialize( $tutor_quiz_option ), - 'attempt_status' => 'attempt_started', + 'attempt_status' => $attempt_status, 'attempt_ip' => tutor_utils()->get_ip(), 'attempt_started_at' => $date, ); @@ -380,10 +398,12 @@ public function start_the_quiz() { $wpdb->insert( $wpdb->prefix . 'tutor_quiz_attempts', $attempt_data ); $attempt_id = (int) $wpdb->insert_id; - do_action( 'tutor_quiz/start/after', $quiz_id, $user_id, $attempt_id ); - - wp_safe_redirect( get_permalink( $quiz_id ) ); - die(); + if ( $attempt_id ) { + do_action( 'tutor_quiz/start/after', $quiz_id, $user_id, $attempt_id ); + return $attempt_id; + } else { + return 0; + } } /** @@ -442,7 +462,6 @@ public static function tutor_quiz_attemp_submit() { tutor_utils()->checking_nonce(); // Prepare attempt info. - global $wpdb; $user_id = get_current_user_id(); $attempt_id = Input::post( 'attempt_id', 0, Input::TYPE_INT ); $attempt = tutor_utils()->get_attempt( $attempt_id ); @@ -456,17 +475,33 @@ public static function tutor_quiz_attemp_submit() { if ( ! $attempt || $user_id != $attempt->user_id ) { die( 'Operation not allowed, attempt not found or permission denied' ); } + self::manage_attempt_answers( $attempt_answers, $attempt, $attempt_id, $course_id, $user_id ); + return true; + } - // Before ook. + /** + * Manage attempt answers + * + * Evaluate each attempt answer and update the attempts table & insert in the attempt_answers table. + * + * @since 2.6.1 + * + * @param array $attempt_answers attempt answers. + * @param object $attempt single attempt. + * @param int $attempt_id attempt id. + * @param int $course_id course id. + * @param int $user_id user id. + * + * @return void + */ + public static function manage_attempt_answers( $attempt_answers, $attempt, $attempt_id, $course_id, $user_id ) { + global $wpdb; + // Before hook. do_action( 'tutor_quiz/attempt_analysing/before', $attempt_id ); - // Loop through every single attempt answer // Single quiz can have multiple question. So multiple answer should be saved. foreach ( $attempt_answers as $attempt_id => $attempt_answer ) { - - /** - * Get total marks of all question comes - */ + // Get total marks of all question comes. $question_ids = tutor_utils()->avalue_dot( 'quiz_question_ids', $attempt_answer ); $question_ids = array_filter( $question_ids, @@ -710,8 +745,6 @@ function ( $ans ) { // After hook. do_action( 'tutor_quiz/attempt_ended', $attempt_id, $course_id, $user_id ); - - return true; } diff --git a/classes/RestAPI.php b/classes/RestAPI.php index eb326e6df0..31b2fbae6d 100644 --- a/classes/RestAPI.php +++ b/classes/RestAPI.php @@ -278,7 +278,7 @@ public function init_routes() { 'methods' => 'GET', 'callback' => array( $this->announcement_obj, - 'course_annoucement', + 'course_announcement', ), 'args' => array( 'id' => array( diff --git a/classes/Shortcode.php b/classes/Shortcode.php index 4d02249e0b..a33f57466e 100644 --- a/classes/Shortcode.php +++ b/classes/Shortcode.php @@ -48,7 +48,6 @@ public function __construct() { add_shortcode( 'tutor_course', array( $this, 'tutor_course' ) ); add_shortcode( 'tutor_instructor_list', array( $this, 'tutor_instructor_list' ) ); - add_action( 'tutor_options_after_instructors', array( $this, 'tutor_instructor_layout' ) ); add_action( 'wp_ajax_load_filtered_instructor', array( $this, 'load_filtered_instructor' ) ); add_action( 'wp_ajax_nopriv_load_filtered_instructor', array( $this, 'load_filtered_instructor' ) ); @@ -460,15 +459,4 @@ function( $cat ) { wp_send_json_success( array( 'html' => ob_get_clean() ) ); exit; } - - /** - * Show layout selection dashboard in instructor setting - * - * @since 1.0.0 - * - * @return void - */ - public function tutor_instructor_layout() { - tutor_load_template( 'instructor-setting', array( 'templates' => $this->instructor_layout ) ); - } } diff --git a/classes/Tutor_Setup.php b/classes/Tutor_Setup.php index a538223c1d..d905d2d22e 100644 --- a/classes/Tutor_Setup.php +++ b/classes/Tutor_Setup.php @@ -470,7 +470,7 @@ public function tutor_setup_attributes() { ), 'enable_q_and_a_on_course' => array( 'type' => 'switch', - 'lable' => __( 'Question and Anwser', 'tutor' ), + 'lable' => __( 'Question and Answer', 'tutor' ), 'desc' => __( 'Allows a Q&A forum on each course.', 'tutor' ), ), 'courses_col_per_row' => array( diff --git a/gulpfile.js b/gulpfile.js index abfb675db5..3e9be7175b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -2,12 +2,13 @@ var gulp = require('gulp'), sass = require('gulp-sass')(require('sass')), sourcemaps = require('gulp-sourcemaps'), rename = require('gulp-rename'), - prefix = require('gulp-autoprefixer'), plumber = require('gulp-plumber'), notify = require('gulp-notify'), wpPot = require('gulp-wp-pot'), clean = require('gulp-clean'), zip = require('gulp-zip'), + watch = require("gulp-watch"), + gulpIf = require("gulp-if"), fs = require('fs'), path = require('path'), versionNumber = ''; @@ -29,12 +30,6 @@ var onError = function(err) { this.emit('end'); }; -var prefixerOptions = { - overrideBrowserslist: ['last 2 versions'], -}; - - - var scss_blueprints = { tutor_front: { src: 'assets/scss/front/index.scss', mode: 'expanded', destination: 'tutor-front.min.css' }, @@ -46,8 +41,9 @@ var scss_blueprints = { destination: 'tutor-setup.min.css', }, - tutor_v2: { src: 'v2-library/_src/scss/tutor-main.scss', mode: 'expanded', destination: 'tutor.min.css' }, + tutor_v2: { src: 'v2-library/_src/scss/main.scss', mode: 'expanded', destination: 'tutor.min.css' }, tutor_v2_rtl: { src: 'v2-library/_src/scss/main.rtl.scss', mode: 'expanded', destination: 'tutor.rtl.min.css' }, + tutor_icon: { src: 'v2-library/bundle/fonts/tutor-icon/tutor-icon.css', mode: 'expanded', @@ -65,21 +61,14 @@ var scss_blueprints = { mode: 'expanded', destination: 'tutor-course-builder.min.css', }, - - v2_scss: { src: 'v2-library/_src/scss/main.scss', destination: 'main.min.css', dest_path: 'v2-library/bundle' }, - v2_rtl_scss: { src: 'v2-library/_src/scss/main.rtl.scss', destination: 'main.rtl.min.css', dest_path: 'v2-library/bundle' }, - - v2_scss_docz: { - src: 'v2-library/_src/scss/main.scss', - destination: 'main.min.css', - dest_path: '.docz/static/v2-library/bundle', - }, }; var task_keys = Object.keys(scss_blueprints); for (let task in scss_blueprints) { let blueprint = scss_blueprints[task]; + const isV2 = blueprint.src === 'v2-library/_src/scss/main.scss' + const isV2RTL = blueprint.src === 'v2-library/_src/scss/main.rtl.scss' gulp.task(task, function() { return gulp @@ -88,26 +77,15 @@ for (let task in scss_blueprints) { .pipe(sourcemaps.init({ loadMaps: true, largeFile: true })) .pipe(sass({ outputStyle: 'compressed', sass: require('sass') })) .pipe(rename(blueprint.destination)) - .pipe(gulp.dest(blueprint.dest_path || 'assets/css')); + .pipe(gulp.dest(blueprint.dest_path || 'assets/css')) + .pipe(gulpIf(isV2, rename('main.min.css'))) + .pipe(gulpIf(isV2, gulp.dest('v2-library/bundle'))) + .pipe(gulpIf(isV2, gulp.dest('.docz/static/v2-library/bundle'))) + .pipe(gulpIf(isV2RTL, rename('main.rtl.min.css'))) + .pipe(gulpIf(isV2RTL, gulp.dest('v2-library/bundle'))); }); } -// Add task to add tutor prefix to v2 scss -// gulp.task('v2_tutor_prefix', function(resolve) { -// var exp = path.resolve(__dirname + '/assets/css/tutor.min.css'); -// var min = path.resolve(__dirname + '/assets/css/tutor.min.css'); -// var docz = path.resolve(__dirname + '/v2-library/bundle/main.min.css'); - -// [exp, min, docz].forEach((css) => { -// var string = fs.readFileSync(css).toString(); -// string = string.replace(/\.tutor\-prefix \./g, '.tutor-'); -// fs.writeFileSync(css, string); -// }); - -// resolve(); -// }); -// task_keys.push('v2_tutor_prefix'); - var added_texts = []; const regex = /__\(\s*(['"])((?:(?!(?prefix . 'tutor_quiz_attempt_answers'; + $table_quiz_questions = $wpdb->prefix . 'tutor_quiz_questions'; + $table_quiz_attempts = $wpdb->prefix . 'tutor_quiz_attempts'; + $table_quiz_question_answers = $wpdb->prefix . 'tutor_quiz_question_answers'; + + $query = "SELECT + ques.question_id, + ques.question_title, + ques.question_type, + ( + SELECT + GROUP_CONCAT(answer_title) + FROM + {$table_quiz_question_answers} + WHERE + belongs_question_id = ques.question_id + AND is_correct = 1 + ) AS correct_answers, + + ( + + SELECT + + CASE + WHEN CHAR_LENGTH(att_ans.given_answer) = 1 AND att_ans.given_answer REGEXP '^[0-9]$' THEN + -- If given_answer is a single digit integer + ( + SELECT + answer_title + FROM + {$table_quiz_question_answers} + WHERE + answer_id = CAST(att_ans.given_answer AS UNSIGNED) + ) + WHEN CHAR_LENGTH(att_ans.given_answer) > 1 AND SUBSTRING(att_ans.given_answer, 1, 2) = 'a:' THEN + -- If given_answer is serialized array + ( + att_ans.given_answer + ) + ELSE + -- If given_answer is a serialized string + att_ans.given_answer + END + ) AS given_answer, + att_ans.question_mark, + att_ans.achieved_mark, + att_ans.is_correct, + ( + SELECT + attempt_info + FROM {$table_quiz_attempts} + WHERE attempt_id = {$attempt_id} + LIMIT 1 + ) AS attempt_info + FROM + {$table_quiz_attempt_answers} AS att_ans + JOIN {$table_quiz_questions} AS ques ON ques.question_id = att_ans.question_id + JOIN {$table_quiz_question_answers} AS ans ON ans.answer_id = att_ans.attempt_answer_id + WHERE + quiz_attempt_id = %d + LIMIT + 50 + "; + + $result = $wpdb->get_results( $wpdb->prepare( $query, $attempt_id ) ); + + // If array and count result then loop with each result and prepare given answer. + if ( is_array( $result ) && count( $result ) ) { + foreach ( $result as $key => $value ) { + // Check if given answer is a serialized string. + if ( is_serialized( $value->given_answer ) ) { + $given_answers = tutor_utils()->get_answer_by_id( maybe_unserialize( $value->given_answer ) ); + $result[ $key ]->given_answer = array_column( $given_answers, 'answer_title' ); + } elseif ( is_numeric( $value->given_answer ) ) { + $given_answers = tutor_utils()->get_answer_by_id( maybe_unserialize( $value->given_answer ) ); + $result[ $key ]->given_answer = array_column( $given_answers, 'answer_title' ); + } + } + } + + return $result; + } } diff --git a/package-lock.json b/package-lock.json index 6d7f7c4f74..07a0464cf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3812,7 +3812,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", - "dev": true, "requires": { "ansi-wrap": "0.1.0" } @@ -3847,8 +3846,7 @@ "ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", - "dev": true + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==" }, "anymatch": { "version": "3.1.3", @@ -5958,8 +5956,7 @@ "clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", - "dev": true + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==" }, "clone-deep": { "version": "4.0.1", @@ -5989,14 +5986,12 @@ "clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", - "dev": true + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==" }, "cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, "requires": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", @@ -6078,8 +6073,7 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, "colorette": { "version": "2.0.20", @@ -9468,6 +9462,57 @@ } } }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -9909,6 +9954,11 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "optional": true }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==" + }, "filesize": { "version": "3.5.11", "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz", @@ -10158,6 +10208,14 @@ "parse-filepath": "^1.0.1" } }, + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==", + "requires": { + "readable-stream": "^2.0.2" + } + }, "flagged-respawn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", @@ -10236,6 +10294,11 @@ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" }, + "fork-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", + "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==" + }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", @@ -12839,6 +12902,38 @@ "path-is-absolute": "^1.0.0" } }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -13727,32 +13822,6 @@ } } }, - "gulp-autoprefixer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-7.0.1.tgz", - "integrity": "sha512-QJGEmHw+bEt7FSqvmbAUTxbCuNLJYx4sz3ox9WouYqT/7j5FH5CQ8ZnpL1M7H5npX1bUJa7lUVY1w20jXxhOxg==", - "dev": true, - "requires": { - "autoprefixer": "^9.6.1", - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.1", - "postcss": "^7.0.17", - "through2": "^3.0.1", - "vinyl-sourcemaps-apply": "^0.2.1" - }, - "dependencies": { - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - } - } - }, "gulp-clean": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.4.0.tgz", @@ -14324,6 +14393,35 @@ } } }, + "gulp-if": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", + "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", + "requires": { + "gulp-match": "^1.1.0", + "ternary-stream": "^3.0.0", + "through2": "^3.0.1" + }, + "dependencies": { + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + } + } + }, + "gulp-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", + "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", + "requires": { + "minimatch": "^3.0.3" + } + }, "gulp-notify": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz", @@ -14468,100 +14566,711 @@ "kind-of": "^1.1.0" } }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", - "dev": true - }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", - "dev": true, - "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - } + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true + } + } + }, + "gulp-rename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", + "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", + "dev": true + }, + "gulp-sass": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.1.0.tgz", + "integrity": "sha512-7VT0uaF+VZCmkNBglfe1b34bxn/AfcssquLKVDYnCDJ3xNBaW7cUuI3p3BQmoKcoKFrs9jdzUxyb+u+NGfL4OQ==", + "dev": true, + "requires": { + "lodash.clonedeep": "^4.5.0", + "picocolors": "^1.0.0", + "plugin-error": "^1.0.1", + "replace-ext": "^2.0.0", + "strip-ansi": "^6.0.1", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "dependencies": { + "replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true + } + } + }, + "gulp-sourcemaps": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", + "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "dev": true, + "requires": { + "@gulp-sourcemaps/identity-map": "1.X", + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "5.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "1.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom-string": "1.X", + "through2": "2.X" + }, + "dependencies": { + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "gulp-watch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", + "integrity": "sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog==", + "requires": { + "ansi-colors": "1.1.0", + "anymatch": "^1.3.0", + "chokidar": "^2.0.0", + "fancy-log": "1.3.2", + "glob-parent": "^3.0.1", + "object-assign": "^4.1.0", + "path-is-absolute": "^1.0.1", + "plugin-error": "1.0.1", + "readable-stream": "^2.2.2", + "slash": "^1.0.0", + "vinyl": "^2.1.0", + "vinyl-file": "^2.0.0" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==" + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "requires": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + } + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", + "requires": { + "is-extglob": "^1.0.0" + } + }, + "fancy-log": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", + "integrity": "sha512-7E6IFy84FpO6jcnzEsCcoxDleHpMTFzncmCXXBIVYq1/Oakqnbc/lTKPJyyW6edGeC/rnZmV78hJe7SuoZo0aQ==", + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "time-stamp": "^1.0.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "requires": { + "hasown": "^2.0.0" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-data-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", + "requires": { + "hasown": "^2.0.0" + } + }, + "is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "requires": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "requires": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==" }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", "requires": { - "ansi-regex": "^2.0.0" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - } - } - }, - "gulp-rename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", - "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", - "dev": true - }, - "gulp-sass": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.1.0.tgz", - "integrity": "sha512-7VT0uaF+VZCmkNBglfe1b34bxn/AfcssquLKVDYnCDJ3xNBaW7cUuI3p3BQmoKcoKFrs9jdzUxyb+u+NGfL4OQ==", - "dev": true, - "requires": { - "lodash.clonedeep": "^4.5.0", - "picocolors": "^1.0.0", - "plugin-error": "^1.0.1", - "replace-ext": "^2.0.0", - "strip-ansi": "^6.0.1", - "vinyl-sourcemaps-apply": "^0.2.1" - }, - "dependencies": { - "replace-ext": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", - "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", - "dev": true - } - } - }, - "gulp-sourcemaps": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", - "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", - "dev": true, - "requires": { - "@gulp-sourcemaps/identity-map": "1.X", - "@gulp-sourcemaps/map-sources": "1.X", - "acorn": "5.X", - "convert-source-map": "1.X", - "css": "2.X", - "debug-fabulous": "1.X", - "detect-newline": "2.X", - "graceful-fs": "4.X", - "source-map": "~0.6.0", - "strip-bom-string": "1.X", - "through2": "2.X" - }, - "dependencies": { - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -14829,6 +15538,21 @@ } } }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "requires": { + "function-bind": "^1.1.2" + }, + "dependencies": { + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + } + } + }, "hast-to-hyperscript": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", @@ -15866,6 +16590,19 @@ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", + "requires": { + "is-primitive": "^2.0.0" + } + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -16019,6 +16756,16 @@ "isobject": "^3.0.1" } }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==" + }, "is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -16151,8 +16898,7 @@ "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", - "dev": true + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" }, "is-valid-domain": { "version": "0.1.6", @@ -17270,6 +18016,11 @@ "integrity": "sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==", "dev": true }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" + }, "md5-file": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz", @@ -19137,6 +19888,25 @@ "make-iterator": "^1.0.0" } }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "requires": { + "for-in": "^1.0.1" + } + } + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -19530,6 +20300,32 @@ "path-root": "^0.1.1" } }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -19961,7 +20757,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, "requires": { "ansi-colors": "^1.0.1", "arr-diff": "^4.0.0", @@ -19973,7 +20768,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, "requires": { "ansi-wrap": "^0.1.0" } @@ -21152,6 +21946,11 @@ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==" }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==" + }, "prettier": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", @@ -21428,6 +22227,28 @@ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -22376,6 +23197,14 @@ "@babel/runtime": "^7.8.4" } }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, "regex-escape": { "version": "3.4.10", "resolved": "https://registry.npmjs.org/regex-escape/-/regex-escape-3.4.10.tgz", @@ -24338,6 +25167,25 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==", + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, "strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", @@ -24716,6 +25564,49 @@ "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==" }, + "ternary-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", + "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", + "requires": { + "duplexify": "^4.1.1", + "fork-stream": "^0.0.4", + "merge-stream": "^2.0.0", + "through2": "^3.0.1" + }, + "dependencies": { + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + } + } + }, "terser": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", @@ -24879,8 +25770,7 @@ "time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", - "dev": true + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==" }, "timed-out": { "version": "4.0.1", @@ -26152,7 +27042,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, "requires": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", @@ -26165,8 +27054,55 @@ "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + } + } + }, + "vinyl-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", + "integrity": "sha512-44i5QVLwRPbiRyuiHJ+zJXooNNRXUUifdfYIC1Gm7YTlemMgYQrZ+q1LERS6AYAN8w0xe7n9OgjEYckQjR5+4g==", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^1.1.0" + }, + "dependencies": { + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==" + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } } } }, diff --git a/package.json b/package.json index 5cb465ead8..31cb4b9254 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "eslint-loader": "^4.0.2", "fs": "0.0.1-security", "gulp": "^4.0.2", - "gulp-autoprefixer": "^7.0.1", "gulp-clean": "^0.4.0", "gulp-cssnano": "^2.1.3", "gulp-notify": "^3.2.0", @@ -82,6 +81,8 @@ "docz": "^2.3.1", "emotion": "^11.0.0", "fs-extra": "^10.0.0", + "gulp-if": "^3.0.0", + "gulp-watch": "^5.0.1", "immutability-helper": "^3.1.1", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.2.2", diff --git a/readme.txt b/readme.txt index 6dd04a3d71..6627128357 100644 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Tags: lms, course, elearning, education, learning management system Requires at least: 5.3 Tested up to: 6.4 Requires PHP: 7.4 -Stable tag: 2.6.0 +Stable tag: 2.6.1 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -316,6 +316,13 @@ These key integrations with Tutor LMS extend its capabilities for a more powerfu == Changelog == += 2.6.1 - February 19, 2024 + +New: Added API functionality for submitting and retrieving list of quizzes (Pro) +Update: Improved security to ensure safe submission of questions and answers +Update: Improved response data, extendability, and performance across all APIs within the Tutor LMS Free plugin +Fix: Fixed issue causing "Resource not found" error related to ChatGPT (Pro) + = 2.6.0 - January 11, 2024 New: Added Write and Delete permissions in REST API (Pro) diff --git a/restapi/REST_Author.php b/restapi/REST_Author.php index 9a48ac0ae5..27eee61450 100644 --- a/restapi/REST_Author.php +++ b/restapi/REST_Author.php @@ -1,50 +1,76 @@ + * @link https://themeum.com + * @since 1.7.1 + */ + namespace TUTOR; + use WP_REST_Request; -if(!defined( 'ABSPATH' )) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} +/** + * Class REST_Author + * + * @package Tutor + * @since 1.0.0 + */ class REST_Author { use REST_Response; + /** + * User ID. + * + * @var int $user_id The ID of the user. + */ private $user_id; - /* - *require user id - *return json object with user detail - */ - public function author_detail(WP_REST_Request $request) { - $this->user_id = $request->get_param('id'); - global $wpdb; - $table = $wpdb->prefix."users"; - //author obj - $author = $wpdb->get_row( - $wpdb->prepare( - "SELECT user_email, user_registered, display_name FROM $table WHERE ID = %d", - $this->user_id - )); - - if($author) { - //get author course id - $author->courses = get_user_meta($this->user_id,'_tutor_instructor_course_id',false); + /** + * Retrieve author details via REST API. + * + * @param WP_REST_Request $request The REST request object. + * + * @return mixed + */ + public function author_detail( WP_REST_Request $request ) { + $this->user_id = $request->get_param( 'id' ); + + $user_data = get_userdata( $this->user_id ); + + // Author object. + $author = is_a( $user_data, 'WP_User' ) ? $user_data->data : false; + + if ( $author ) { + // Unset user pass & key. + unset( $author->user_pass ); + unset( $author->user_activation_key ); + + // Get author course ID. + $author->courses = get_user_meta( $this->user_id, '_tutor_instructor_course_id', false ); $response = array( - 'status_code'=> 'success', - 'message'=> __('Author details retrieved successfully','tutor'), - 'data'=> $author + 'status_code' => 'success', + 'message' => __( 'Author details retrieved successfully', 'tutor' ), + 'data' => $author, ); - return self::send($response); + return self::send( $response ); } - + $response = array( - 'status_code'=> 'invalid_id', - 'message'=> __('Author not found','tutor'), - 'data'=> [] + 'status_code' => 'invalid_id', + 'message' => __( 'Author not found', 'tutor' ), + 'data' => array(), ); - return self::send($response); + return self::send( $response ); } } diff --git a/restapi/REST_Course.php b/restapi/REST_Course.php index 5eee138fb4..3e415281d9 100644 --- a/restapi/REST_Course.php +++ b/restapi/REST_Course.php @@ -1,292 +1,403 @@ + * @link https://themeum.com + * @since 1.7.1 + */ namespace TUTOR; + use WP_REST_Request; use WP_Query; -if( !defined ('ABSPATH')) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} -class REST_Course { - - use REST_Response; +/** + * Rest_Course class + */ +class REST_Course { - private $post_type; + use REST_Response; - private $course_cat_tax = "course-category"; - private $course_tag_tax = "course-tag"; + /** + * The post type associated with the course handler. + * + * @since 1.7.1 + * + * @var string $post_type The post type for courses. + */ + private $post_type; + /** + * The taxonomy for course categories. + * + * @since 1.7.1 + * + * @var string $course_cat_tax The taxonomy for course categories. + */ + private $course_cat_tax = 'course-category'; + + /** + * The taxonomy for course tags. + * + * @since 1.7.1 + * + * @var string $course_tag_tax The taxonomy for course tags. + */ + private $course_tag_tax = 'course-tag'; + + /** + * Constructor for the Tutor_Course_Handler class. + * + * Initializes the post type property. + * + * @since 1.7.1 + */ public function __construct() { $this->post_type = tutor()->course_post_type; } - /* - *require rest request - *return course info - *pagination enable - *category,tags terms included - */ - public function course(WP_REST_Request $request) { - $order = sanitize_text_field($request->get_param('order')); - $orderby = sanitize_text_field($request->get_param('orderby')); - $paged = sanitize_text_field($request->get_param('paged')); + /** + * Course read API handler + * + * Get course list along with pagination, categories, tags + * author details, reviews + * + * @param WP_REST_Request $request request data. + * + * @return WP_REST_Response + */ + public function course( WP_REST_Request $request ) { + $order = sanitize_text_field( $request->get_param( 'order' ) ); + $orderby = sanitize_text_field( $request->get_param( 'orderby' ) ); + $paged = sanitize_text_field( $request->get_param( 'paged' ) ); $args = array( - 'post_type' => $this->post_type, - 'post_status' => 'publish', - 'posts_per_page' => 10, - 'paged' => $paged ? $paged : 1, - 'order' => $order ? $order : 'ASC', - 'orderby' => $orderby ? $orderby :'title' + 'post_type' => $this->post_type, + 'post_status' => 'publish', + 'posts_per_page' => 10, + 'paged' => $paged ? $paged : 1, + 'order' => $order ? $order : 'ASC', + 'orderby' => $orderby ? $orderby : 'title', ); - $query = new WP_Query($args); + $args = apply_filters( 'tutor_rest_course_query_args', $args ); + $query = new WP_Query( $args ); - //if post found - if(count($query->posts)>0) { - //unset filter properpty - array_map(function($post){ - unset($post->filter); - }, $query->posts); + // if post found. + if ( count( $query->posts ) > 0 ) { + // unset filter property. + array_map( + function( $post ) { + unset( $post->filter ); + }, + $query->posts + ); - $data = [ - 'posts'=> [], + $data = array( + 'posts' => array(), 'total_course' => $query->found_posts, - 'total_page' => $query->max_num_pages - ]; + 'total_page' => $query->max_num_pages, + ); - foreach($query->posts as $post) { - $category = wp_get_post_terms($post->ID,$this->course_cat_tax); + foreach ( $query->posts as $post ) { + $category = wp_get_post_terms( $post->ID, $this->course_cat_tax ); - $tag = wp_get_post_terms($post->ID,$this->course_tag_tax); + $tag = wp_get_post_terms( $post->ID, $this->course_tag_tax ); + + $author = get_userdata( $post->post_author ); + + if ( $author ) { + // Unset user pass & key. + unset( $author->data->user_pass ); + unset( $author->data->user_activation_key ); + } + + is_a( $author, 'WP_User' ) ? $post->post_author = $author->data : new \stdClass(); + + $thumbnail_size = apply_filters( 'tutor_rest_course_thumbnail_size', 'post-thumbnail' ); + $post->thumbnail_url = get_the_post_thumbnail_url( $post->ID, $thumbnail_size ); + + $post->additional_info = $this->course_additional_info( $post->ID ); + + $post->ratings = tutor_utils()->get_course_rating( $post->ID ); $post->course_category = $category; $post->course_tag = $tag; - array_push($data['posts'], $post); - + $post = apply_filters( 'tutor_rest_course_single_post', $post ); + array_push( $data['posts'], $post ); } $response = array( - 'status_code'=> "success", - "message"=> __('Course retrieved successfully','tutor'), - 'data'=> $data + 'status_code' => 'success', + 'message' => __( 'Course retrieved successfully', 'tutor' ), + 'data' => $data, ); - return self::send($response); + return self::send( $response ); } $response = array( - 'status_code'=> "not_found", - "message"=> __('Course not found','tutor'), - 'data'=> [] + 'status_code' => 'not_found', + 'message' => __( 'Course not found', 'tutor' ), + 'data' => array(), ); - return self::send($response); + return self::send( $response ); } - /* - *require rest request - *return post meta items - */ - function course_detail(WP_REST_Request $request) { - $post_id = $request->get_param('id'); + /** + * Course Details API handler + * + * @since 1.7.1 + * + * @param WP_REST_Request $request request params. + * + * @return WP_REST_Response + */ + public function course_detail( WP_REST_Request $request ) { + $post_id = $request->get_param( 'id' ); + + $detail = $this->course_additional_info( $post_id ); + if ( $detail ) { + $response = array( + 'status_code' => 'course_detail', + 'message' => __( 'Course detail retrieved successfully', 'tutor' ), + 'data' => $detail, + ); + return self::send( $response ); + } + $response = array( + 'status_code' => 'course_detail', + 'message' => __( 'Detail not found for given ID', 'tutor' ), + 'data' => array(), + ); + + return self::send( $response ); + } + /** + * Get course additional info + * + * @since 2.6.1 + * + * @param integer $post_id post id. + * + * @return array + */ + public function course_additional_info( int $post_id ) { $detail = array( - 'course_settings' => get_post_meta($post_id,'_tutor_course_settings',false), + 'course_settings' => get_post_meta( $post_id, '_tutor_course_settings', false ), - 'course_price_type' => get_post_meta($post_id,'_tutor_course_price_type',false), + 'course_price_type' => get_post_meta( $post_id, '_tutor_course_price_type', false ), - 'course_duration' => get_post_meta($post_id,'_course_duration',false), + 'course_duration' => get_post_meta( $post_id, '_course_duration', false ), - 'course_level' => get_post_meta($post_id,'_tutor_course_level',false), + 'course_level' => get_post_meta( $post_id, '_tutor_course_level', false ), - 'course_benefits' => get_post_meta($post_id,'_tutor_course_benefits',false), + 'course_benefits' => get_post_meta( $post_id, '_tutor_course_benefits', false ), - 'course_requirements' => get_post_meta($post_id,'_tutor_course_requirements',false), + 'course_requirements' => get_post_meta( $post_id, '_tutor_course_requirements', false ), - 'course_target_audience' => get_post_meta($post_id,'_tutor_course_target_audience',false), + 'course_target_audience' => get_post_meta( $post_id, '_tutor_course_target_audience', false ), - 'course_material_includes' => get_post_meta($post_id,'_tutor_course_material_includes',false), + 'course_material_includes' => get_post_meta( $post_id, '_tutor_course_material_includes', false ), - 'video' => get_post_meta($post_id,'_video',false), - - 'disable_qa' => get_post_meta($post_id, '_tutor_enable_qa', true)!='yes' + 'video' => get_post_meta( $post_id, '_video', false ), + + 'disable_qa' => get_post_meta( $post_id, '_tutor_enable_qa', true ) != 'yes', ); - if($detail) { - $response = array( - 'status_code' => "course_detail", - "message" => __('Course detail retrieved successfully','tutor'), - 'data' => $detail - ); - return self::send($response); - } - $response = array( - 'status_code' => "course_detail", - "message" => __('Detail not found for given ID','tutor'), - 'data' => [] - ); + return apply_filters( 'tutor_course_additional_info', $detail ); - return self::send($response); } - /* - *return post type terms - */ - public function course_by_terms(WP_REST_Request $request) { - $post_fields = $request->get_params(); - $validate_err = $this->validate_terms($post_fields); - - //check array or not - if(count($validate_err)>0) { + /** + * Get Course by Terms API handler + * + * @since 1.7.1 + * + * @param WP_REST_Request $request request params. + * + * @return WP_REST_Response + */ + public function course_by_terms( WP_REST_Request $request ) { + $post_fields = $request->get_params(); + $validate_err = $this->validate_terms( $post_fields ); + + // check array or not. + if ( count( $validate_err ) > 0 ) { $response = array( - 'status_code'=> "validation_error", - "message"=> $validate_err, - 'data'=> [] - ); + 'status_code' => 'validation_error', + 'message' => $validate_err, + 'data' => array(), + ); - return self::send($response); + return self::send( $response ); } - //sanitize terms + // sanitize terms. $categories = sanitize_term( $request['categories'], $this->course_cat_tax, $context = 'db' ); $tags = sanitize_term( $request['tags'], $this->course_tag_tax, $context = 'db' ); $args = array( - 'post_type' => $this->post_type, - 'tax_query' => array( - 'relation' => 'OR', - array( - 'taxonomy' => $this->course_cat_tax, - 'field' => 'name', - 'terms' => $categories - ), - array( - 'taxonomy' => $this->course_tag_tax, - 'field' => 'name', - 'terms' => $tags - - ) - ) + 'post_type' => $this->post_type, + 'tax_query' => array( + 'relation' => 'OR', + array( + 'taxonomy' => $this->course_cat_tax, + 'field' => 'name', + 'terms' => $categories, + ), + array( + 'taxonomy' => $this->course_tag_tax, + 'field' => 'name', + 'terms' => $tags, + + ), + ), ); - $query = new WP_Query ($args); + $args = apply_filters( 'tutor_rest_course_by_terms_query_args', $args ); + $query = new WP_Query( $args ); - if(count($query->posts)>0) { - //unset filter proterty - array_map(function($post){ - unset($post->filter); - }, $query->posts); + if ( count( $query->posts ) > 0 ) { + // unset filter property. + array_map( + function( $post ) { + unset( $post->filter ); + }, + $query->posts + ); $response = array( - 'status_code'=> "success", - "message"=> __("Course retrieved successfully",'tutor'), - 'data'=> $query->posts - ); + 'status_code' => 'success', + 'message' => __( 'Course retrieved successfully', 'tutor' ), + 'data' => $query->posts, + ); - return self::send($response); + return self::send( $response ); } $response = array( - 'status_code'=> "not_found", - "message"=> __("Course not found for given terms",'tutor'), - 'data'=> [] - ); - return self::send($response); + 'status_code' => 'not_found', + 'message' => __( 'Course not found for given terms', 'tutor' ), + 'data' => array(), + ); + return self::send( $response ); } - /* - *categories array validation - *tags array validation - */ - public function validate_terms(array $post) { + /** + * Validate terms + * + * @since 1.7.1 + * + * @param array $post post array. + * + * @return array validation errors. + */ + public function validate_terms( array $post ) { $categories = $post['categories']; - $tags = $post['tags']; + $tags = $post['tags']; - $error = []; + $error = array(); - if (!is_array($categories)) { - array_push($error,__('Categories field is not an array','tutor')); + if ( ! is_array( $categories ) ) { + array_push( $error, __( 'Categories field is not an array', 'tutor' ) ); } - - if (!is_array($tags)) { - array_push($error,__('Tags field is not an array','tutor')); + + if ( ! is_array( $tags ) ) { + array_push( $error, __( 'Tags field is not an array', 'tutor' ) ); } return $error; } - public function course_sort_by_price(WP_REST_Request $request) { - $order = $request->get_param('order'); - $paged = $request->get_param('page'); - - $order = sanitize_text_field($order); - $paged = sanitize_text_field($paged); + /** + * Course sort by price API handler + * + * @since 1.7.1 + * + * @param WP_REST_Request $request request params. + * + * @return WP_REST_Response + */ + public function course_sort_by_price( WP_REST_Request $request ) { + $order = $request->get_param( 'order' ); + $paged = $request->get_param( 'page' ); + + $order = sanitize_text_field( $order ); + $paged = sanitize_text_field( $paged ); $args = array( - 'post_type'=> 'product', - 'post_status'=> 'publish', - 'posts_per_page' => 10, - 'paged'=> $paged ? $paged : 1, - - 'meta_key'=> '_regular_price', - 'orderby'=> 'meta_value_num', - 'order'=> $order, - 'meta_query'=> array( - 'relation'=>'AND', - array( - 'key'=> '_tutor_product', - 'value'=> "yes" - - ) - ) + 'post_type' => 'product', + 'post_status' => 'publish', + 'posts_per_page' => 10, + 'paged' => $paged ? $paged : 1, + + 'meta_key' => '_regular_price', + 'orderby' => 'meta_value_num', + 'order' => $order, + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => '_tutor_product', + 'value' => 'yes', + + ), + ), ); + $args = apply_filters( 'tutor_rest_course_sort_by_price_args', $args ); $query = new WP_Query( $args ); - if (count($query->posts)>0) { - //unset filter property - array_map(function($post){ - unset($post->filter); - }, $query->posts); + if ( count( $query->posts ) > 0 ) { + // unset filter property. + array_map( + function( $post ) { + unset( $post->filter ); + }, + $query->posts + ); - $posts = []; + $posts = array(); - foreach ($query->posts as $post) { - $post->price = get_post_meta($post->ID,'_regular_price', true); - array_push($posts, $post); + foreach ( $query->posts as $post ) { + $post->price = get_post_meta( $post->ID, '_regular_price', true ); + array_push( $posts, $post ); } $data = array( - 'posts'=> $posts, + 'posts' => $posts, 'total_course' => $query->found_posts, - 'total_page' => $query->max_num_pages + 'total_page' => $query->max_num_pages, ); $response = array( - 'status_code'=> 'success', - 'message'=> __('Course retrieved successfully','tutor'), - 'data'=> $data + 'status_code' => 'success', + 'message' => __( 'Course retrieved successfully', 'tutor' ), + 'data' => $data, ); - return self::send($response); + return self::send( $response ); } - + $response = array( - 'status'=> 'not_found', - 'message'=> __('Course not found','tutor'), - 'data'=> [] + 'status' => 'not_found', + 'message' => __( 'Course not found', 'tutor' ), + 'data' => array(), ); - return self::send($response); + return self::send( $response ); } } diff --git a/restapi/REST_Course_Announcement.php b/restapi/REST_Course_Announcement.php index 1231278f39..6337cc30d2 100644 --- a/restapi/REST_Course_Announcement.php +++ b/restapi/REST_Course_Announcement.php @@ -1,53 +1,81 @@ + * @link https://themeum.com + * @since 1.7.1 + */ namespace TUTOR; + use WP_REST_Request; -if(!defined('ABSPATH')) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} +/** + * Class REST_Course_Announcement + * + * @package Tutor + * @since 1.0.0 + */ class REST_Course_Announcement { use REST_Response; + /** + * Post parent ID. + * + * @var int $post_parent The ID of the post parent. + */ private $post_parent; - private $post_type = "tutor_announcements"; - /* - *require rest request - *return accoucement by course id - */ - public function course_annoucement(WP_REST_Request $request) { - $this->post_parent = $request->get_param('id'); + /** + * Post type. + * + * @var string $post_type The post type for announcements. + */ + private $post_type = 'tutor_announcements'; + + /** + * Retrieve announcements by course ID via REST API. + * + * @since 1.7.1 + * + * @param WP_REST_Request $request The REST request object. + * + * @return mixed + */ + public function course_announcement( WP_REST_Request $request ) { + $this->post_parent = $request->get_param( 'id' ); global $wpdb; - $table = $wpdb->prefix."posts"; + $table = $wpdb->prefix . 'posts'; $result = $wpdb->get_results( - $wpdb->prepare("SELECT ID, post_title, post_content, post_name FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent) + $wpdb->prepare( "SELECT ID, post_title, post_content, post_name FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent ) ); - if (count($result)>0) { + if ( count( $result ) > 0 ) { $response = array( - 'status_code'=> "success", - "message"=> __('Announcement retrieved successfully','tutor'), - 'data'=> $result - ); - - return self::send($response); + 'status_code' => 'success', + 'message' => __( 'Announcement retrieved successfully', 'tutor' ), + 'data' => $result, + ); + + return self::send( $response ); } $response = array( - 'status_code'=> "not_found", - "message"=> __('Announcement not found for given ID','tutor'), - 'data'=> [] - ); - - return self::send($response); + 'status_code' => 'not_found', + 'message' => __( 'Announcement not found for given ID', 'tutor' ), + 'data' => array(), + ); + + return self::send( $response ); } } diff --git a/restapi/REST_Lesson.php b/restapi/REST_Lesson.php index 36c80dc54c..70280f80fe 100644 --- a/restapi/REST_Lesson.php +++ b/restapi/REST_Lesson.php @@ -1,68 +1,114 @@ + * @link https://themeum.com + * @since 1.7.1 + */ namespace TUTOR; + +use WP_Query; use WP_REST_Request; -if(!defined('ABSPATH')) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} +/** + * Class REST_Lesson + */ class REST_Lesson { use REST_Response; - + + /** + * Post type + * + * @var string $post_type + */ private $post_type; + + /** + * Post parent ID + * + * @var int $post_parent + */ private $post_parent; + /** + * REST_Lesson constructor. + */ public function __construct() { $this->post_type = tutor()->lesson_post_type; } - public function topic_lesson(WP_REST_Request $request) { - $this->post_parent = $request->get_param('id'); - global $wpdb; + /** + * Get lessons for a specific topic. + * + * @param WP_REST_Request $request REST request object. + * + * @return mixed + */ + public function topic_lesson( WP_REST_Request $request ) { + $this->post_parent = $request->get_param( 'id' ); + + $args = array( + 'post_type' => $this->post_type, + 'post_parent' => $this->post_parent, + 'posts_per_page' => -1, + ); - $table = $wpdb->prefix."posts"; + $lessons_query = new WP_Query( $args ); - $lessons = $wpdb->get_results( - $wpdb->prepare("SELECT ID, post_title, post_content, post_name, (SELECT post_parent from {$table} WHERE ID = {$this->post_parent} ) as course_id FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent) - ); - $data = array(); - if(count($lessons)>0) { - foreach ($lessons as $lesson) { - $attachments = []; - $attachments_id = get_post_meta($lesson->ID,'_tutor_attachments',false); + if ( $lessons_query->have_posts() ) { + while ( $lessons_query->have_posts() ) { + $lessons_query->the_post(); + + $lesson = new \stdClass(); + + $lesson->ID = get_the_ID(); + $lesson->post_title = get_the_title(); + $lesson->post_content = get_the_content(); + $lesson->post_name = $post->post_name; + $lesson->course_id = wp_get_post_parent_id( $lesson->ID ); + + $attachments = array(); + $attachments_id = get_post_meta( $lesson->ID, '_tutor_attachments', false ); $attachments_id = $attachments_id[0]; - foreach($attachments_id as $id) { - $guid = get_the_guid($id); - array_push($attachments, $guid); + + foreach ( $attachments_id as $id ) { + $guid = get_the_guid( $id ); + array_push( $attachments, $guid ); } $lesson->attachments = $attachments; - $lesson->thumbnail = get_the_post_thumbnail_url($lesson->ID); - $lesson->video = get_post_meta($lesson->ID, '_video',false); - array_push($data, $lesson); + $lesson->thumbnail = get_the_post_thumbnail_url( $lesson->ID ); + $lesson->video = get_post_meta( $lesson->ID, '_video', false ); + + array_push( $data, $lesson ); } $response = array( - 'status_code'=> "success", - "message"=> __('Lesson retrieved successfully','tutor'), - 'data'=> $data + 'status_code' => 'success', + 'message' => __( 'Lesson retrieved successfully', 'tutor' ), + 'data' => $data, ); - return self::send($response); + return self::send( $response ); } + $response = array( - 'status_code'=> "not_found", - "message"=> __('Lesson not found for given topic ID','tutor'), - 'data'=> [] + 'status_code' => 'not_found', + 'message' => __( 'Lesson not found for the given topic ID', 'tutor' ), + 'data' => array(), ); - return self::send($response); - } + + return self::send( $response ); + } + } diff --git a/restapi/REST_Quiz.php b/restapi/REST_Quiz.php index 4c7f04af40..e3e24f9765 100644 --- a/restapi/REST_Quiz.php +++ b/restapi/REST_Quiz.php @@ -1,210 +1,350 @@ + * @link https://themeum.com + * @since 1.7.1 + */ namespace TUTOR; + +use Themeum\Products\Helpers\QueryHelper; use WP_REST_Request; -if(!defined ('ABSPATH')) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} +/** + * Class REST_Quiz + */ class REST_Quiz { use REST_Response; - private $post_type = "tutor_quiz"; + /** + * Quiz post type + * + * @var string The post type for quizzes. + */ + private $post_type = 'tutor_quiz'; + + /** + * Post parent ID + * + * @var int|null The post parent ID. + */ private $post_parent; - private $t_quiz_question = "tutor_quiz_questions"; - private $t_quiz_ques_ans = "tutor_quiz_question_answers"; - private $t_quiz_attempt = "tutor_quiz_attempts"; - private $t_quiz_attempt_ans = "tutor_quiz_attempt_answers"; - public function quiz_with_settings(WP_REST_Request $request) { - $this->post_parent = $request->get_param('id'); + /** + * Quiz questions table name + * + * @var string The table name for quiz questions. + */ + private $t_quiz_question = 'tutor_quiz_questions'; + + /** + * Quiz question answers table name + * + * @var string The table name for quiz question answers. + */ + private $t_quiz_ques_ans = 'tutor_quiz_question_answers'; + + /** + * Quiz question answer options table name + * + * @var string The table name for quiz attempts. + */ + private $t_quiz_attempt = 'tutor_quiz_attempts'; + + /** + * Quiz attempt answers table name + * + * @var string The table name for quiz attempt answers. + */ + private $t_quiz_attempt_ans = 'tutor_quiz_attempt_answers'; + + /** + * Get quiz with settings. + * + * @since 1.7.1 + * + * @param WP_REST_Request $request REST request object. + * + * @return mixed + */ + public function quiz_with_settings( WP_REST_Request $request ) { + $this->post_parent = $request->get_param( 'id' ); global $wpdb; - $table = $wpdb->prefix."posts"; + $table = $wpdb->prefix . 'posts'; $quizs = $wpdb->get_results( - $wpdb->prepare("SELECT ID, post_title, post_content, post_name FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent) + $wpdb->prepare( + "SELECT + ID, + post_title, + post_content, + post_name + FROM $table + WHERE post_type = %s + AND post_parent = %d + ", + $this->post_type, + $this->post_parent + ) ); - $data = []; + $data = array(); - if (count($quizs)>0) { - foreach ($quizs as $quiz) { - $quiz->quiz_settings = get_post_meta($quiz->ID,'tutor_quiz_option',false); + if ( count( $quizs ) > 0 ) { + foreach ( $quizs as $quiz ) { + $quiz->quiz_settings = get_post_meta( $quiz->ID, 'tutor_quiz_option', false ); - array_push($data, $quiz); + array_push( $data, $quiz ); $response = array( - 'status_code'=> 'success', - 'message'=> __("Quiz retrieved successfully",'tutor'), - 'data'=> $data + 'status_code' => 'success', + 'message' => __( 'Quiz retrieved successfully', 'tutor' ), + 'data' => $data, ); } - return self::send($response); - } + return self::send( $response ); + } + $response = array( - 'status_code'=> 'not_found', - 'message'=> __("Quiz not found for given ID",'tutor'), - 'data'=> $data + 'status_code' => 'not_found', + 'message' => __( 'Quiz not found for given ID', 'tutor' ), + 'data' => $data, ); - return self::send($response); + return self::send( $response ); } - public function quiz_question_ans(WP_REST_Request $request) { + /** + * Get quiz question and answers. + * + * @param WP_REST_Request $request REST request object. + * + * @return mixed + */ + public function quiz_question_ans( WP_REST_Request $request ) { global $wpdb; - $this->post_parent = $request->get_param('id'); - + $this->post_parent = $request->get_param( 'id' ); - $q_t = $wpdb->prefix.$this->t_quiz_question;//question table + $q_t = $wpdb->prefix . $this->t_quiz_question; // question table - $q_a_t = $wpdb->prefix.$this->t_quiz_ques_ans;//question answer table + $q_a_t = $wpdb->prefix . $this->t_quiz_ques_ans; // question answer table $quizs = $wpdb->get_results( - $wpdb->prepare("SELECT question_id,question_title, question_description, question_type, question_mark, question_settings FROM $q_t WHERE quiz_id = %d", $this->post_parent) - ); - $data = []; - - if (count($quizs)>0) { + $wpdb->prepare( "SELECT + question_id, + question_title, + question_description, + question_type, + question_mark, + question_settings FROM $q_t + WHERE quiz_id = %d + ", + $this->post_parent + ) + ); + $data = array(); - //get question ans by question_id - foreach ($quizs as $quiz) { - //unserialized question settings - $quiz->question_settings = maybe_unserialize($quiz->question_settings); + if ( count( $quizs ) > 0 ) { + // get question ans by question_id + foreach ( $quizs as $quiz ) { + // un-serialized question settings. + $quiz->question_settings = maybe_unserialize( $quiz->question_settings ); - //question options with correct ans + // question options with correct ans. $options = $wpdb->get_results( - $wpdb->prepare("SELECT answer_title,is_correct FROM $q_a_t WHERE belongs_question_id = %d", $quiz->question_id) + $wpdb->prepare( "SELECT + answer_id, + answer_title, + is_correct FROM $q_a_t + WHERE belongs_question_id = %d + ", + $quiz->question_id + ) ); - //set question_answers as quiz property + // set question_answers as quiz property. $quiz->question_answers = $options; - array_push($data, $quiz); + array_push( $data, $quiz ); } $response = array( - 'status_code'=> 'success', - 'message'=> __('Question retrieved successfully','tutor'), - 'data'=> $data + 'status_code' => 'success', + 'message' => __( 'Question retrieved successfully', 'tutor' ), + 'data' => $data, ); - return self::send($response); + return self::send( $response ); } $response = array( - 'status_code'=> 'not_found', - 'message'=> __('Question not found for given ID','tutor'), - 'data'=> [] + 'status_code' => 'not_found', + 'message' => __( 'Question not found for given ID', 'tutor' ), + 'data' => array(), ); - return self::send($response); + return self::send( $response ); } - public function quiz_attempt_details(WP_REST_Request $request) { - $quiz_id = $request->get_param('id'); - + /** + * Get quiz attempt details. + * + * @since 1.7.1 + * + * @param WP_REST_Request $request REST request object. + * + * @return mixed + */ + public function quiz_attempt_details( WP_REST_Request $request ) { global $wpdb; - $quiz_attempt = $wpdb->prefix.$this->t_quiz_attempt; + + $quiz_id = $request->get_param( 'id' ); + + $quiz_attempt = $wpdb->prefix . $this->t_quiz_attempt; $attempts = $wpdb->get_results( - $wpdb->prepare("SELECT att.user_id,att.total_questions,att.total_answered_questions,att.total_marks,att.earned_marks,att.attempt_info,att.attempt_status,att.attempt_started_at,att.attempt_ended_at,att.is_manually_reviewed,att.manually_reviewed_at FROM $quiz_attempt att WHERE att.quiz_id = %d", $quiz_id) + $wpdb->prepare( + "SELECT + att.user_id, + att.total_questions, + att.total_answered_questions, + att.total_marks, + att.earned_marks, + att.attempt_info, + att.attempt_status, + att.attempt_started_at, + att.attempt_ended_at, + att.is_manually_reviewed, + att.manually_reviewed_at + FROM $quiz_attempt att + WHERE att.quiz_id = %d + ", + $quiz_id + ) ); - - if (count($attempts)>0) { - //unserialize each attempt info - foreach ($attempts as $key => $attempt) { - $attempt->attempt_info = maybe_unserialize($attempt->attempt_info); - //attach attempt ans - $answers = $this->get_quiz_attemp_ans($quiz_id); - - if($answers !==false) - { + + if ( count( $attempts ) > 0 ) { + // unserialize each attempt info. + foreach ( $attempts as $key => $attempt ) { + $attempt->attempt_info = maybe_unserialize( $attempt->attempt_info ); + // attach attempt ans. + $answers = $this->get_quiz_attempt_ans( $quiz_id ); + + if ( false !== $answers ) { $attempt->attempts_answer = $answers; + } else { + $attempt->attempts_answer = array(); } - else - { - $attempt->attempts_answer = []; - } - } $response = array( - 'status_code'=> 'success', - 'message'=> __('Quiz attempts retrieved successfully','tutor'), - 'data'=> $attempts + 'status_code' => 'success', + 'message' => __( 'Quiz attempts retrieved successfully', 'tutor' ), + 'data' => $attempts, ); - return self::send($response); + return self::send( $response ); } + $response = array( - 'status_code'=> 'not_found', - 'message'=> __('Quiz attempts not found for given ID','tutor'), - 'data'=> [] + 'status_code' => 'not_found', + 'message' => __( 'Quiz attempts not found for given ID', 'tutor' ), + 'data' => array(), ); - return self::send($response); + return self::send( $response ); } - /* - *required quiz_id - *return attempts ans - */ - protected function get_quiz_attemp_ans($quiz_id) { + /** + * Get quiz attempt answers. + * + * @since 1.7.1 + * + * @param int $quiz_id quiz id. + * + * @return mixed + */ + protected function get_quiz_attempt_ans( $quiz_id ) { global $wpdb; - $quiz_attempt_ans = $wpdb->prefix.$this->t_quiz_attempt_ans; - $quiz_question = $wpdb->prefix.$this->t_quiz_question; - //get attempt answers + $quiz_attempt_ans = $wpdb->prefix . $this->t_quiz_attempt_ans; + $quiz_question = $wpdb->prefix . $this->t_quiz_question; + + // get attempt answers. $answers = $wpdb->get_results( - $wpdb->prepare("SELECT q.question_title,att_ans.given_answer,att_ans.question_mark,att_ans.achieved_mark,att_ans.minus_mark,att_ans.is_correct FROM $quiz_attempt_ans as att_ans JOIN $quiz_question q ON q.question_id = att_ans.question_id WHERE att_ans.quiz_id = %d",$quiz_id) + $wpdb->prepare( + "SELECT + q.question_title, + att_ans.given_answer, + att_ans.question_mark, + att_ans.achieved_mark, + att_ans.minus_mark, + att_ans.is_correct FROM $quiz_attempt_ans as att_ans + JOIN $quiz_question q ON q.question_id = att_ans.question_id + WHERE att_ans.quiz_id = %d + ", + $quiz_id + ) ); - if (count($answers)>0) { - //unserialize each given answer - foreach ($answers as $key => $answer) { - $answer->given_answer = maybe_unserialize($answer->given_answer); + if ( count( $answers ) > 0 ) { + // unserialize each given answer. + foreach ( $answers as $key => $answer ) { + $answer->given_answer = maybe_unserialize( $answer->given_answer ); - if(is_numeric($answer->given_answer) || is_array($answer->given_answer)) - { - $ids = $answer->given_answer; - $ans_title = $this->answer_titles_by_id($ids); + if ( is_numeric( $answer->given_answer ) || is_array( $answer->given_answer ) ) { + $ids = $answer->given_answer; + $ans_title = $this->answer_titles_by_id( $ids ); $answer->given_answer = $ans_title; } } - return $answers; + return $answers; } return false; } - - /* - *require ids (1,2,3) - *return results containing answer title - */ - protected function answer_titles_by_id($id) { + + /** + * Get answer titles by id. + * + * @since 1.7.1 + * + * @param int $id answer id. + * + * @return mixed + */ + protected function answer_titles_by_id( $id ) { global $wpdb; - $table = $wpdb->prefix.$this->t_quiz_ques_ans; + $table = $wpdb->prefix . $this->t_quiz_ques_ans; - if(is_array($id)) { - $string = implode(',', $id); - $array=array_map('intval', explode(',', $string)); - $array = implode("','",$array); + if ( is_array( $id ) ) { + $array = QueryHelper::prepare_in_clause( $id ); $results = $wpdb->get_results( - "SELECT answer_title FROM $table WHERE answer_id IN ('".$array."')" - ); + "SELECT + answer_title + FROM $table + WHERE + answer_id IN ('" . $array . "')" + ); } else { $results = $wpdb->get_results( - "SELECT answer_title FROM $table WHERE answer_id = {$id}" + "SELECT + answer_title + FROM $table + WHERE answer_id = {$id}" ); } return $results; - } + } } diff --git a/restapi/REST_Rating.php b/restapi/REST_Rating.php index 1528e8e799..1aae3a438b 100644 --- a/restapi/REST_Rating.php +++ b/restapi/REST_Rating.php @@ -1,60 +1,84 @@ + * @link https://themeum.com + * @since 1.7.1 + */ namespace TUTOR; + use WP_REST_Request; -use WP_Comment_Query; -if(!defined( 'ABSPATH' )) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} +/** + * Class REST_Rating + * + * @package Tutor + * @since 1.0.0 + */ class REST_Rating { + + /** + * Course response trait + * + * @since 1.7.1 + */ use REST_Response; + /** + * Course ID. + * + * @since 1.7.1 + * + * @var int $post_id The ID of the course. + */ private $post_id; - private $post_type = "tutor_course_rating"; - - /* - *require course id - *return comment/review with meta by course id and post type - */ - public function course_rating(WP_REST_Request $request) { - $this->post_id = $request->get_param('id'); - - global $wpdb; - $t_comment = $wpdb->prefix."comments"; - $t_commentmeta = $wpdb->prefix."commentmeta"; - - $ratings = $wpdb->get_results( - $wpdb->prepare( - "SELECT c.comment_author,c.comment_author_email,comment_date, - comment_content,comment_approved, cm.meta_value as rating - FROM $t_comment as c JOIN $t_commentmeta as cm ON cm.comment_id = c.comment_ID - WHERE c.comment_post_ID = %d AND c.comment_type = %s ", - $this->post_id,$this->post_type - )); - - if (count($ratings)>0) { + /** + * Post type for course ratings. + * + * @since 1.7.1 + * + * @var string $post_type The post type for course ratings. + */ + private $post_type = 'tutor_course_rating'; + + /** + * Retrieve course ratings via REST API. + * + * @since 1.7.1 + * + * @param WP_REST_Request $request The REST request object. + * + * @return mixed + */ + public function course_rating( WP_REST_Request $request ) { + $this->post_id = $request->get_param( 'id' ); + + $ratings = tutor_utils()->get_course_rating( $this->post_id ); + + if ( count( $ratings ) > 0 ) { $response = array( - 'status_code'=> 'success', - 'message'=> __('Course rating retrieved successfully','tutor'), - 'data'=> $ratings + 'status_code' => 'success', + 'message' => __( 'Course rating retrieved successfully', 'tutor' ), + 'data' => $ratings, ); - return self::send($response); + return self::send( $response ); } - + $response = array( - 'status_code'=> 'not_found', - 'message'=> __('Rating not found for given ID','tutor'), - 'data'=> [] + 'status_code' => 'not_found', + 'message' => __( 'Rating not found for given ID', 'tutor' ), + 'data' => array(), ); - return self::send($response); + return self::send( $response ); } } diff --git a/restapi/REST_Response.php b/restapi/REST_Response.php index 51ebc5da84..eea2b15eb9 100644 --- a/restapi/REST_Response.php +++ b/restapi/REST_Response.php @@ -1,21 +1,34 @@ + * @link https://themeum.com + * @since 1.7.1 + */ namespace TUTOR; + use WP_REST_Response; -if( ! defined('ABSPATH')) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} trait REST_Response { - /* - @send WP_REST_Response with - code, message along with data - */ - public static function send(array $response) { - return new WP_REST_Response($response); - } + + /** + * Send response + * + * @since 1.7.1 + * + * @param array $response The response data. + * + * @return WP_REST_Response + */ + public static function send( array $response ) { + $response = new WP_REST_Response( $response ); + return apply_filters( 'tutor_rest_response', $response ); + } } diff --git a/restapi/REST_Topic.php b/restapi/REST_Topic.php index 5e9e021c08..b45f7e6391 100644 --- a/restapi/REST_Topic.php +++ b/restapi/REST_Topic.php @@ -1,52 +1,80 @@ + * @link https://themeum.com + * @since 1.7.1 + */ namespace TUTOR; + use WP_REST_Request; -use WP_Query; -if(!defined('ABSPATH')) -exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} +/** + * Class REST_Topic + * + * @package Tutor + * + * @since 1.7.1 + */ class REST_Topic { use REST_Response; + /** + * Post parent ID. + * + * @var int $post_parent The ID of the post parent. + */ private $post_parent; - private $post_type = "topics"; - /* - *require rest request - *return topic by course id - */ - public function course_topic(WP_REST_Request $request) { - $this->post_parent = $request->get_param('id'); + /** + * Post type. + * + * @var string $post_type The post type for topics. + */ + private $post_type = 'topics'; + + /** + * Retrieve topics by course ID via REST API. + * + * @param WP_REST_Request $request The REST request object. + * + * @since 1.7.1 + * + * @return mixed + */ + public function course_topic( WP_REST_Request $request ) { + $this->post_parent = $request->get_param( 'id' ); global $wpdb; - $table = $wpdb->prefix."posts"; + $table = $wpdb->prefix . 'posts'; $result = $wpdb->get_results( - $wpdb->prepare("SELECT ID, post_title, post_content, post_name FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent) + $wpdb->prepare( "SELECT ID, post_title, post_content, post_name FROM $table WHERE post_type = %s AND post_parent = %d", $this->post_type, $this->post_parent ) ); - if (count($result)>0) { + if ( count( $result ) > 0 ) { $response = array( - 'status_code'=> "get_topic", - "message"=> __('Topic retrieved successfully','tutor'), - 'data'=> $result - ); - - return self::send($response); + 'status_code' => 'get_topic', + 'message' => __( 'Topic retrieved successfully', 'tutor' ), + 'data' => $result, + ); + + return self::send( $response ); } $response = array( - 'status_code'=> "not_found", - "message"=> __('Topic not found for given ID','tutor'), - 'data'=> [] - ); - - return self::send($response); + 'status_code' => 'not_found', + 'message' => __( 'Topic not found for given ID', 'tutor' ), + 'data' => array(), + ); + + return self::send( $response ); } } diff --git a/restapi/RestAuth.php b/restapi/RestAuth.php index b3cb2eadda..2df5f59c80 100644 --- a/restapi/RestAuth.php +++ b/restapi/RestAuth.php @@ -153,7 +153,7 @@ public static function update_api_permission() { $info = QueryHelper::get_row( $wpdb->usermeta, array( 'umeta_id' => $meta_id ), 'umeta_id' ); $meta_value = json_decode( $info->meta_value ); - $meta_value->permission = $permission; + $meta_value->permission = $permission; $meta_value->description = $description; // Update user meta. diff --git a/templates/dashboard/purchase_history.php b/templates/dashboard/purchase_history.php index 11768ad28e..35972dad96 100644 --- a/templates/dashboard/purchase_history.php +++ b/templates/dashboard/purchase_history.php @@ -69,6 +69,12 @@ 'filter_calendar' => true, ); +?> +