/**
 * Show the page where the user can set up the certificate settings. 
 */
function WPCW_showPage_Certificates_load()
{
    $page = new PageBuilder(true);
    $page->showPageHeader(__('Training Courses - Certificate Settings', 'wp_courseware'), '75%', WPCW_icon_getPageIconURL());
    $settingsFields = array('section_certificates_defaults' => array('type' => 'break', 'html' => WPCW_forms_createBreakHTML(__('Certificate Settings', 'wp_courseware'))), 'cert_signature_type' => array('label' => __('Signature Type', 'wp_courseware'), 'type' => 'radio', 'cssclass' => 'wpcw_cert_signature_type', 'required' => 'true', 'data' => array('text' => sprintf('<b>%s</b> - %s', __('Text', 'wp_courseware'), __('Just use text for the signature.', 'wp_courseware')), 'image' => sprintf('<b>%s</b> - %s', __('Image File', 'wp_courseware'), __('Use an image for the signature.', 'wp_courseware')))), 'cert_sig_text' => array('label' => __('Name to use for signature', 'wp_courseware'), 'type' => 'text', 'cssclass' => 'wpcw_cert_signature_type_text', 'desc' => __('The name to use for the signature area.', 'wp_courseware'), 'validate' => array('type' => 'string', 'maxlen' => 150, 'minlen' => 1, 'regexp' => '/^[^<>]+$/', 'error' => __('Please enter the name to use for the signature area.', 'wp_courseware'))), 'cert_sig_image_url' => array('label' => __('Your Signature Image', 'wp_courseware'), 'cssclass' => 'wpcw_image_upload_field wpcw_cert_signature_type_image', 'type' => 'text', 'desc' => '&bull;&nbsp;' . __('The URL of your signature image. Using a transparent image is recommended.', 'wp_courseware') . '<br/>&bull;&nbsp;' . sprintf(__('The image must be <b>%d pixels wide, and %d pixels high</b> to render correctly. ', 'wp_courseware'), WPCW_CERTIFICATE_SIGNATURE_WIDTH_PX * 2, WPCW_CERTIFICATE_SIGNATURE_HEIGHT_PX * 2), 'validate' => array('type' => 'url', 'maxlen' => 300, 'minlen' => 1, 'error' => __('Please enter the URL of your signature image.', 'wp_courseware')), 'extrahtml' => sprintf('<a id="cert_sig_image_url_btn" href="#" class="wpcw_insert_image button-secondary" data-uploader_title="%s" data-uploader_btn_text="%s" data-target="cert_sig_image_url"><span class="wpcw_insert_image_img"></span> %s</a>', __('Choose an image to use for the signature image...', 'wp_courseware'), __('Select Image', 'wp_courseware'), __('Select Image', 'wp_courseware'))), 'cert_logo_enabled' => array('label' => __('Show your logo?', 'wp_courseware'), 'cssclass' => 'wpcw_cert_logo_enabled', 'type' => 'radio', 'required' => 'true', 'data' => array('cert_logo' => sprintf('<b>%s</b> - %s', __('Yes', 'wp_courseware'), __('Use your logo on the certificate.', 'wp_courseware')), 'no_cert_logo' => sprintf('<b>%s</b> - %s', __('No', 'wp_courseware'), __('Don\'t show a logo on the certificate.', 'wp_courseware')))), 'cert_logo_url' => array('label' => __('Your Logo Image', 'wp_courseware'), 'type' => 'text', 'cssclass' => 'wpcw_cert_logo_url wpcw_image_upload_field', 'desc' => '&bull;&nbsp;' . __('The URL of your logo image. Using a transparent image is recommended.', 'wp_courseware') . '<br/>&bull;&nbsp;' . sprintf(__('The image must be <b>%d pixels wide, and %d pixels</b> high to render correctly. ', 'wp_courseware'), WPCW_CERTIFICATE_LOGO_WIDTH_PX * 2, WPCW_CERTIFICATE_LOGO_HEIGHT_PX * 2), 'validate' => array('type' => 'url', 'maxlen' => 300, 'minlen' => 1, 'error' => __('Please enter the URL of your logo image.', 'wp_courseware')), 'extrahtml' => sprintf('<a id="cert_logo_url_btn" href="#" class="wpcw_insert_image button-secondary" data-uploader_title="%s" data-uploader_btn_text="%s" data-target="cert_logo_url"><span class="wpcw_insert_image_img"></span> %s</a>', __('Choose an image to use for your logo on the certificate...', 'wp_courseware'), __('Select Image', 'wp_courseware'), __('Select Image', 'wp_courseware'))), 'cert_background_type' => array('label' => __('Certificate Background', 'wp_courseware'), 'cssclass' => 'wpcw_cert_background_type', 'type' => 'radio', 'required' => 'true', 'data' => array('use_default' => sprintf('<b>%s</b> - %s', __('Built-in', 'wp_courseware'), __('Use the built-in certificate background.', 'wp_courseware')), 'use_custom' => sprintf('<b>%s</b> - %s', __('Custom', 'wp_courseware'), __('Use your own certificate background.', 'wp_courseware')))), 'cert_background_custom_url' => array('label' => __('Custom Background Image', 'wp_courseware'), 'type' => 'text', 'cssclass' => 'wpcw_cert_background_custom_url wpcw_image_upload_field', 'desc' => '&bull;&nbsp;' . __('The URL of your background image.', 'wp_courseware') . '<br/>&bull;&nbsp;' . sprintf(__('The background image must be <b>%d pixels wide, and %d pixels</b> high at <b>72 dpi</b> to render correctly. ', 'wp_courseware'), WPCW_CERTIFICATE_BG_WIDTH_PX, WPCW_CERTIFICATE_BG_HEIGHT_PX), 'validate' => array('type' => 'url', 'maxlen' => 300, 'minlen' => 1, 'error' => __('Please enter the URL of your certificate background image.', 'wp_courseware')), 'extrahtml' => sprintf('<a id="cert_background_custom_url_btn" href="#" class="wpcw_insert_image button-secondary" data-uploader_title="%s" data-uploader_btn_text="%s" data-target="cert_background_custom_url"><span class="wpcw_insert_image_img"></span> %s</a>', __('Choose an image to use for the certificate background...', 'wp_courseware'), __('Select Image', 'wp_courseware'), __('Select Image', 'wp_courseware'))));
    $settings = new SettingsForm($settingsFields, WPCW_DATABASE_SETTINGS_KEY, 'wpcw_form_settings_certificates');
    $settings->setSaveButtonLabel(__('Save ALL Settings', 'wp_courseware'));
    $settings->msg_settingsSaved = __('Settings successfully saved.', 'wp_courseware');
    $settings->msg_settingsProblem = __('There was a problem saving the settings.', 'wp_courseware');
    $settings->setAllTranslationStrings(WPCW_forms_getTranslationStrings());
    $settings->show();
    // RHS Support Information
    $page->showPageMiddle('23%');
    // Create a box where the admin can preview the certificates to see what they look like.
    $page->openPane('wpcw-certificates-preview', __('Preview Certificate', 'wp_courseware'));
    printf('<p>%s</p>', __('After saving the settings, you can preview the certificate using the button below. The preview opens in a new window.', 'wp_courseware'));
    printf('<div class="wpcw_btn_centre"><a href="%spdf_create_certificate.php?certificate=preview" target="_blank" class="button-primary">%s</a></div>', WPCW_plugin_getPluginPath(), __('Preview Certificate', 'wp_courseware'));
    $page->closePane();
    WPCW_docs_showSupportInfo($page);
    WPCW_docs_showSupportInfo_News($page);
    WPCW_docs_showSupportInfo_Affiliate($page);
    $page->showPageFooter();
}
/**
 * Show the page where the user can set up the certificate settings. 
 */
function WPCW_showPage_Certificates_load()
{
    $page = new PageBuilder(true);
    $page->showPageHeader(__('Training Courses - Certificate Settings', 'wp_courseware'), '75%', WPCW_icon_getPageIconURL());
    $settingsFields = array('section_certificates_defaults' => array('type' => 'break', 'html' => WPCW_forms_createBreakHTML(__('Certificate Settings', 'wp_courseware'))), 'cert_signature_type' => array('label' => __('Signature Type', 'wp_courseware'), 'type' => 'radio', 'cssclass' => 'wpcw_cert_signature_type', 'required' => 'true', 'data' => array('text' => sprintf('<b>%s</b> - %s', __('Text', 'wp_courseware'), __('Just use text for the signature.', 'wp_courseware')), 'image' => sprintf('<b>%s</b> - %s', __('Image File', 'wp_courseware'), __('Use an image for the signature.', 'wp_courseware')))), 'cert_sig_text' => array('label' => __('Name to use for signature', 'wp_courseware'), 'type' => 'text', 'cssclass' => 'wpcw_cert_signature_type_text', 'desc' => __('The name to use for the signature area.', 'wp_courseware'), 'validate' => array('type' => 'string', 'maxlen' => 150, 'minlen' => 1, 'regexp' => '/^[^<>]+$/', 'error' => __('Please enter the name to use for the signature area.', 'wp_courseware'))), 'cert_sig_image_url' => array('label' => __('Your Signature Image', 'wp_courseware'), 'cssclass' => 'wpcw_image_upload_field wpcw_cert_signature_type_image', 'type' => 'text', 'desc' => '&bull;&nbsp;' . __('The URL of your signature image. Using a transparent image is recommended.', 'wp_courseware') . '<br/>&bull;&nbsp;' . sprintf(__('The image must be <b>%d pixels wide, and %d pixels high</b> to render correctly. ', 'wp_courseware'), WPCW_CERTIFICATE_SIGNATURE_WIDTH_PX * 2, WPCW_CERTIFICATE_SIGNATURE_HEIGHT_PX * 2), 'validate' => array('type' => 'url', 'maxlen' => 300, 'minlen' => 1, 'error' => __('Please enter the URL of your signature image.', 'wp_courseware')), 'extrahtml' => sprintf('<a id="cert_sig_image_url_btn" href="#" class="wpcw_insert_image button-secondary" data-uploader_title="%s" data-uploader_btn_text="%s" data-target="cert_sig_image_url"><span class="wpcw_insert_image_img"></span> %s</a>', __('Choose an image to use for the signature image...', 'wp_courseware'), __('Select Image', 'wp_courseware'), __('Select Image', 'wp_courseware'))), 'cert_logo_enabled' => array('label' => __('Show your logo?', 'wp_courseware'), 'cssclass' => 'wpcw_cert_logo_enabled', 'type' => 'radio', 'required' => 'true', 'data' => array('cert_logo' => sprintf('<b>%s</b> - %s', __('Yes', 'wp_courseware'), __('Use your logo on the certificate.', 'wp_courseware')), 'no_cert_logo' => sprintf('<b>%s</b> - %s', __('No', 'wp_courseware'), __('Don\'t show a logo on the certificate.', 'wp_courseware')))), 'cert_logo_url' => array('label' => __('Your Logo Image', 'wp_courseware'), 'type' => 'text', 'cssclass' => 'wpcw_cert_logo_url wpcw_image_upload_field', 'desc' => '&bull;&nbsp;' . __('The URL of your logo image. Using a transparent image is recommended.', 'wp_courseware') . '<br/>&bull;&nbsp;' . sprintf(__('The image must be <b>%d pixels wide, and %d pixels</b> high to render correctly. ', 'wp_courseware'), WPCW_CERTIFICATE_LOGO_WIDTH_PX * 2, WPCW_CERTIFICATE_LOGO_HEIGHT_PX * 2), 'validate' => array('type' => 'url', 'maxlen' => 300, 'minlen' => 1, 'error' => __('Please enter the URL of your logo image.', 'wp_courseware')), 'extrahtml' => sprintf('<a id="cert_logo_url_btn" href="#" class="wpcw_insert_image button-secondary" data-uploader_title="%s" data-uploader_btn_text="%s" data-target="cert_logo_url"><span class="wpcw_insert_image_img"></span> %s</a>', __('Choose an image to use for your logo on the certificate...', 'wp_courseware'), __('Select Image', 'wp_courseware'), __('Select Image', 'wp_courseware'))), 'cert_background_type' => array('label' => __('Certificate Background', 'wp_courseware'), 'cssclass' => 'wpcw_cert_background_type', 'type' => 'radio', 'required' => 'true', 'data' => array('use_default' => sprintf('<b>%s</b> - %s', __('Built-in', 'wp_courseware'), __('Use the built-in certificate background.', 'wp_courseware')), 'use_custom' => sprintf('<b>%s</b> - %s', __('Custom', 'wp_courseware'), __('Use your own certificate background.', 'wp_courseware')))), 'cert_background_custom_url' => array('label' => __('Custom Background Image', 'wp_courseware'), 'type' => 'text', 'cssclass' => 'wpcw_cert_background_custom_url wpcw_image_upload_field', 'desc' => '&bull;&nbsp;' . __('The URL of your background image.', 'wp_courseware') . '<br/>&bull;&nbsp;' . sprintf(__('The background image must be <b>%d pixels wide, and %d pixels</b> high at <b>72 dpi</b> to render correctly. ', 'wp_courseware'), WPCW_CERTIFICATE_BG_WIDTH_PX, WPCW_CERTIFICATE_BG_HEIGHT_PX), 'validate' => array('type' => 'url', 'maxlen' => 300, 'minlen' => 1, 'error' => __('Please enter the URL of your certificate background image.', 'wp_courseware')), 'extrahtml' => sprintf('<a id="cert_background_custom_url_btn" href="#" class="wpcw_insert_image button-secondary" data-uploader_title="%s" data-uploader_btn_text="%s" data-target="cert_background_custom_url"><span class="wpcw_insert_image_img"></span> %s</a>', __('Choose an image to use for the certificate background...', 'wp_courseware'), __('Select Image', 'wp_courseware'), __('Select Image', 'wp_courseware'))), 'section_encodings' => array('type' => 'break', 'html' => WPCW_forms_createBreakHTML(__('Language and Encoding Settings', 'wp_courseware'))), 'certificate_encoding' => array('label' => __('Certificate Encoding', 'wp_courseware'), 'type' => 'select', 'required' => true, 'desc' => __('Choose a codepage encoding that matches your language to ensure certificates render correctly. You may need an encoding other than <code>ISO-8859-1</code> if you are using a non-English language.', 'wp_courseware'), 'data' => array('ISO-8859-1' => __('ISO-8859-1 - Latin alphabet - North America, Western Europe, Latin America, etc. (Default)', 'wp_courseware'), 'ISO-8859-2' => __('ISO-8859-2 - Latin alphabet 2 - Eastern Europe.', 'wp_courseware'), 'ISO-8859-3' => __('ISO-8859-3 - Latin alphabet 3 - SE Europe, Esperanto', 'wp_courseware'), 'ISO-8859-4' => __('ISO-8859-4 - Latin alphabet 4 - Scandinavia/Baltics', 'wp_courseware'), 'ISO-8859-5' => __('ISO-8859-5 - Latin/Cyrillic - Bulgarian, Belarusian, Russian and Macedonian', 'wp_courseware'), 'ISO-8859-6' => __('ISO-8859-6 - Latin/Arabic - Arabic languages', 'wp_courseware'), 'ISO-8859-7' => __('ISO-8859-7 - Latin/Greek - modern Greek language', 'wp_courseware'), 'ISO-8859-8' => __('ISO-8859-8 - Latin/Hebrew - Hebrew languages', 'wp_courseware'), 'ISO-8859-9' => __('ISO-8859-9 - Latin 5 part 9 - Turkish languages', 'wp_courseware'), 'ISO-8859-10' => __('ISO-8859-10 - Latin 6 Lappish, Nordic, Eskimo - The Nordic languages', 'wp_courseware'), 'ISO-8859-15' => __('ISO-8859-15 - Latin 9 (aka Latin 0) - Similar to ISO 8859-1', 'wp_courseware'))));
    $settings = new SettingsForm($settingsFields, WPCW_DATABASE_SETTINGS_KEY, 'wpcw_form_settings_certificates');
    $settings->setSaveButtonLabel(__('Save ALL Settings', 'wp_courseware'));
    $settings->msg_settingsSaved = __('Settings successfully saved.', 'wp_courseware');
    $settings->msg_settingsProblem = __('There was a problem saving the settings.', 'wp_courseware');
    $settings->setAllTranslationStrings(WPCW_forms_getTranslationStrings());
    $settings->show();
    // RHS Support Information
    $page->showPageMiddle('23%');
    // Create a box where the admin can preview the certificates to see what they look like.
    $page->openPane('wpcw-certificates-preview', __('Preview Certificate', 'wp_courseware'));
    printf('<p>%s</p>', __('After saving the settings, you can preview the certificate using the button below. The preview opens in a new window.', 'wp_courseware'));
    printf('<div class="wpcw_btn_centre"><a href="%spdf_create_certificate.php?certificate=preview" target="_blank" class="button-primary">%s</a></div>', WPCW_plugin_getPluginPath(), __('Preview Certificate', 'wp_courseware'));
    $page->closePane();
    WPCW_docs_showSupportInfo($page);
    WPCW_docs_showSupportInfo_News($page);
    WPCW_docs_showSupportInfo_Affiliate($page);
    $page->showPageFooter();
}
예제 #3
0
/**
 * Creates the course unit post type.
 */
function WPCW_plugin_registerCustomPostTypes()
{
    $labels = array('name' => __('Course Units', 'wp_courseware'), 'singular_name' => __('Course Unit', 'wp_courseware'), 'add_new' => __('Add New', 'wp_courseware'), 'add_new_item' => __('Add New Course Unit', 'wp_courseware'), 'edit_item' => __('Edit Course Unit', 'wp_courseware'), 'new_item' => __('New Course Unit', 'wp_courseware'), 'view_item' => __('View Course Unit', 'wp_courseware'), 'search_items' => __('Search Course Units', 'wp_courseware'), 'not_found' => __('No course units found', 'wp_courseware'), 'not_found_in_trash' => __('No course units found in Trash', 'wp_courseware'), 'parent_item_colon' => __('Parent Course Unit:', 'wp_courseware'), 'menu_name' => __('Course Units', 'wp_courseware'));
    $args = array('labels' => $labels, 'hierarchical' => false, 'supports' => array('title', 'editor', 'revisions'), 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 100, 'menu_icon' => WPCW_plugin_getPluginPath() . 'img/icon_training_16.png', 'show_in_nav_menus' => false, 'publicly_queryable' => true, 'exclude_from_search' => false, 'has_archive' => false, 'query_var' => true, 'can_export' => true, 'rewrite' => false, 'capability_type' => 'post', 'menu_position' => WPCW_MENU_POSITION + 1);
    register_post_type('course_unit', $args);
}
예제 #4
0
    /**
     * Output the form that allows questions to be configured.
     */
    function editForm_toString()
    {
        $answerList = false;
        if ($this->quizItem->question_data_answers) {
            $answerList = WPCW_quizzes_decodeAnswers($this->quizItem->question_data_answers);
        }
        $html = false;
        // Extra CSS for errors
        $errorClass_Question = false;
        $errorClass_CorAnswer = false;
        // Error Check - Have we got an issue with a lack of question?
        if ($this->showErrors) {
            if (!$this->quizItem->question_question) {
                $errorClass_Question = 'wpcw_quiz_missing';
                $this->gotError = true;
            }
            if ($this->needCorrectAnswers && !$this->quizItem->question_correct_answer) {
                $errorClass_CorAnswer = 'wpcw_quiz_missing';
                $this->gotError = true;
            }
        }
        // Track columns needed to show question details
        $columnCount = 4;
        // Render just the question area
        $html .= sprintf('<li id="wpcw_quiz_details_%s" class="%s"><table class="wpcw_quiz_details_questions_wrap" cellspacing="0">', $this->quizItem->question_id, $this->cssClasses);
        // Details of the question - top of the question details.
        $html .= $this->getSection_processHeader($columnCount);
        // Main question details here...
        $html .= sprintf('<tr class="wpcw_quiz_row_question %s">', $errorClass_Question);
        $html .= sprintf('<th>%s</th>', __('Question', 'wp_courseware'));
        $html .= sprintf('<td>');
        $html .= sprintf('<textarea name="question_question_%s">%s</textarea>', $this->quizItem->question_id, htmlspecialchars($this->quizItem->question_question));
        $html .= sprintf('<input type="hidden" name="question_type_%s" value="multi" />', $this->quizItem->question_id);
        // Field storing order of question among other questions
        $html .= sprintf('<input type="hidden" name="question_order_%s" value="%s" class="wpcw_question_hidden_order" />', $this->quizItem->question_id, $this->quizItem->question_order + 0);
        $html .= sprintf('</td>');
        // Only show column if need correct answers.
        $html .= sprintf('<td class="wpcw_quiz_details_tick_correct wpcw_quiz_only_td">%s</td>', __('Correct<br/>Answer?', 'wp_courseware'));
        // Column for add/remove buttons
        $html .= '<td>&nbsp;</td>';
        $html .= sprintf('</tr>');
        // Render the section that allows an image to be shown.
        $html .= $this->getSection_showImageField($columnCount);
        // Render the field that allows answers to be randomized
        $html .= $this->getSection_showRandomizeAnswersField($columnCount);
        // Render the list of answers if we have any.
        if ($answerList) {
            $count = 0;
            $odd = true;
            foreach ($answerList as $answerItem) {
                // Extract image if available
                $answerItemImageVal = WPCW_arrays_getValue($answerItem, 'image');
                // Exract the answer if available
                $answerItemVal = trim($answerItem['answer']);
                $count++;
                // Show an error if the field is still blank.
                $errorClass_Answer = false;
                if ($this->showErrors) {
                    // Check that answer contains some characters.
                    if (strlen($answerItemVal) == 0) {
                        $errorClass_Answer = 'wpcw_quiz_missing';
                        $this->gotError = true;
                    }
                }
                // Add 'data-answer-id' field to store the ID of this row, and other rows that match this.
                $html .= sprintf('<tr class="wpcw_quiz_row_answer %s %s" data-answer-id="%d">', $errorClass_Answer, $odd ? 'alternate' : '', $count);
                $html .= sprintf('<th>%s <span>%d</span></th>', __('Answer', 'wp_courseware'), $count);
                $html .= sprintf('<td><input type="text" name="question_answer_%s[%d]" value="%s" /></td>', $this->quizItem->question_id, $count, htmlspecialchars($answerItemVal));
                // Correct answer column
                $html .= sprintf('<td class="wpcw_quiz_details_tick_correct wpcw_quiz_only_td">
										<input type="radio" name="question_answer_sel_%s" value="%s" %s />
									  </td>', $this->quizItem->question_id, $count, $this->quizItem->question_correct_answer == $count ? 'checked="checked"' : false);
                // Buttons for add/remove questions
                $html .= sprintf('
					<td class="wpcw_quiz_add_rem">
						<a href="#" title="%s" class="wpcw_question_add"><img src="%simg/icon_add_32.png" /></a>
						<a href="#" title="%s" class="wpcw_question_remove"><img src="%simg/icon_remove_32.png" /></a>
					</td>', __('Add a new answer...', 'wp_courseware'), WPCW_plugin_getPluginPath(), __('Remove this answer...', 'wp_courseware'), WPCW_plugin_getPluginPath());
                $html .= sprintf('</tr>');
                // Add the image URL for this answer - added as a new row.
                $html .= sprintf('<tr class="wpcw_quiz_row_answer_image wpcw_quiz_row_answer_image_%d %s %s">', $count, $errorClass_Answer, $odd ? 'alternate' : '');
                $html .= sprintf('<th>%s <span class="wpcw_inner_hint">%s</span></th>', __('Answer Image URL', 'wp_courseware'), __('(Optional) ', 'wp_courseware'));
                $html .= '<td>';
                // Field name - needs to use underscore, as square brackets break the jQuery to find the target.
                $thisAnswerFieldName = 'question_answer_image_' . $this->quizItem->question_id . '_' . $count;
                // The URL field.
                $html .= sprintf('<input type="text" name="question_answer_image_%s[%d]" id="%s" value="%s" />', $this->quizItem->question_id, $count, $thisAnswerFieldName, $answerItemImageVal);
                // The insert button.
                $html .= sprintf('<span class="wpcw_insert_image_wrap"><a href="#" class="button wpcw_insert_image" data-uploader_title="%s" data-uploader_btn_text="%s" data-target="%s" title="%s"><span class="wpcw_insert_image_img"></span> %s</a></span>', __('Choose an image for this answer...', 'wp_courseware'), __('Select Image...', 'wp_courseware'), $thisAnswerFieldName, __('Select Image', 'wp_courseware'), __('Select Image', 'wp_courseware'));
                $html .= '</td>';
                // Filler for the remaining space
                $html .= '<td colspan="2"></td>';
                $html .= sprintf('</tr>');
                $odd = !$odd;
            }
        }
        // Extra fields at the bottom of a question.
        $html .= $this->getSection_processFooter($columnCount);
        // All done
        $html .= sprintf('</table></li>');
        return $html;
    }
예제 #5
0
    /**
     * Output the form that allows questions to be configured.
     */
    function editForm_toString()
    {
        $answerList = false;
        if ($this->quizItem->question_answers) {
            $answerList = explode("\n", $this->quizItem->question_answers);
        }
        $html = false;
        // Extra CSS for errors
        $errorClass_Question = false;
        $errorClass_CorAnswer = false;
        // Error Check - Have we got an issue with a lack of question?
        if ($this->showErrors) {
            if (!$this->quizItem->question_question) {
                $errorClass_Question = 'wpcw_quiz_missing';
                $this->gotError = true;
            }
            if ($this->needCorrectAnswers && !$this->quizItem->question_correct_answer) {
                $errorClass_CorAnswer = 'wpcw_quiz_missing';
                $this->gotError = true;
            }
        }
        // Track columns needed to show question details
        $columnCount = 3;
        // Render just the question area
        $html .= sprintf('<li id="wpcw_quiz_details_%s" class="%s"><table class="wpcw_quiz_details_questions_wrap" cellspacing="0">', $this->quizItem->question_id, $this->cssClasses);
        $html .= sprintf('<tr class="wpcw_quiz_row_question">');
        $html .= sprintf('<th class="%s">%s</th>', $errorClass_Question, __('Question', 'wp_courseware'));
        $html .= sprintf('<td class="%s">', $errorClass_Question);
        $html .= sprintf('<input type="text" name="question_question_%s" value="%s" />', $this->quizItem->question_id, $this->quizItem->question_question);
        $html .= sprintf('<input type="hidden" name="question_type_%s" value="multi" />', $this->quizItem->question_id);
        // Field storing order of question among other questions
        $html .= sprintf('<input type="hidden" name="question_order_%s" value="%s" class="wpcw_question_hidden_order" />', $this->quizItem->question_id, $this->quizItem->question_order + 0);
        $html .= sprintf('</td>');
        // Only show column if need correct answers.
        if ($this->needCorrectAnswers) {
            $html .= sprintf('<td class="wpcw_quiz_details_tick_correct %s">%s</td>', $errorClass_CorAnswer, __('Correct<br/>Answer?', 'wp_courseware'));
            $columnCount++;
        }
        // Column for add/remove buttons
        $html .= '<td>&nbsp;</td>';
        $html .= sprintf('</tr>');
        // Render the list of answers if we have any.
        if ($answerList) {
            $count = 0;
            $odd = true;
            foreach ($answerList as $answerItem) {
                $answerItem = trim($answerItem);
                $count++;
                // Show an error if the field is still blank.
                $errorClass_Answer = false;
                if ($this->showErrors) {
                    // Check that answer contains some characters.
                    if (strlen($answerItem) == 0) {
                        $errorClass_Answer = 'wpcw_quiz_missing';
                        $this->gotError = true;
                    }
                }
                $html .= sprintf('<tr class="wpcw_quiz_row_answer %s">', $errorClass_Answer);
                $html .= sprintf('<th>%s <span>%d</span></th>', __('Answer', 'wp_courseware'), $count);
                $html .= sprintf('<td><input type="text" name="question_answer_%s[%d]" value="%s" /></td>', $this->quizItem->question_id, $count, $answerItem);
                // Correct answer column
                if ($this->needCorrectAnswers) {
                    $html .= sprintf('<td class="wpcw_quiz_details_tick_correct %s">
										<input type="radio" name="question_answer_sel_%s" value="%s" %s />
									  </td>', $errorClass_CorAnswer, $this->quizItem->question_id, $count, $this->quizItem->question_correct_answer == $count ? 'checked="checked"' : false);
                }
                // Buttons for add/remove questions
                $html .= sprintf('
					<td class="wpcw_quiz_add_rem">
						<a href="#" title="%s" class="wpcw_question_add"><img src="%simg/icon_add_32.png" /></a>
						<a href="#" title="%s" class="wpcw_question_remove"><img src="%simg/icon_remove_32.png" /></a>
					</td>', __('Add a new answer...', 'wp_courseware'), WPCW_plugin_getPluginPath(), __('Remove this answer...', 'wp_courseware'), WPCW_plugin_getPluginPath());
                $html .= sprintf('</tr>');
                $odd = !$odd;
            }
        }
        // Add icons for adding or removing a question.
        $html .= $this->getActionButtons($columnCount);
        // All done
        $html .= sprintf('</table></li>');
        return $html;
    }
예제 #6
0
    /**
     * Shows the quiz for the unit, based on the type being shown.
     * 
     * @param Boolean $showErrorsOnForm If true, show the errors on the form, acts as an override to disable showing errors if they are not wanted.
     * 
     * @return String The HTML that renders the quiz for answering.
     */
    function render_quizzes_handleQuizRendering($showErrorsOnForm = true)
    {
        // Hopefully not needed, but just in case.
        if (!$this->unitQuizDetails) {
            return false;
        }
        // Reload all raw answers user has created so far (if we have anything so far)
        if (!empty($this->unitQuizProgress) && 'incomplete' == $this->unitQuizProgress->quiz_paging_status) {
            $this->fetch_quizzes_loadRawAnswersSoFarForThisQuiz($this->unitQuizProgress->quiz_data);
        }
        // Render the wrapper for the quiz using the pending message section
        // Use the Quiz ID and Unit ID to validate permissions.
        $html = sprintf('<div class="wpcw_fe_quiz_box_wrap" id="wpcw_fe_quiz_complete_%d_%d">', $this->unitPost->ID, $this->unitQuizDetails->quiz_id);
        // Are we going to show the answers on a single for as part of the review?
        if ($this->check_paging_shouldWeShowReviewPage_rightNow()) {
            // To do, show a message here about reviewing answers...
            $html .= WPCW_UnitFrontend::message_createMessage_warning(__('You can now review your answers before submitting them.', 'wp_courseware'));
        }
        // Use any raw selection data that we have so far to pre-fill answers.
        $resultsList = $this->unchecked_QuizAnswersToGrade;
        // enctype="multipart/form-data" for file uploads..
        // data-wpcw_expired="false" - this is used by the timer to indicate if the timer has expired or not.
        $html .= sprintf('<form method="post" enctype="multipart/form-data" id="quiz_complete_%d_%d" data-wpcw_unit="%d" data-wpcw_quiz="%d" data-wpcw_expired="false">', $this->unitPost->ID, $this->unitQuizDetails->quiz_id, $this->unitPost->ID, $this->unitQuizDetails->quiz_id);
        $html .= '<div class="wpcw_fe_quiz_box wpcw_fe_quiz_box_pending">';
        // #### 1 - Quiz Title - constant for all quizzes
        $html .= sprintf('<div class="wpcw_fe_quiz_title">%s</div>', $this->unitQuizDetails->quiz_title);
        // #### 2 - Pass mark - just needed for blocking quizes
        if ('quiz_block' == $this->unitQuizDetails->quiz_type) {
            $totalQs = count($this->unitQuizDetails->questions);
            $passQs = ceil($this->unitQuizDetails->quiz_pass_mark / 100 * $totalQs);
            $html .= '<div class="wpcw_fe_quiz_pass_mark">';
            $html .= sprintf(__('You\'ll need to correctly answer at least <b>%d of the %d</b> questions below (<b>at least %d%%</b>) to progress to the next unit.', 'wp_courseware'), $passQs, $totalQs, $this->unitQuizDetails->quiz_pass_mark);
            $html .= '</div>';
        }
        // #### 3 - The actual question form.
        if (!empty($this->unitQuizDetails->questions)) {
            $questionNum = 1;
            $showQuestions = true;
            // Timer Mode
            if ('use_timer' == $this->unitQuizDetails->quiz_timer_mode) {
                // We've not started the quiz yet, so we show the begin test button.
                if (empty($this->unitQuizProgress) || 'retake_waiting' == $this->unitQuizProgress->quiz_next_step_type) {
                    $showQuestions = false;
                    $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>';
                    // Begin Quiz
                    $html .= sprintf('
									<div class="wpcw_fe_quiz_begin_quiz">
										<div class="wpcw_fe_quiz_begin_quiz_hdr">%s</div>
										<a href="#" class="fe_btn fe_btn_completion" id="wpcw_fe_quiz_begin_quiz" data-wpcw_quiz="%d" data-wpcw_unit="%d" >%s</a>
										
										<div class="wpcw_fe_submit_loader wpcw_loader">
											<img src="%simg/ajax_loader.gif" />
										</div>
									</div>', sprintf(__('You have <b>%s</b> to complete this quiz...', 'wp_coursware'), WPCW_time_convertMinutesToHumanLabel($this->unitQuizDetails->quiz_timer_mode_limit)), $this->unitQuizDetails->quiz_id, $this->unitPost->ID, __('Begin Quiz...', 'wp_courseware'), WPCW_plugin_getPluginPath());
                } else {
                    // Total time in seconds for this quiz
                    $timerDetails_secondsLeft = $allowedTime = 60 * $this->unitQuizDetails->quiz_timer_mode_limit;
                    // What time do we have left?
                    $timeSoFar = strtotime($this->unitQuizProgress->quiz_started_date);
                    $timeNow = current_time('timestamp');
                    // We've started the quiz when the time so far is > 0
                    if ($timeSoFar > 0) {
                        $timerDetails_secondsLeft = $allowedTime - ($timeNow - $timeSoFar);
                    }
                    // Show the timer if we have time left.
                    $html .= sprintf('<div id="wpcw_fe_timer_countdown" data-time_left="%d">%s</div>', $timerDetails_secondsLeft, WPCW_time_convertSecondsToHumanLabel($timerDetails_secondsLeft));
                }
            }
            if ($showQuestions) {
                // We're paging, so check what question or questions to show next.
                if ($this->check_paging_areWePagingQuestions()) {
                    // Stores the index of the next question to show
                    $questionToShowIndex = $this->fetch_paging_getQuestionIndex();
                    $question = $this->fetch_paging_getQuestion($questionToShowIndex);
                    // Show progress of user through the questions.
                    $html .= sprintf('<div class="wpcw_fe_paging_progress">%s</div>', sprintf(__('Question %d of %d', 'wp_courseware'), $questionToShowIndex + 1, $this->fetch_paging_getQuestionCount()));
                    // Header before questions (but show progress before the header line).
                    $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>';
                    // Show the answer later button.
                    if ($this->check_paging_shouldWeShowAnswerLaterButton()) {
                        $html .= sprintf('<div class="wpcw_fe_quiz_answer_later"><a href="#" class="fe_btn fe_btn_small fe_btn_navigation" id="wpcw_fe_quiz_answer_later">%s</a></div>', __('Answer Later...', 'wp_courseware'));
                    }
                    // Render just this question
                    $html .= $this->render_quizzes_handleQuizRendering_singleQuestion($question, $resultsList, $showErrorsOnForm, $questionToShowIndex + 1, true, 0);
                    // Work out what caption to show for the submit button. Change this if we're about to
                    // submit the answers for the final question or review our answers.
                    $buttonCaption = __('Save &amp; Next Question &raquo;', 'wp_courseware');
                    // Are we showing the last incomplete question (and we're nearly complete)
                    if ($this->unitQuizProgress && $this->unitQuizProgress->quiz_paging_incomplete <= 1 && $this->check_paging_isThisTheLastIncompleteQuestion($question->question_id)) {
                        // We appear to be on the last question. Are we going to review too?
                        if ($this->check_paging_shouldWeShowReviewPage()) {
                            $buttonCaption = __('Save &amp; Review Answers &raquo;', 'wp_courseware');
                        } else {
                            $buttonCaption = __('Submit Answers', 'wp_courseware');
                        }
                    }
                    // Previous button - created if required. Using a button rather than a link to ensure the buttons look right when side-by-side.
                    $buttonPreviousClicker = false;
                    if ($this->check_paging_shouldWeShowPreviousButton()) {
                        $buttonPreviousClicker = sprintf('<input type="submit" class="fe_btn fe_btn_completion btn_completion" id="fe_btn_quiz_previous" name="previous_question" value="%s">', __('&laquo; Previous Question', 'wp_courseware'));
                    }
                    // #### 4A - The navigation buttons
                    $html .= sprintf('<div class="wpcw_fe_quiz_submit wpcw_fe_quiz_submit_data">				
										%s								    
										<div class="wpcw_fe_submit_loader wpcw_loader">
											<img src="%simg/ajax_loader.gif" />
										</div>									
										%s						
										<input type="submit" class="fe_btn fe_btn_completion btn_completion" id="fe_btn_quiz_next" name="submit" value="%s">
									</div>', WPCW_content_progressBar(0, 'wpcw_fe_upload_progress'), WPCW_plugin_getPluginPath(), $buttonPreviousClicker, $buttonCaption);
                } else {
                    // Header before questions
                    $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>';
                    $questionIndex = 0;
                    foreach ($this->unitQuizDetails->questions as $question) {
                        $html .= $this->render_quizzes_handleQuizRendering_singleQuestion($question, $resultsList, $showErrorsOnForm, $questionNum++, false, $questionIndex++);
                    }
                    // #### 4B - The submit answers button. //<a href="#" class="fe_btn fe_btn_completion btn_completion" id="quiz_complete_%d_%d">%s</a>
                    $html .= sprintf('<div class="wpcw_fe_quiz_submit wpcw_fe_quiz_submit_data">
					
										%s
									    
										<div class="wpcw_fe_submit_loader wpcw_loader">
											<img src="%simg/ajax_loader.gif" />
										</div>
										
										<input type="submit" class="fe_btn fe_btn_completion btn_completion" name="submit" value="%s">						
									</div>', WPCW_content_progressBar(0, 'wpcw_fe_upload_progress'), WPCW_plugin_getPluginPath(), __('Submit Answers', 'wp_courseware'));
                }
                // end of check for paging.
            }
            // end of check of showing questions (e.g. timer mode)
        }
        // end of question check
        $html .= '</div>';
        // .wpcw_fe_quiz_box
        $html .= '</form>';
        $html .= '</div>';
        // .wpcw_fe_quiz_box_wrap
        return $html;
    }
예제 #7
0
/**
 * Return a URL to download a certificate.
 * 
 * @param String $accessKey The access key for the certificate.
 * @return String The full URL to the certificate.
 */
function WPCW_certificate_generateLink($accessKey)
{
    return apply_filters('wpcw_certificate_generated_url', WPCW_plugin_getPluginPath() . 'pdf_create_certificate.php?certificate=' . $accessKey);
}
예제 #8
0
/**
 * Shows the quiz for the unit, based on the type being shown.
 * 
 * @param Integer $unitID The ID of the unit that's currently pending.
 * @param Object $quizDetails The details of the quiz to show.
 * @param Array $resultsList The list of checked answers, wrong answers, and error messages for the quiz form.
 * 
 * @return String The HTML that renders the quiz for answering.
 */
function WPCW_quizzes_handleQuizRendering($unitID, $quizDetails, $resultsList = false)
{
    // Hopefully not needed, but just in case.
    if (!$quizDetails) {
        return false;
    }
    // Create an array so that we can use isset($answerList['key']).
    if (!$resultsList) {
        $resultsList = array('answer_list' => array(), 'wrong_answer_list' => array(), 'error_answer_list' => array());
    }
    // Render the wrapper for the quiz using the pending message section
    // Use the Quiz ID and Unit ID to validate permissions.
    $html = sprintf('<div class="wpcw_fe_quiz_box_wrap" id="wpcw_fe_quiz_complete_%d_%d">', $unitID, $quizDetails->quiz_id);
    // enctype="multipart/form-data" for file uploads.
    $html .= sprintf('<form method="post" enctype="multipart/form-data" id="quiz_complete_%d_%d">', $unitID, $quizDetails->quiz_id);
    $html .= '<div class="wpcw_fe_quiz_box wpcw_fe_quiz_box_pending">';
    // #### 1 - Quiz Title - constant for all quizzes
    $html .= sprintf('<div class="wpcw_fe_quiz_title">%s</div>', $quizDetails->quiz_title);
    // #### 2 - Pass mark - just needed for blocking quizes
    if ('quiz_block' == $quizDetails->quiz_type) {
        $totalQs = count($quizDetails->questions);
        $passQs = ceil($quizDetails->quiz_pass_mark / 100 * $totalQs);
        $html .= '<div class="wpcw_fe_quiz_pass_mark">';
        $html .= sprintf(__('You\'ll need to correctly answer at least <b>%d of the %d</b> questions below (<b>at least %d%%</b>) to progress to the next unit.', 'wp_courseware'), $passQs, $totalQs, $quizDetails->quiz_pass_mark);
        $html .= '</div>';
    }
    // Header before questions
    $html .= '<div class="wpcw_fe_quiz_q_hdr"></div>';
    // #### 3 - The actual question form.
    if ($quizDetails->questions && count($quizDetails->questions) > 0) {
        $questionNum = 1;
        foreach ($quizDetails->questions as $question) {
            // See if we want to show an answer
            $selectAnswer = false;
            if (isset($resultsList['answer_list'][$question->question_id])) {
                $selectAnswer = $resultsList['answer_list'][$question->question_id];
            }
            switch ($question->question_type) {
                case 'multi':
                    $quObj = new WPCW_quiz_MultipleChoice($question);
                    break;
                case 'open':
                    $quObj = new WPCW_quiz_OpenEntry($question);
                    break;
                case 'upload':
                    $quObj = new WPCW_quiz_FileUpload($question);
                    break;
                case 'truefalse':
                    $quObj = new WPCW_quiz_TrueFalse($question);
                    break;
                    // Not expecting anything here... so not handling the error case.
                // Not expecting anything here... so not handling the error case.
                default:
                    break;
            }
            // Use the objects to render the questions, showing an answer as wrong if appropriate.
            $showAsError = false;
            // Only worry about errors if actual data has been submitted.
            $errorToShow = false;
            if (isset($_POST['submit'])) {
                // Something went wrong
                if (isset($resultsList['error_answer_list'][$question->question_id])) {
                    $errorToShow = $resultsList['error_answer_list'][$question->question_id];
                    $showAsError = 'error';
                } else {
                    if (!isset($resultsList['answer_list'][$question->question_id])) {
                        $showAsError = 'missing';
                    } else {
                        if (isset($resultsList['wrong_answer_list'][$question->question_id])) {
                            $showAsError = 'wrong';
                        }
                    }
                }
            }
            // Use object to render
            $html .= $quObj->renderForm_toString($quizDetails, $questionNum++, $selectAnswer, $showAsError, $errorToShow);
        }
    }
    // #### 4 - The submit answers button. //<a href="#" class="fe_btn fe_btn_completion btn_completion" id="quiz_complete_%d_%d">%s</a>
    $html .= sprintf('<div class="wpcw_fe_quiz_submit">
	
						<div class="wpcw_fe_upload_progress">
					        <div class="wpcw_fe_upload_progress_bar"></div>
					        <div class="wpcw_fe_upload_progress_percent">0%%</div>
					    </div>
					    
						<div class="wpcw_fe_submit_loader wpcw_loader">
							<img src="%simg/ajax_loader.gif" />
						</div>
						
						<input type="submit" class="fe_btn fe_btn_completion btn_completion" name="submit" value="%s">						
					</div>', WPCW_plugin_getPluginPath(), __('Submit Answers', 'wp_courseware'));
    $html .= '</div>';
    // .wpcw_fe_quiz_box
    $html .= '</form>';
    $html .= '</div>';
    // .wpcw_fe_quiz_box_wrap
    return $html;
}
예제 #9
0
/**
 * Function to show a list of questions in the question pool for use by a standard page or the AJAX thickbox.
 * 
 * @param Integer $itemsPerPage The number of items to show on each table page.
 * @param Array $paramSrc The array of parameters to use for filtering/searching the question pool.
 * @param String $actionMode The type of mode we're in (ajax or std). 
 * @param PageBuilder $page The current page object (optional).
 */
function WPCW_questionPool_showPoolTable($itemsPerPage, $paramSrc, $actionMode = 'std', $page = false)
{
    global $wpcwdb, $wpdb;
    $wpdb->show_errors();
    // AJAX loader
    if ('ajax' == $actionMode) {
        printf('<img src="%simg/ajax_loader.gif" class="wpcw_loader" style="display: none;" />', WPCW_plugin_getPluginPath());
    }
    // Check to see if we've got questions to process
    if ('std' == $actionMode) {
        WPCW_showPage_QuestionPool_processActionForm($page);
    }
    $paging_pageWanted = WPCW_arrays_getValue($paramSrc, 'pagenum') + 0;
    if ($paging_pageWanted == 0) {
        $paging_pageWanted = 1;
    }
    // Handle the sorting and filtering
    $orderBy = WPCW_arrays_getValue($paramSrc, 'orderby');
    $ordering = WPCW_arrays_getValue($paramSrc, 'order');
    // Validate ordering
    switch ($orderBy) {
        case 'question_question':
        case 'question_type':
            break;
            // Default and question_id
            //case 'question_id':
        // Default and question_id
        //case 'question_id':
        default:
            $orderBy = 'qs.question_id';
            break;
    }
    // Create opposite ordering for reversing it.
    $ordering_opposite = false;
    switch ($ordering) {
        case 'desc':
            $ordering_opposite = 'asc';
            break;
        case 'asc':
            $ordering_opposite = 'desc';
            break;
        default:
            $ordering = 'desc';
            $ordering_opposite = 'asc';
            break;
    }
    // Was a search string specified? Or a specific item?
    $searchString = WPCW_arrays_getValue($paramSrc, 's');
    // Create WHERE string based search - Title or Description of Quiz
    $SQL_WHERE = false;
    if ($searchString) {
        $SQL_WHERE = $wpdb->prepare(" AND question_question LIKE %s", '%' . $searchString . '%');
    }
    $summaryPageURL = admin_url('admin.php?page=WPCW_showPage_QuestionPool');
    // Show the form for searching
    ?>
			
	<form id="wpcw_questions_search_box" method="get" action="<?php 
    echo $summaryPageURL;
    ?>
">
	<p class="search-box">
		<label class="screen-reader-text" for="wpcw_questions_search_input"><?php 
    _e('Search Questions', 'wp_courseware');
    ?>
</label>
		<input id="wpcw_questions_search_input" type="text" value="<?php 
    echo $searchString;
    ?>
" name="s"/>
		<input class="button" type="submit" value="<?php 
    _e('Search Questions', 'wp_courseware');
    ?>
"/>
		
		<input type="hidden" name="page" value="WPCW_showPage_QuestionPool" />
	</p>
	</form>
	<?php 
    $SQL_TAG_FILTER = false;
    $tagFilter = intval(WPCW_arrays_getValue($paramSrc, 'filter', false));
    // See if we have any tag filtering to do.
    if ($tagFilter > 0) {
        // Ensure we add the tag mapping table to the query
        $SQL_TAG_FILTER = "\n\t\t\tLEFT JOIN {$wpcwdb->question_tag_mapping} qtm ON qtm.question_id = qs.question_id\t\n\t\t";
        $SQL_WHERE .= $wpdb->prepare("\n\t\t\tAND qtm.tag_id = %d\n\t\t\tAND qs.question_question IS NOT NULL  \n\t\t", $tagFilter);
        //
    }
    $SQL_PAGING = "\n\t\t\tSELECT COUNT(*) as question_count \n\t\t\tFROM {$wpcwdb->quiz_qs} qs\n\t\t\t{$SQL_TAG_FILTER}\n\t\t\tWHERE question_type <> 'random_selection'\n\t\t\t{$SQL_WHERE} \n\t\t";
    $paging_resultsPerPage = $itemsPerPage;
    $paging_totalCount = $wpdb->get_var($SQL_PAGING);
    $paging_recordStart = ($paging_pageWanted - 1) * $paging_resultsPerPage + 1;
    $paging_recordEnd = $paging_pageWanted * $paging_resultsPerPage;
    $paging_pageCount = ceil($paging_totalCount / $paging_resultsPerPage);
    $paging_sqlStart = $paging_recordStart - 1;
    // Show search message - that a search has been tried.
    if ($searchString) {
        printf('<div class="wpcw_search_count">%s "%s" (%s %s) (<a href="%s">%s</a>)</div>', __('Search results for', 'wp_courseware'), htmlentities($searchString), $paging_totalCount, _n('result', 'results', $paging_totalCount, 'wp_courseware'), $summaryPageURL, __('reset', 'wp_courseware'));
    }
    // Do main query
    $SQL = "SELECT * \n\t\t\tFROM {$wpcwdb->quiz_qs} qs\t\t\t\n\t\t\t{$SQL_TAG_FILTER}\t\t\t\n\t\t\tWHERE question_type <> 'random_selection'\n\t\t\t{$SQL_WHERE}\n\t\t\tORDER BY {$orderBy} {$ordering}\n\t\t\tLIMIT {$paging_sqlStart}, {$paging_resultsPerPage}\t\t\t \n\t\t\t";
    // These are already checked, so they are safe, hence no prepare()
    // Generate paging code
    $baseURL = WPCW_urls_getURLWithParams($summaryPageURL, 'pagenum') . "&pagenum=";
    $questions = $wpdb->get_results($SQL);
    $tbl = new TableBuilder();
    $tbl->attributes = array('id' => 'wpcw_tbl_question_pool', 'class' => 'widefat wpcw_tbl');
    // Checkbox Col
    //$tblCol = new TableColumn(false, 'question_selection');
    //$tblCol->cellClass = "question_selection wpcw_center";
    //$tbl->addColumn($tblCol);
    // Wanting sorting links... in standard mode
    if ('std' == $actionMode) {
        // Checkbox field (no name, as we'll use jQuery to do a check all)
        $tblCol = new TableColumn('<input type="checkbox" />', 'question_id_cb');
        $tblCol->cellClass = "wpcw_center wpcw_select_cb";
        $tblCol->headerClass = "wpcw_center wpcw_select_cb";
        $tbl->addColumn($tblCol);
        // ID - sortable
        $sortableLink = sprintf('<a href="%s&order=%s&orderby=question_id"><span>%s</span><span class="sorting-indicator"></span></a>', $baseURL, 'question_id' == $orderBy ? $ordering_opposite : 'asc', __('ID', 'wp_courseware'));
        // ID - render
        $tblCol = new TableColumn($sortableLink, 'question_id');
        $tblCol->headerClass = 'question_id' == $orderBy ? 'sorted ' . $ordering : 'sortable';
        $tblCol->cellClass = "question_id";
        $tbl->addColumn($tblCol);
        // Question - sortable
        $sortableLink = sprintf('<a href="%s&order=%s&orderby=question_question"><span>%s</span><span class="sorting-indicator"></span></a>', $baseURL, 'question_question' == $orderBy ? $ordering_opposite : 'asc', __('Question', 'wp_courseware'));
        // Question - render
        $tblCol = new TableColumn($sortableLink, 'question_question');
        $tblCol->headerClass = 'question_question' == $orderBy ? 'sorted ' . $ordering : 'sortable';
        $tblCol->cellClass = "question_question";
        $tbl->addColumn($tblCol);
        // Question Type - sortable
        $sortableLink = sprintf('<a href="%s&order=%s&orderby=question_type"><span>%s</span><span class="sorting-indicator"></span></a>', $baseURL, 'question_type' == $orderBy ? $ordering_opposite : 'asc', __('Question Type', 'wp_courseware'));
        // Question Type - render
        $tblCol = new TableColumn($sortableLink, 'question_type');
        $tblCol->headerClass = ('question_type' == $orderBy ? 'sorted ' . $ordering : 'sortable') . ' wpcw_center';
        $tblCol->cellClass = "question_type";
        $tbl->addColumn($tblCol);
    } else {
        $tblCol = new TableColumn(__('ID', 'wp_courseware'), 'question_id');
        $tblCol->cellClass = "question_id";
        $tbl->addColumn($tblCol);
        $tblCol = new TableColumn(__('Question', 'wp_courseware'), 'question_question');
        $tblCol->cellClass = "question_question";
        $tbl->addColumn($tblCol);
        $tblCol = new TableColumn(__('Question Type', 'wp_courseware'), 'question_type');
        $tblCol->cellClass = "question_type";
        $tbl->addColumn($tblCol);
    }
    $tblCol = new TableColumn(__('Associated Quizzes', 'wp_courseware'), 'associated_quizzes');
    $tblCol->headerClass = "wpcw_center";
    $tblCol->cellClass = "associated_quizzes wpcw_center";
    $tbl->addColumn($tblCol);
    $tblCol = new TableColumn(__('Tags', 'wp_courseware'), 'question_tags');
    $tblCol->cellClass = "question_tags wpcw_center";
    $tbl->addColumn($tblCol);
    // Actions
    $tblCol = new TableColumn(__('Actions', 'wp_courseware'), 'actions');
    $tblCol->cellClass = "actions actions_right";
    $tblCol->headerClass = "actions_right";
    $tbl->addColumn($tblCol);
    // Stores course details in a mini cache to save lots of MySQL lookups.
    $miniCourseDetailCache = array();
    // Format row data and show it.
    if ($questions) {
        $odd = false;
        foreach ($questions as $singleQuestion) {
            $data = array();
            // URLs
            $editURL = admin_url('admin.php?page=WPCW_showPage_ModifyQuestion&question_id=' . $singleQuestion->question_id);
            // Maintain paging where possible.
            $deleteURL = $baseURL . '&action=delete&question_id=' . $singleQuestion->question_id;
            // Basic Details
            $data['question_id'] = $singleQuestion->question_id;
            $data['question_type'] = WPCW_quizzes_getQuestionTypeName($singleQuestion->question_type);
            $data['question_id_cb'] = sprintf('<input type="checkbox" name="question_%d" />', $singleQuestion->question_id);
            // Association Count
            $data['associated_quizzes'] = $singleQuestion->question_usage_count;
            // Actions - Std mode
            if ('std' == $actionMode) {
                // Edit by clicking
                $data['question_question'] = sprintf('<a href="%s">%s</a>', $editURL, $singleQuestion->question_question);
                $data['actions'] = '<ul class="wpcw_action_link_list">';
                $data['actions'] .= sprintf('<li><a href="%s" class="button-primary">%s</a></li>', $editURL, __('Edit', 'wp_courseware'));
                $data['actions'] .= sprintf('<li><a href="%s" class="button-secondary wpcw_action_link_delete_question wpcw_action_link_delete" rel="%s">%s</a></li>', $deleteURL, __('Are you sure you wish to delete this question? This cannot be undone.', 'wp_courseware'), __('Delete', 'wp_courseware'));
                $data['actions'] .= '</ul>';
            } else {
                if ('ajax' == $actionMode) {
                    // No Edit by clicking
                    $data['question_question'] = $singleQuestion->question_question . sprintf('<span class="wpcw_action_status wpcw_action_status_added">%s</span>', __('Added', 'wp_courseware'));
                    $data['actions'] = '<ul class="wpcw_action_link_list">';
                    $data['actions'] .= sprintf('<li><a href="#" class="button-primary wpcw_tb_action_add" data-questionnum="%d">%s</a></li>', $singleQuestion->question_id, __('Add To Quiz', 'wp_courseware'));
                    $data['actions'] .= '</ul>';
                }
            }
            // Tags
            $data['question_tags'] = sprintf('<span class="wpcw_quiz_details_question_tags" data-questionid="%d" id="wpcw_quiz_details_question_tags_%d">', $singleQuestion->question_id, $singleQuestion->question_id);
            $data['question_tags'] .= WPCW_questions_tags_render($singleQuestion->question_id, WPCW_questions_tags_getTagsForQuestion($singleQuestion->question_id));
            $data['question_tags'] .= '</span>';
            // Odd/Even row colouring.
            $odd = !$odd;
            $tbl->addRow($data, $odd ? 'alternate' : '');
        }
    } else {
        // No questions - show error in table.
        $tbl->addRowObj(new RowDataSimple('wpcw_center wpcw_none_found', __('There are currently no questions to show.', 'wp_courseware'), 7));
    }
    // Add the form for the start of the multiple-add
    $formWrapper_start = false;
    if ('std' == $actionMode) {
        // Set the action URL to preserve parameters that we have.
        $formWrapper_start = sprintf('<form method="POST" action="%s">', WPCW_urls_getURLWithParams($summaryPageURL, 'pagenum'));
    }
    // Create tag filter (uses a form)
    $tagFilter = WPCW_questions_tags_createTagFilter($tagFilter, 'WPCW_showPage_QuestionPool');
    // Work out paging and filtering
    $paging = WPCW_tables_showPagination($baseURL, $paging_pageWanted, $paging_pageCount, $paging_totalCount, $paging_recordStart, $paging_recordEnd, $tagFilter);
    // Show the actions
    $formWrapper_end = false;
    if ('std' == $actionMode) {
        $formWrapper_end = WPCW_showPage_QuestionPool_actionForm();
        // Form tag - needed for processing
        $formWrapper_end .= '</form>';
    }
    // Finally show table
    return $paging . $formWrapper_start . $tbl->toString() . $formWrapper_end . $paging;
}