/** * Shows a detailed summary of the user's quiz or survey answers. */ function WPCW_showPage_UserProgess_quizAnswers_load() { global $wpcwdb, $wpdb; $wpdb->show_errors(); $page = new PageBuilder(false); $page->showPageHeader(__('Detailed User Quiz/Survey Results', 'wp_courseware'), '75%', WPCW_icon_getPageIconURL()); $userID = WPCW_arrays_getValue($_GET, 'user_id') + 0; $unitID = WPCW_arrays_getValue($_GET, 'unit_id') + 0; $quizID = WPCW_arrays_getValue($_GET, 'quiz_id') + 0; // Create a link back to the detailed user progress, and back to all users. printf('<div class="wpcw_button_group">'); // Link back to all user summary printf('<a href="%s" class="button-secondary">%s</a> ', admin_url('users.php'), __('« Return to User Summary', 'wp_courseware')); if ($userDetails = get_userdata($userID)) { // Link back to user's personal summary printf('<a href="%s&user_id=%d" class="button-secondary">%s</a> ', admin_url('users.php?page=WPCW_showPage_UserProgess'), $userDetails->ID, sprintf(__('« Return to <b>%s\'s</b> Progress Report', 'wp_courseware'), $userDetails->display_name)); } // Try to get the full detailed results. $results = WPCW_quizzes_getUserResultsForQuiz($userID, $unitID, $quizID); // No results, so abort. if (!$results) { // Close the button wrapper for above early printf('</div>'); // .wpcw_button_group $page->showMessage(__('Sorry, but no results could be found.', 'wp_courseware'), true); $page->showPageFooter(); return; } // Could potentially have an issue where the quiz has been deleted // but the data exists.. small chance though. $quizDetails = WPCW_quizzes_getQuizDetails($quizID, true, true, $userID); // Extra button - return to gradebook printf('<a href="%s&course_id=%d" class="button-secondary">%s</a> ', admin_url('admin.php?page=WPCW_showPage_GradeBook'), $quizDetails->parent_course_id, __("« Return to Gradebook", 'wp_courseware')); printf('</div>'); // .wpcw_button_group // #### 1 - Handle grades being updated $results = WPCW_showPage_UserProgess_quizAnswers_handingGrading($quizDetails, $results, $page, $userID, $unitID); // #### 2A - Check if next action for user has been triggered by the admin. $results = WPCW_showPage_UserProgess_quizAnswers_whatsNext_savePreferences($quizDetails, $results, $page, $userID, $unitID); // #### 2B - Handle telling admin what's next WPCW_showPage_UserProgess_quizAnswers_whatsNext($quizDetails, $results, $page, $userID, $unitID); //Ê#### 3 - Handle sending emails if something has changed. if (isset($results->sendOutEmails) && $results->sendOutEmails) { $extraDetail = isset($results->extraEmailDetail) ? $results->extraEmailDetail : ''; // Only called if the quiz was graded. if (isset($results->quiz_has_just_been_graded) && $results->quiz_has_just_been_graded) { // Need to call the action anyway, but any functions hanging off this // should check if the admin wants users to have notifications or not. do_action('wpcw_quiz_graded', $userID, $quizDetails, number_format($results->quiz_grade, 1), $extraDetail); } $courseDetails = WPCW_courses_getCourseDetails($quizDetails->parent_course_id); if ($courseDetails->email_quiz_grade_option == 'send_email') { // Message is only if quiz has been graded. if (isset($results->quiz_has_just_been_graded) && $results->quiz_has_just_been_graded) { $page->showMessage(__('The user has been sent an email with their grade for this course.', 'wp_courseware')); } } } // #### - Table 1 - Overview printf('<h3>%s</h3>', __('Quiz/Survey Overview', 'wp_courseware')); $tbl = new TableBuilder(); $tbl->attributes = array('id' => 'wpcw_tbl_progress_quiz_info', 'class' => 'widefat wpcw_tbl'); $tblCol = new TableColumn(false, 'quiz_label'); $tblCol->cellClass = 'wpcw_tbl_label'; $tbl->addColumn($tblCol); $tblCol = new TableColumn(false, 'quiz_detail'); $tbl->addColumn($tblCol); // These are the base details for the quiz to show. $summaryData = array(__('Quiz Title', 'wp_courseware') => $quizDetails->quiz_title, __('Quiz Description', 'wp_courseware') => $quizDetails->quiz_desc, __('Quiz Type', 'wp_courseware') => WPCW_quizzes_getQuizTypeName($quizDetails->quiz_type), __('No. of Questions', 'wp_courseware') => $results->quiz_question_total, __('Completed Date', 'wp_courseware') => __('About', 'wp_courseware') . ' ' . human_time_diff($results->quiz_completed_date_ts) . ' ' . __('ago', 'wp_courseware') . '<br/><small>(' . date('D jS M Y \\a\\t H:i:s', $results->quiz_completed_date_ts) . ')</small>', __('Number of Quiz Attempts', 'wp_courseware') => $results->attempt_count, __('Permitted Quiz Attempts', 'wp_courseware') => -1 == $quizDetails->quiz_attempts_allowed ? __('Unlimited', 'wp_courseware') : $quizDetails->quiz_attempts_allowed); // Quiz details relating to score, etc. if ('survey' != $quizDetails->quiz_type) { $summaryData[__('Pass Mark', 'wp_courseware')] = $quizDetails->quiz_pass_mark . '%'; // Still got items to grade if ($results->quiz_needs_marking > 0) { $summaryData[__('No. of Questions to Grade', 'wp_courseware')] = '<span class="wpcw_status_info wpcw_icon_pending">' . $results->quiz_needs_marking . '</span>'; $summaryData[__('Overall Grade', 'wp_courseware')] = '<span class="wpcw_status_info wpcw_icon_pending">' . __('Awaiting Final Grading', 'wp_courseware') . '</span>'; } else { $summaryData[__('No. of Question to Grade', 'wp_courseware')] = '-'; // Show if PASSED or FAILED with the overall grade. $gradeData = false; if ($results->quiz_grade >= $quizDetails->quiz_pass_mark) { $gradeData = sprintf('<span class="wpcw_tbl_progress_quiz_overall wpcw_question_yesno_status wpcw_question_yes">%s%% %s</span>', number_format($results->quiz_grade, 1), __('Passed', 'wp_courseware')); } else { $gradeData = sprintf('<span class="wpcw_tbl_progress_quiz_overall wpcw_question_yesno_status wpcw_question_no">%s%% %s</span>', number_format($results->quiz_grade, 1), __('Failed', 'wp_courseware')); } $summaryData[__('Overall Grade', 'wp_courseware')] = $gradeData; } } foreach ($summaryData as $label => $data) { $tbl->addRow(array('quiz_label' => $label . ':', 'quiz_detail' => $data)); } echo $tbl->toString(); // ### 4 - Form Code - to allow instructor to send data back to printf('<form method="POST" id="wpcw_tbl_progress_quiz_grading_form">'); printf('<input type="hidden" name="grade_answers_submitted" value="true">'); // ### 5 - Table 2 - Each Specific Quiz $questionNumber = 0; if ($results->quiz_data && count($results->quiz_data) > 0) { foreach ($results->quiz_data as $questionID => $answer) { $data = $answer; // Get the question type if (isset($quizDetails->questions[$questionID])) { // Store as object for easy reference. $quObj = $quizDetails->questions[$questionID]; // Render the question as a table. printf('<h3>%s #%d - %s</h3>', __('Question', 'wp_courseware'), ++$questionNumber, $quObj->question_question); $tbl = new TableBuilder(); $tbl->attributes = array('id' => 'wpcw_tbl_progress_quiz_info', 'class' => 'widefat wpcw_tbl wpcw_tbl_progress_quiz_answers_' . $quObj->question_type); $tblCol = new TableColumn(false, 'quiz_label'); $tblCol->cellClass = 'wpcw_tbl_label'; $tbl->addColumn($tblCol); $tblCol = new TableColumn(false, 'quiz_detail'); $tbl->addColumn($tblCol); $theirAnswer = false; switch ($quObj->question_type) { case 'truefalse': case 'multi': $theirAnswer = $answer['their_answer']; break; // File Upload - create a download link // File Upload - create a download link case 'upload': $theirAnswer = sprintf('<a href="%s%s" target="_blank" class="button-primary">%s .%s %s (%s)</a>', WP_CONTENT_URL, $answer['their_answer'], __('Open', 'wp_courseware'), pathinfo($answer['their_answer'], PATHINFO_EXTENSION), __('File', 'wp_courseware'), WPCW_files_getFileSize_human($answer['their_answer'])); break; // Open Ended - Wrap in span tags, to cap the size of the field, and format new lines. // Open Ended - Wrap in span tags, to cap the size of the field, and format new lines. case 'open': $theirAnswer = '<span class="wpcw_q_answer_open_wrap"><textarea readonly>' . $data['their_answer'] . '</textarea></span>'; break; } // end of $theirAnswer check $summaryData = array(__('Type', 'wp_courseware') => array('data' => WPCW_quizzes_getQuestionTypeName($quObj->question_type), 'cssclass' => ''), __('Their Answer', 'wp_courseware') => array('data' => $theirAnswer, 'cssclass' => '')); // Just for quizzes - show answers/grade if ('survey' != $quizDetails->quiz_type) { switch ($quObj->question_type) { case 'truefalse': case 'multi': // The right answer... $summaryData[__('Correct Answer', 'wp_courseware')] = array('data' => $answer['correct'], 'cssclass' => ''); // Did they get it right? $getItRight = sprintf('<span class="wpcw_question_yesno_status wpcw_question_%s">%s</span>', $answer['got_right'], 'yes' == $answer['got_right'] ? __('Yes', 'wp_courseware') : __('No', 'wp_courseware')); $summaryData[__('Did they get it right?', 'wp_courseware')] = array('data' => $getItRight, 'cssclass' => ''); break; case 'upload': case 'open': $gradeHTML = false; $theirGrade = WPCW_arrays_getValue($answer, 'their_grade'); // Not graded - show select box. if ($theirGrade == 0) { $cssClass = 'wpcw_grade_needs_grading'; } else { $cssClass = 'wpcw_grade_already_graded'; $gradeHTML = sprintf('<span class="wpcw_grade_view">%d%% <a href="#">(%s)</a></span>', $theirGrade, __('Click to edit', 'wp_courseware')); } // Not graded yet, allow admin to grade the quiz, or change // the grading later if they want to. $gradeHTML .= WPCW_forms_createDropdown('grade_quiz_' . $quObj->question_id, WPCW_quizzes_getPercentageList(__('-- Select a grade --', 'wp_courseware')), $theirGrade, false, 'wpcw_tbl_progress_quiz_answers_grade'); $summaryData[__('Their Grade', 'wp_courseware')] = array('data' => $gradeHTML, 'cssclass' => $cssClass); break; } } // Check of showing the right answer. foreach ($summaryData as $label => $data) { $tbl->addRow(array('quiz_label' => $label . ':', 'quiz_detail' => $data['data']), $data['cssclass']); } echo $tbl->toString(); } // end if (isset($quizDetails->questions[$questionID])) } // foreach ($results->quiz_data as $questionID => $answer) } printf('</form>'); // Shows a bar that pops up, allowing the user to easily save all grades that have changed. ?> <div id="wpcw_sticky_bar" style="display: none"> <div id="wpcw_sticky_bar_inner"> <a href="#" id="wpcw_tbl_progress_quiz_grading_updated" class="button-primary"><?php _e('Save Changes to Grades', 'wp_courseware'); ?> </a> <span id="wpcw_sticky_bar_status" title="<?php _e('Grades have been changed. Ready to save changes?', 'wp_courseware'); ?> "></span> </div> </div> <br/><br/><br/><br/> <?php $page->showPageFooter(); }
/** * Shows a the header at the top of the question. * * @param Integer $columnCount The number of columns that are being rendered to show the question. * @return String The HTML for rendering the header section. */ function getSection_processHeader($columnCount) { $html = false; // Show the quiz type $html .= sprintf('<tr class="wpcw_quiz_row_header">'); // Quiz type $html .= sprintf('<td colspan="%d">%s: <b>%s</b>', $columnCount, __('Type', 'wp_courseware'), WPCW_quizzes_getQuestionTypeName($this->questionType)); // Usage count if (!$this->hideQuestionUsageCount) { $html .= sprintf('<span class="wpcw_quiz_row_header_usage_count">(%s)</span>', sprintf(_n('In use by 1 quiz', 'In use by %d quizzes', $this->quizItem->question_usage_count, 'wp_courseware'), $this->quizItem->question_usage_count)); } $html .= '</td>'; $html .= '</tr>'; return $html; }
/** * 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; }