app = $app; $enabled = $this->isEnabled(); $this->addToGlobalMenu($enabled); if (!$enabled) { return; } $this->addToFormSettingsMenu(); $this->maybeRandomize(); $this->registerAjaxHandlers(); add_filter('fluentform/all_editor_shortcodes', function ($shortCodes) { $shortCodes[] = [ 'title' => __('Quiz', 'fluentformpro'), 'shortcodes' => [ '{quiz_result}' => 'Quiz Result Table' ] ]; return $shortCodes; }); new QuizScoreComponent(); add_filter('safe_style_css', function ($styles) { $style_tags = ['display']; $style_tags = apply_filters('fluentform/allowed_css_properties', $style_tags); foreach ($style_tags as $tag) { $styles[] = $tag; } return $styles; }); add_filter('fluentform/shortcode_parser_callback_quiz_result', [$this, 'getQuizResultTable'], 10, 2); add_filter('fluentform/form_submission_confirmation', [$this, 'maybeAppendResult'], 10, 3); add_filter('fluentform/submission_cards', [$this, 'pushQuizResult'], 10, 3); } public function registerAjaxHandlers() { $this->app->addAdminAjaxAction('ff_get_quiz_module_settings', [$this, 'getSettingsAjax']); $this->app->addAdminAjaxAction('ff_store_quiz_module_settings', [$this, 'saveSettingsAjax']); $this->app->addAdminAjaxAction('ff_delete_quiz_module_settings', [$this, 'deleteSettingsAjax']); } public function getSettingsAjax() { $formId = intval($_REQUEST['form_id']); Acl::verify('fluentform_forms_manager', $formId); $settings = $this->getSettings($formId); wp_send_json_success([ 'settings' => $settings, 'quiz_fields' => $this->getQuizFields($formId), 'settings_fields' => QuizController::getIntegrationFields(), ]); } public function saveSettingsAjax() { $formId = intval($_REQUEST['form_id']); Acl::verify('fluentform_forms_manager', $formId); $settings = $_REQUEST['settings']; $formattedSettings = wp_unslash($settings); Helper::setFormMeta($formId, $this->metaKey, $formattedSettings); wp_send_json_success([ 'message' => __('Settings successfully updated', 'fluentformpro'), ]); } public function deleteSettingsAjax() { $formId = intval($_REQUEST['form_id']); Acl::verify('fluentform_forms_manager', $formId); Helper::deleteFormMeta($formId, $this->metaKey); wp_send_json_success([ 'message' => __('Settings deleted successfully', 'fluentformpro'), ]); } public function getSettings($formId) { $settings = Helper::getFormMeta($formId, $this->metaKey, []); $form = $this->getForm($formId); $fields = $this->getQuizFields($form); $resultType = self::getScoreType($form); $defaults = [ 'enabled' => false, 'randomize_answer' => false, 'append_result' => true, 'randomize_question' => false, 'saved_quiz_fields' => $fields, 'grades' => [ [ 'label' => 'A', 'min' => 90, 'max' => 100, ], [ 'label' => 'B', 'min' => 80, 'max' => 89, ], [ 'label' => 'C', 'min' => 70, 'max' => 79, ], ] ]; $settings = $this->removeDeletedFields($settings, $fields); $settings = wp_parse_args($settings, $defaults); $settings['saved_quiz_fields'] = empty($settings['saved_quiz_fields']) ? [] : $settings['saved_quiz_fields']; $settings['result_type'] = $resultType; return $settings; } protected function getForm($formId) { return wpFluent()->table('fluentform_forms')->find($formId); } public static function getIntegrationFields() { return [ [ 'key' => 'append_result', 'label' => 'Append Result', 'component' => 'checkbox-single', 'checkbox_label' => __('Show Result on confirmation page', 'fluentformpro') ], [ 'key' => 'randomize_question', 'label' => 'Randomize Questions', 'checkbox_label' => __('Questions will be randomized each time its loaded', 'fluentformpro'), 'component' => 'checkbox-single' ], [ 'key' => 'randomize_answer', 'label' => 'Randomize Options', 'checkbox_label' => __('Options will be randomized each time its loaded', 'fluentformpro'), 'component' => 'checkbox-single' ], ]; } public function isEnabled() { $globalModules = get_option('fluentform_global_modules_status'); $quizAddon = Arr::get($globalModules, $this->key); if ($quizAddon == 'yes') { return true; } return false; } public function addToGlobalMenu($enabled) { add_filter('fluentform/global_addons', function ($addOns) use ($enabled) { $addOns[$this->key] = [ 'title' => 'Quiz Module', 'description' => __('With this module, you can create quizzes and show scores with grades, points, fractions, or percentages', 'fluentformpro'), 'logo' => fluentFormMix('img/integrations/quiz-icon.svg'), 'enabled' => ($enabled) ? 'yes' : 'no', 'config_url' => '', 'category' => '' ]; return $addOns; }, 9); } public function addToFormSettingsMenu() { add_filter('fluentform/form_settings_menu', function ($menu) { $menu['quiz_settings'] = [ 'title' => __('Quiz Settings', 'fluentform'), 'slug' => 'form_settings', 'hash' => 'quiz_settings', 'route' => '/quiz_settings' ]; return $menu; }); } /** * Maybe Randomize Questions and Answers * * @return void */ public function maybeRandomize() { add_filter('fluentform/rendering_form', function ($form) { $settings = $this->getSettings($form->id); $enabled = $settings['enabled'] == 'yes'; if (!$enabled) { return $form; } if ($settings['randomize_answer']) { $this->randomizeCheckableInputs(); } if (!$settings['randomize_question']) { return $form; } $fields = $form->fields; $quizFields = $this->getQuizFields($form); $quizFieldsKeys = array_keys($quizFields); $formQuizFields = array_filter($fields['fields'], function ($field) use ($quizFieldsKeys) { return in_array(Arr::get($field, 'attributes.name'), $quizFieldsKeys); }); if (empty($formQuizFields)) { return $form; } $quizGroup = []; $i = 0; foreach ($formQuizFields as $key => $field) { $inSequence = isset($formQuizFields[$key + 1]) ? $formQuizFields[$key + 1] : false; if ($inSequence) { $quizGroup[$i][$key] = $field; } else { $quizGroup[$i][$key] = $field; $i++; } } //shuffle groups and replace their positions in the original array foreach ($quizGroup as $group) { $startIndex = Arr::get(array_keys($group), '0'); shuffle($group); $length = count($group); array_splice($fields['fields'], $startIndex, $length, $group); } $form->fields = $fields; return $form; }, 10, 1); } /** * Generate Quiz Result Table * * @param $shortCode * @param ShortCodeParser $parser * * @return string|void */ public function getQuizResultTable($shortCode, ShortCodeParser $parser) { $form = $parser::getForm(); $entry = $parser::getEntry(); $quizSettings = $this->getSettings($form->id); $quizFields = Arr::get($quizSettings,'saved_quiz_fields'); if (!$entry || !$form || $quizSettings['enabled'] != 'yes') { return; } $scoreType = self::getScoreType($form); $response = json_decode($entry->response, true); $results = $this->getFormattedResults($quizSettings, $response, $form); /* For full width in single entry page */ $width = defined('FLUENTFORM_RENDERING_ENTRIES') ? '' : ' width="600"'; $html = '
|
{$rightWrongHtml} {$result['label']}
|
| %s | ", $userValueFormatted ); $correctAnsFormatted = is_array($result['correct_value']) ? join(', ', $result['correct_value']) : $result['correct_value']; //skip right wrong for when total point is selected if ($scoreType == 'total_point') { $score = 0; if ($result['has_advance_scoring'] == 'yes') { $score = $result['advance_points_score']; } else { if ($result['correct']) { $score = $result['points']; } } $html .= sprintf( "
| %s : %s | ", __('Point', 'fluentformpro'), $score ); } else if (!$result['correct'] && $scoreType != 'total_point' && $scoreType != 'personality') { $conditionText = ''; if ($result['correct_ans_condition'] == 'not_includes') { $conditionText = 'does Not includes '; } elseif ($result['correct_ans_condition'] == 'includes_any') { $conditionText = 'any of the following '; } elseif ($result['correct_ans_condition'] == 'includes_all') { $conditionText = 'all of the following '; } if($rightWrongHtml){ $html .= sprintf( "
| %s %s: %s | ", __('Correct answer', 'fluentformpro'), $conditionText, $correctAnsFormatted ); } } $html .= '
| %s |
| %s | ", $result ); $html .= '
{quiz_result}
'; $resultHtml = ShortCodeParser::parse( $contents, $submission->id, $entryData, $form ); $widgetData = [ 'title' => __('Quiz Result', 'fluentformpro'), 'content' => $resultHtml ]; $cards['quiz_result'] = $widgetData; return $cards; } /** * Wrong Answer Icon * * @return string */ protected static function getWrongIcon($forPdf) { $icon = ''; if ($forPdf) { $icon = ''; } return apply_filters('fluentform/quiz_wrong_ans_icon', $icon); } /** * Right Answer Icon * * @return string */ protected static function getRightIcon($forPdf) { $icon = ''; if ($forPdf) { $icon = ''; } return apply_filters('fluentform/quiz_right_ans_icon', $icon); } public function advancePoints($options) { if (!isset($options['options'])) { return (object)[]; } $formattedOptions = []; foreach ($options['options'] as $key => $value) { $formattedOptions[$key] = 0; } return $formattedOptions; } private function calcAdvancePoints($settings, $userValue) { $hasAdvancePoint = Arr::get($settings, 'has_advance_scoring'); if ($hasAdvancePoint) { $advancePoints = Arr::get($settings, 'advance_points'); $userValue = is_array($userValue) ? $userValue : [$userValue]; $total = 0; foreach ($userValue as $value) { $value = Arr::get($advancePoints, $value, 0); if (!is_numeric($value)) { continue; } $total += $value; } return $total; } else { return Arr::get($settings, 'points'); } } private static function getScoreType($form) { $scoreType = ''; $scoreInput = self::getScoreInput($form); if ($scoreInput) { $scoreType = Arr::get($scoreInput, 'raw.settings.result_type'); } return $scoreType; } public static function getScoreInput($form){ $scoreInput = \FluentForm\App\Modules\Form\FormFieldsParser::getInputsByElementTypes($form,['quiz_score'],['raw']); if ($scoreInput) { return array_shift($scoreInput); } return false; } public function randomizeCheckableInputs() { add_filter('fluentform/rendering_field_data_input_checkbox', function ($data) { $options = $data['settings']['advanced_options']; shuffle($options); $data['settings']['advanced_options'] = $options; return $data; }, 10, 1); add_filter('fluentform/rendering_field_data_input_radio', function ($data) { $options = $data['settings']['advanced_options']; shuffle($options); $data['settings']['advanced_options'] = $options; return $data; }, 10, 1); } }