table = acadlix()->helper()->acadlix_wp_prefix('posts'); // Posts table is used for all post types } public function scopeOfQuiz($query) { return $query->where('post_type', self::$postType); } public function getCategoryAttribute() { return acadlix()->model()->category()->get_quiz_category($this->ID); } public function getLanguagesAttribute() { return acadlix()->model()->language()->get_quiz_languages($this->ID); } public function getRenderedPostContentAttribute() { return acadlix()->helper()->renderShortCode($this->post_content); } public function getQuizAttemptsAttribute() { return acadlix() ->model() ->userActivityMeta() ->ofQuiz() ->ofQuizAttempt() ->where('type_id', $this->ID) ->whereRaw("meta_value REGEXP '^-?[0-9]+(\\.[0-9]+)?$'") ->sum('meta_value'); } public function quiz_shortcode() { return $this->hasOne(acadlix()->model()->quizShortcode(), 'quiz_id', 'ID'); } public function metas() { return $this->hasMany(acadlix()->model()->wpPostMeta(), 'post_id', 'ID'); } public function quiz_categories() { return $this->belongsToMany( acadlix()->model()->wpTermTaxonomy(), acadlix()->helper()->acadlix_wp_prefix('term_relationships'), 'object_id', 'term_taxonomy_id' ) ->where('taxonomy', self::$categoryTaxonomy) ->with('term'); } public function getRenderedMetasAttribute() { $metas = $this->metas; if (empty($metas)) { return []; } $keyValueArray = []; foreach ($metas as $meta) { // Ensure meta_key and meta_value exist in the object if (isset($meta['meta_key'], $meta['meta_value'])) { $key = $meta['meta_key']; $value = $meta['meta_value']; // Decode JSON if applicable if (is_string($value) && $decoded = json_decode($value, true)) { $value = $decoded; } $keyValueArray[$key] = $value; } } $renderedMetas = !empty($keyValueArray) && is_array($keyValueArray) ? acadlix()->helper()->cpt()->acadlix_remome_prefix_meta_keys($keyValueArray, 'quiz') : []; // if(isset($renderedMetas[])) return $renderedMetas; } public function createShuffledArray($number, $preserveLast = false, $ensureDifferent = false) { $original = range(0, $number - 1); // For arrays that are too small to shuffle differently, return the original. if ($number <= 1) { return $original; } if ($preserveLast && $number <= 2) { // With 2 elements and preserving the last, there's only one possible order. return $original; } // Define a closure for performing the Fisher-Yates shuffle. // It shuffles the entire array or only the portion excluding the last element if $preserveLast is true. $shuffleFunction = function ($arr, $preserveLast) { $n = count($arr); if ($preserveLast) { $lastIndex = $n - 1; // The index of the last element to be preserved. // Shuffle indices 0 to lastIndex - 1 for ($i = $lastIndex - 1; $i >= 0; $i--) { $j = wp_rand(0, $i); // Swap the elements at indices $i and $j. $temp = $arr[$i]; $arr[$i] = $arr[$j]; $arr[$j] = $temp; } } else { // Shuffle the entire array. for ($i = $n - 1; $i > 0; $i--) { $j = wp_rand(0, $i); $temp = $arr[$i]; $arr[$i] = $arr[$j]; $arr[$j] = $temp; } } return $arr; }; // If we need to ensure the shuffled array is different from the original, // use a loop to reshuffle until the two arrays differ or until a max attempt is reached. if ($ensureDifferent) { $attempt = 0; $maxAttempts = 100; // Prevents an infinite loop in edge cases. do { $shuffled = $shuffleFunction($original, $preserveLast); $attempt++; } while ($shuffled === $original && $attempt < $maxAttempts); } else { // Otherwise, perform a single shuffle. $shuffled = $shuffleFunction($original, $preserveLast); } return $shuffled; } public function applyShuffleOrder(array $array = [], array $order = []): array { $shuffled = []; // Iterate through each index provided in the shuffle order. foreach ($order as $index) { // Check if the index exists in the original array. if (array_key_exists($index, $array)) { $shuffled[] = $array[$index]; } else { // Optionally, handle cases where the index is not valid. // Here, we'll simply skip it. continue; } } return $shuffled; } public function getRenderedQuestionsAttribute() { $metas = $this->rendered_metas; $show_only_specific_number_of_questions = $metas['quiz_settings']['show_only_specific_number_of_questions'] ?? false; $specific_number_of_questions = $metas['quiz_settings']['specific_number_of_questions'] ?? 0; $random_question = $metas['quiz_settings']['random_question'] ?? false; $sort_by_subject = $metas['quiz_settings']['sort_by_subject'] ?? false; $random_option = $metas['quiz_settings']['random_option'] ?? false; $do_not_randomize_last_option = $metas['quiz_settings']['do_not_randomize_last_option'] ?? false; $questions = $this->questions() ->when($random_question, function ($query) { // reorder() wipes out the default ->orderBy('sort') from the relationship return $query->reorder()->inRandomOrder(); }) ->get(); if ($sort_by_subject) { $questions = $questions->sortByDesc(fn($q) => optional($q->subject)->subject_name ?? '')->values(); } if ($show_only_specific_number_of_questions) { $questions = $questions->slice(0, $specific_number_of_questions); } if (count($questions) > 0) { foreach ($questions as $question) { $answer_type = $question->answer_type; $question_languages = $question->question_languages; $shuffled_order = []; foreach ($question_languages as $question_language) { if (in_array($answer_type, ['sortingChoice'])) { $value = $question_language->answer_data[$answer_type]; if (count($shuffled_order) == 0) { $shuffled_order = $this->createShuffledArray(count($value), false, true); } $answerData = $question_language->answer_data; $answerData[$answer_type] = $this->applyShuffleOrder($value, $shuffled_order); $question_language->answer_data = $answerData; } if (in_array($answer_type, ['singleChoice', 'multipleChoice']) && $random_option) { $value = $question_language->answer_data[$answer_type]; if (count($shuffled_order) == 0) { $shuffled_order = $this->createShuffledArray(count($value), $do_not_randomize_last_option, false); } $answerData = $question_language->answer_data; $answerData[$answer_type] = $this->applyShuffleOrder($value, $shuffled_order); $question_language->answer_data = $answerData; } } $question->question_languages = $question_languages; } } return $questions; } public function questions() { return $this->hasMany(acadlix()->model()->question(), "quiz_id", "ID")->ofOnline()->orderBy("sort"); } public function getQuestionsAttribute() { return acadlix()->model()->question()->ofOnline()->where('quiz_id', $this->ID)->get(); } public function getQuestionsCountAttribute() { return count($this->questions); } public function author() { return $this->belongsTo(acadlix()->model()->wpUsers(), 'post_author', 'ID'); } public static function insertQuiz(array $data, array $meta = []) { $data['post_content'] = wp_slash($data['post_content'] ?? ""); $data = wp_parse_args($data, [ 'post_title' => '', 'post_content' => '', 'post_status' => 'publish', 'post_type' => self::$postType, 'post_author' => 1, ]); // Add meta data to the 'meta_input' argument for wp_insert_post. if (!empty($meta)) { $data['meta_input'] = wp_slash($meta); } // Insert the post and return the ID or WP_Error. $postId = wp_insert_post($data); return $postId; } public static function updateQuiz(int $postId, array $data = [], array $meta = []) { $data['post_content'] = wp_slash($data['post_content'] ?? ""); // Parse default arguments for the quiz update. $data = wp_parse_args($data, [ 'ID' => $postId, ]); // return $data; // Add meta data to the 'meta_input' argument for wp_update_post. if (!empty($meta)) { $data['meta_input'] = wp_slash($meta); } // Update the post and return the result or WP_Error. $result = wp_update_post($data, true); return $result; } public static function updateQuizSettings(int $postId, array $settingsToUpdate = []) { $setting_name = self::$metaPrefix . "quiz_settings"; // Fetch current quiz_settings $quizSettingsSerialized = get_post_meta($postId, $setting_name, true); // Unserialize to get an array $quizSettings = maybe_unserialize($quizSettingsSerialized); // Ensure it's an array before modifying if (!is_array($quizSettings)) { $quizSettings = []; } // Update the settings foreach ($settingsToUpdate as $key => $value) { $quizSettings[$key] = $value; } // Serialize and save the updated settings $updated = update_post_meta($postId, $setting_name, wp_slash($quizSettings)); return (bool) $updated; } public static function deleteQuiz(int $postId) { // Check if post exists $post = get_post($postId); if (!$post || $post->post_type !== self::$postType) { return new \WP_Error('invalid_post', __('Invalid post ID or not a quiz post type.', 'acadlix')); } // Delete post $result = wp_delete_post($postId, true); if (!$result) { return new \WP_Error('delete_failed', __('Failed to delete quiz.', 'acadlix')); } // Delete Question acadlix()->model()->question()->where("quiz_id", $postId)->delete(); // Delete Statistic acadlix()->model()->statisticRef()->where("quiz_id", $postId)->delete(); // Delete Toplist acadlix()->model()->toplist()->where("quiz_id", $postId)->delete(); // Delete User Meta Activity data acadlix()->model()->userActivityMeta()->ofQuiz() ->where("type_id", $postId) ->delete(); // Delete Course Section Content $courseSectionContents = acadlix()->model()->courseSectionContent()->getByQuizId($postId); if ($courseSectionContents) { foreach ($courseSectionContents as $csc) { acadlix()->model()->courseSectionContent()->deleteCourseSectionContent($csc->ID); } } return true; } public static function assignCategory(int $postId, int|array $categoryIds) { // Check if post exists $post = get_post($postId); if (!$post || $post->post_type !== self::$postType) { return new \WP_Error('invalid_post', __('Invalid post ID or not a quiz post type.', 'acadlix')); } if (!is_array($categoryIds)) { $categoryIds = [$categoryIds]; } $categoryIds = array_map('intval', $categoryIds); $categoryIds = array_unique($categoryIds); $term_taxonomy_ids = wp_set_object_terms($postId, $categoryIds, self::$categoryTaxonomy); return $term_taxonomy_ids; } public static function assignLanguage(int $postId, int|array $languageIds) { // Check if post exists $post = get_post($postId); if (!$post || $post->post_type !== self::$postType) { return new \WP_Error('invalid_post', __('Invalid post ID or not a quiz post type.', 'acadlix')); } if (!is_array($languageIds)) { $languageIds = [$languageIds]; } $languageIds = array_map('intval', $languageIds); $languageIds = array_unique($languageIds); $term_taxonomy_ids = wp_set_object_terms($postId, $languageIds, self::$languageTaxonomy); return $term_taxonomy_ids; } } }