Adding option "allow_fast_exercise_edition" to enable a fast way to edit exercise questions: jqgrid instead of draggable questio list, textarea instead of fckeditor see BT#6043

skala
Julio Montoya 12 years ago
parent 4b6afee605
commit 7f532ffb68
  1. 28
      main/course_info/infocours.php
  2. 23
      main/exercice/admin.php
  3. 22
      main/exercice/question.class.php
  4. 14
      main/exercice/question_admin.inc.php
  5. 111
      main/exercice/question_list_pagination_admin.inc.php
  6. 7
      main/exercice/unique_answer_no_option.class.php
  7. 40
      main/inc/ajax/model.ajax.php
  8. 21
      main/inc/lib/formvalidator/Element/html_editor.php
  9. 3
      main/inc/lib/formvalidator/FormValidator.class.php
  10. 50
      main/inc/lib/pear/HTML/QuickForm.php
  11. 5
      main/inc/lib/pear/HTML/QuickForm/element.php

@ -21,9 +21,6 @@ $this_section = SECTION_COURSES;
$nameTools = get_lang('ModifInfo');
/* Libraries */
require_once api_get_path(LIBRARY_PATH).'pdf.lib.php';
api_protect_course_script(true);
api_block_anonymous_users();
@ -39,7 +36,9 @@ $course_access_settings = CourseManager :: get_access_settings($course_code);
//LOGIC FUNCTIONS
function is_settings_editable() {
return isset($GLOBALS['course_info_is_editable']) && $GLOBALS['course_info_is_editable'];
//@todo check this setting This course_info_is_editable is defined where?
//return isset($GLOBALS['course_info_is_editable']) && $GLOBALS['course_info_is_editable'];
return true;
}
/* MAIN CODE */
@ -249,7 +248,7 @@ $form->addElement('style_submit_button', null, get_lang('SaveSettings'), 'class=
$form->addElement('html', '</div></div>');
// USER RIGHTS
$form->addElement('html', '<div> <h3>'.Display::return_icon('user.png', Security::remove_XSS(get_lang('UserRights')),'',ICON_SIZE_SMALL).' '.Security::remove_XSS(get_lang('UserRights')).'</h3><div>');
$form->addElement('html', '<div><h3>'.Display::return_icon('user.png', Security::remove_XSS(get_lang('UserRights')),'',ICON_SIZE_SMALL).' '.Security::remove_XSS(get_lang('UserRights')).'</h3><div>');
$group = array();
$group[]=$form->createElement('radio', 'allow_user_edit_agenda', get_lang('AllowUserEditAgenda'), get_lang('AllowUserEditAgendaActivate'), 1);
@ -303,7 +302,6 @@ if (api_get_setting('allow_course_theme') == 'true') {
$group[]=$form->createElement('radio', 'allow_learning_path_theme', get_lang('AllowLearningPathTheme'), get_lang('AllowLearningPathThemeAllow'), 1);
$group[]=$form->createElement('radio', 'allow_learning_path_theme', null, get_lang('AllowLearningPathThemeDisallow'), 0);
$form->addGroup($group, '', array(get_lang("AllowLearningPathTheme")), '');
}
if (is_settings_editable()) {
@ -317,6 +315,20 @@ if (is_settings_editable()) {
}
$form->addElement('html', '</div></div>');
//Exercise
$form->addElement('html', '<div><h3>'.Display::return_icon('quiz.png', Security::remove_XSS(get_lang('Exercises')),'',ICON_SIZE_SMALL).' '.Security::remove_XSS(get_lang('Exercises')).'</h3><div>');
$group = array();
$group[]=$form->createElement('radio', 'allow_fast_exercise_edition', get_lang('AllowFastExerciseEdition'), get_lang('Yes'), 1);
$group[]=$form->createElement('radio', 'allow_fast_exercise_edition', get_lang('AllowFastExerciseEdition'), get_lang('No'), 0);
$form->addGroup($group, '', array(get_lang("AllowFastExerciseEdition")), '');
$form->addElement('style_submit_button', null, get_lang('SaveSettings'), 'class="save"');
$form->addElement('html', '</div></div>');
// THEMATIC ADVANCE SETTINGS
$form->addElement('html', '<div><h3>'.Display::return_icon('course_progress.png', Security::remove_XSS(get_lang('ThematicAdvanceConfiguration')),'',ICON_SIZE_SMALL).' '.Security::remove_XSS(get_lang('ThematicAdvanceConfiguration')).'</h3><div>');
@ -328,12 +340,9 @@ $group[]=$form->createElement('radio', 'display_info_advance_inside_homecourse',
$form->addGroup($group, '', array(get_lang("InfoAboutAdvanceInsideHomeCourse")), '');
$form->addElement('style_submit_button', null, get_lang('SaveSettings'), 'class="save"');
$form->addElement('html', '</div></div>');
// Certificate settings
if (api_get_setting('allow_public_certificates')=='true') {
$form->addElement('html', '<div><h3>'.Display::return_icon('certificate.png', Security::remove_XSS(get_lang('Certificates')),'',ICON_SIZE_SMALL).' '.Security::remove_XSS(get_lang('Certificates')).'</h3><div>');
$group = array();
@ -401,6 +410,7 @@ $values['email_alert_students_on_new_homework'] = api_get_course_setting('em
$values['enable_lp_auto_launch'] = api_get_course_setting('enable_lp_auto_launch');
$values['pdf_export_watermark_text'] = api_get_course_setting('pdf_export_watermark_text');
$values['allow_public_certificates'] = api_get_course_setting('allow_public_certificates');
$values['allow_fast_exercise_edition'] = api_get_course_setting('allow_fast_exercise_edition') == 1 ? 1 : 0;
$app_plugin->set_course_settings_defaults($values);

@ -1,5 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Exercise administration
@ -142,6 +141,13 @@ $aType = array(
get_lang('FreeAnswer')
);
$fastEdition = api_get_course_setting('allow_fast_exercise_edition') == 1 ? true : false;
//$fastEdition = false;
if ($fastEdition) {
$htmlHeadXtra[] = api_get_jqgrid_js();
}
// tables used in the exercise tool
if (!empty($_GET['action']) && $_GET['action'] == 'exportqti2' && !empty($_GET['questionId'])) {
@ -545,7 +551,12 @@ if (isset($_GET['message'])) {
if ($newQuestion || $editQuestion) {
// statement management
$type = Security::remove_XSS($_REQUEST['answerType']);
if ($editQuestion) {
$type = $objQuestion->selectType();
} else {
$type = Security::remove_XSS($_REQUEST['answerType']);
}
echo '<input type="hidden" name="Type" value="'.$type.'" />';
require 'question_admin.inc.php';
}
@ -562,10 +573,14 @@ if (isset($_GET['hotspotadmin'])) {
if (!$newQuestion && !$modifyQuestion && !$editQuestion && !isset($_GET['hotspotadmin'])) {
// question list management
require 'question_list_admin.inc.php';
if ($fastEdition) {
require 'question_list_pagination_admin.inc.php';
} else {
require 'question_list_admin.inc.php';
}
}
Session::write('objExercise', $objExercise);
Session::write('objQuestion', $objQuestion);
Session::write('objAnswer', $objAnswer);
Display::display_footer();
Display::display_footer();

@ -38,7 +38,7 @@ define('MCMA', 2);
define('FIB', 3);
/**
QUESTION CLASS
* QUESTION CLASS
*
* This class allows to instantiate an object of type Question
*
@ -65,6 +65,8 @@ abstract class Question
static $explanationLangVar = '';
public $question_table_class = 'table table-striped';
public $editionMode = 'normal';
static $questionTypes = array(
UNIQUE_ANSWER => array('unique_answer.class.php', 'UniqueAnswer'),
MULTIPLE_ANSWER => array('multiple_answer.class.php', 'MultipleAnswer'),
@ -106,6 +108,7 @@ abstract class Question
$this->course = api_get_course_info();
$this->category_list = array();
$this->parent_id = 0;
$this->editionMode = 'normal';
}
public function getIsContent()
@ -185,6 +188,16 @@ abstract class Question
return false;
}
public function setEditionMode($mode)
{
$this->editionMode = $mode;
}
public function getEditionMode()
{
return $this->editionMode;
}
/**
* returns the question ID
*
@ -1358,8 +1371,8 @@ abstract class Question
$isContent = isset($_REQUEST['isContent']) ? intval($_REQUEST['isContent']) : null;
// Question type
$answerType = isset($_REQUEST['answerType']) ? intval($_REQUEST['answerType']) : null;
$form->addElement('hidden', 'answerType', $_REQUEST['answerType']);
$answerType = isset($_REQUEST['answerType']) ? intval($_REQUEST['answerType']) : $this->selectType();
$form->addElement('hidden', 'answerType', $answerType);
// html editor
$editor_config = array('ToolbarSet' => 'TestQuestionDescription', 'Width' => '100%', 'Height' => '150');
@ -1373,8 +1386,7 @@ abstract class Question
$form->addElement(
'advanced_settings',
'
<a href="javascript://" onclick=" return show_media()"><span id="media_icon"><img style="vertical-align: middle;" src="../img/looknfeel.png" alt="" />&nbsp;'.get_lang(
'<a href="javascript://" onclick=" return show_media()"><span id="media_icon"><img style="vertical-align: middle;" src="../img/looknfeel.png" alt="" />&nbsp;'.get_lang(
'EnrichQuestion'
).'</span></a>
'

@ -23,17 +23,17 @@ if (isset($_GET['editQuestion'])) {
}
if (is_object($objQuestion)) {
//FORM CREATION
//Form creation
$form = new FormValidator('question_admin_form','post', $action);
if(isset($_GET['editQuestion'])) {
if (isset($_GET['editQuestion'])) {
$class="btn save";
$text=get_lang('ModifyQuestion');
$type = Security::remove_XSS($_GET['type']);
//$type = Security::remove_XSS($_GET['type']); already loaded in admin.php
} else {
$class="btn add";
$text=get_lang('AddQuestionToExercise');
$type = $_REQUEST['answerType'];
//$type = $_REQUEST['answerType']; //already loaded in admin.php
}
$types_information = Question::get_question_type_list();
@ -42,9 +42,15 @@ if (is_object($objQuestion)) {
// form title
$form->addElement('header', $text.': '.$form_title_extra);
if ($fastEdition) {
$form->setAllowRichEditorInForm(false);
$form->setAllowedRichEditorList(array('questionDescription'));
}
// question form elements
$objQuestion->createForm($form);
// answer form elements
$objQuestion->createAnswersForm($form);

@ -0,0 +1,111 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Code library for HotPotatoes integration.
* @package chamilo.exercise
*/
/**
* QUESTION LIST ADMINISTRATION
*
* This script allows to manage the question list
* It is included from the script admin.php
*
*/
// deletes a question from the exercise (not from the data base)
if ($deleteQuestion) {
// if the question exists
if ($objQuestionTmp = Question::read($deleteQuestion)) {
$objQuestionTmp->delete($exerciseId);
// if the question has been removed from the exercise
if ($objExercise->removeFromList($deleteQuestion)) {
$nbrQuestions--;
}
}
// destruction of the Question object
unset($objQuestionTmp);
}
$token = Security::get_token();
//jqgrid will use this URL to do the selects
$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_question_list&exerciseId='.$exerciseId;
//The order is important you need to check the the $column variable in the model.ajax.php file
$columns = array(get_lang('Questions'), get_lang('Type'), get_lang('Category'), get_lang('Difficulty'), get_lang('Score'), get_lang('Actions'));
//Column config
$column_model = array(
array('name' => 'question', 'index' => 'question', 'width' => '300', 'align' => 'left'),
array(
'name' => 'type',
'index' => 'type',
'width' => '100',
'align' => 'left',
'sortable' => 'false'
),
array(
'name' => 'category',
'index' => 'category',
'width' => '100',
'align' => 'left',
'sortable' => 'false'
),
array(
'name' => 'level',
'index' => 'level',
'width' => '100',
'align' => 'left',
'sortable' => 'false'
),
array(
'name' => 'score',
'index' => 'score',
'width' => '100',
'align' => 'left',
'sortable' => 'false'
),
array(
'name' => 'actions',
'index' => 'actions',
'width' => '100',
'align' => 'left',
'formatter' => 'action_formatter',
'sortable' => 'false'
)
);
//Autowidth
$extra_params['autowidth'] = 'true';
//height auto
$extra_params['height'] = 'auto';
$courseCode = api_get_course_id();
$delete_link = null;
if ($objExercise->edit_exercise_in_lp == true) {
$delete_link = '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"), ENT_QUOTES))."\'".')) return false;" href="?cidReq='.$courseCode.'&sec_token='.$token.'&deleteQuestion=\'+options.rowId+\'">'.Display::return_icon( 'delete.png', get_lang('Delete'), '', ICON_SIZE_SMALL).'</a>';
}
//With this function we can add actions to the jgrid (edit, delete, etc)
$action_links = 'function action_formatter(cellvalue, options, rowObject) {
return \'<a href="?myid=1&cidReq='.$courseCode.'&editQuestion=\'+options.rowId+\'">'.Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_SMALL).'</a>'.
'&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"), ENT_QUOTES))."\'".')) return false;" href="?cidReq='.$courseCode.'&sec_token='.$token.'&clone_question=\'+options.rowId+\'">'.Display::return_icon('cd.gif',get_lang('Copy'), '',ICON_SIZE_SMALL).'</a>'.
$delete_link.'\';
}';
?>
<script>
$(function () {
<?php
// grid definition see the $career->display() function
echo Display::grid_js('question_list', $url, $columns, $column_model, $extra_params, array(), $action_links, true);
?>
});
</script>
<?php
Question :: display_type_menu($objExercise);
echo '<div style="clear:both;"></div>';
echo Display::grid_html('question_list');

@ -90,7 +90,7 @@ class UniqueAnswerNoOption extends Question {
'.get_lang('Weighting').'
</th>
</tr>';
$form -> addElement ('label', get_lang('Answers').'<br /> <img src="../img/fill_field.png">', $html);
$form->addElement('label', get_lang('Answers').'<br /> <img src="../img/fill_field.png">', $html);
$defaults = array();
$correct = 0;
@ -189,12 +189,11 @@ class UniqueAnswerNoOption extends Question {
$renderer->setElementTemplate('<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>', 'weighting['.$i.']');
$answer_number=$form->addElement('text', 'counter['.$i.']', null,'value="'.$i.'"');
$answer_number = $form->addElement('text', 'counter['.$i.']', null,'value="'.$i.'"');
$answer_number->freeze();
$form->addElement('radio', 'correct', null, null, $i, 'class="checkbox" style="margin-left: 0em;"');
$form->addElement('html_editor', 'answer['.$i.']', null, 'style="vertical-align:middle"', $editor_config);
$form->addElement('html_editor', 'answer['.$i.']', null, 'style="vertical-align:middle"', $editor_config);
if ($obj_ex->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_END) {
// feedback

@ -37,7 +37,8 @@ if (!in_array($action, array(
'get_work_user_list',
'get_timelines',
'get_user_skill_ranking',
'get_usergroups_teacher'
'get_usergroups_teacher',
'get_question_list'
))
) {
api_protect_admin_script(true);
@ -66,19 +67,26 @@ $ops = array(
function get_where_clause($col, $oper, $val) {
global $ops;
if (empty($col)){
if (empty($col)) {
return '';
}
if($oper == 'bw' || $oper == 'bn') $val .= '%';
if($oper == 'ew' || $oper == 'en' ) $val = '%'.$val;
if($oper == 'cn' || $oper == 'nc' || $oper == 'in' || $oper == 'ni') $val = '%'.$val.'%';
if ($oper == 'bw' || $oper == 'bn') {
$val .= '%';
}
if ($oper == 'ew' || $oper == 'en' ) {
$val = '%'.$val;
}
if ($oper == 'cn' || $oper == 'nc' || $oper == 'in' || $oper == 'ni') {
$val = '%'.$val.'%';
}
$val = Database::escape_string($val);
return " $col {$ops[$oper]} '$val' ";
}
$where_condition = ""; //if there is no search request sent by jqgrid, $where should be empty
$operation = isset($_REQUEST['oper']) ? $_REQUEST['oper'] : false;
$export_format = isset($_REQUEST['export_format']) ? $_REQUEST['export_format'] : 'csv';
$operation = isset($_REQUEST['oper']) ? $_REQUEST['oper'] : false;
$export_format = isset($_REQUEST['export_format']) ? $_REQUEST['export_format'] : 'csv';
$search_field = isset($_REQUEST['searchField']) ? $_REQUEST['searchField'] : false;
$search_oper = isset($_REQUEST['searchOper']) ? $_REQUEST['searchOper'] : false;
@ -173,6 +181,13 @@ if (!$sidx) $sidx = 1;
//@todo rework this
switch ($action) {
case 'get_question_list':
require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.class.php';
$exerciseId = isset($_REQUEST['exerciseId']) ? $_REQUEST['exerciseId'] : null;
$exercise = new Exercise(api_get_course_int_id());
$exercise->read($exerciseId);
$count = $exercise->selectNbrQuestions();
break;
case 'get_group_reporting':
$course_id = isset($_REQUEST['course_id']) ? $_REQUEST['course_id'] : null;
$group_id = isset($_REQUEST['gidReq']) ? $_REQUEST['gidReq'] : null;
@ -305,6 +320,12 @@ $is_allowedToEdit = api_is_allowed_to_edit(null,true) || api_is_allowed_to_edit(
$columns = array();
switch ($action) {
case 'get_question_list':
if (isset($exercise) && !empty($exercise)) {
$columns = array('question', 'type', 'category', 'level', 'score', 'actions');
$result = $exercise->getQuestionList($start, $limit, $sidx, $sord, $where_condition);
}
break;
case 'get_group_reporting':
$columns = array('name', 'time', 'progress', 'score', 'works', 'messages', 'actions');
$result = Tracking::get_group_reporting($course_id, $group_id, 'all', $start, $limit, $sidx, $sord, $where_condition);
@ -646,7 +667,8 @@ $allowed_actions = array(
'get_course_exercise_medias',
'get_user_course_report',
'get_user_course_report_resumed',
'get_group_reporting'
'get_group_reporting',
'get_question_list'
);
//5. Creating an obj to return a json
@ -690,7 +712,7 @@ if (in_array($action, $allowed_actions)) {
foreach ($result as $row) {
//print_r($row);
// if results tab give not id, set id to $i otherwise id="null" for all <tr> of the jqgrid - ref #4235
if ($row['id'] == "") {
if (!isset($row['id']) || isset($row['id']) && $row['id'] == "") {
$response->rows[$i]['id']=$i;
} else {
$response->rows[$i]['id']=$row['id'];

@ -15,6 +15,20 @@ class HTML_QuickForm_html_editor extends HTML_QuickForm_textarea {
var $fullPage;
var $fck_editor;
/**
* if true it show the fckeditor as usual if false it shows a textarea
* @var bool
*/
public $richEditorStatus = true;
public function getRichEditorStatus() {
return $this->richEditorStatus;
}
public function setRichEditorStatus($status) {
$this->richEditorStatus = (bool)$status;
}
/**
* Class constructor
* @param string HTML editor name/id
@ -75,6 +89,13 @@ class HTML_QuickForm_html_editor extends HTML_QuickForm_textarea {
* @return string
*/
function toHtml() {
if ($this->getRichEditorStatus() == false) {
//Fix rows and cols
//$this->_attributes['rows'] = 20;
//$this->_attributes['class'] = 20;
return parent::toHtml();
}
$value = $this->getValue();
if ($this->fullPage) {
if (strlen(trim($value)) == 0) {

@ -7,6 +7,7 @@
*/
class FormValidator extends HTML_QuickForm
{
/**
* Create a form validator based on an array of form data:
*
@ -133,7 +134,6 @@ class FormValidator extends HTML_QuickForm
$this->registerRule('compare_fields', null, 'HTML_QuickForm_Compare_Fields', $dir . 'Rule/CompareFields.php');
$this->registerRule('compare_datetime_text', null, 'HTML_QuickForm_Rule_CompareDateTimeText', $dir . 'Rule/CompareDateTimeText.php');
// Modify the default templates
$renderer = & $this->defaultRenderer();
@ -529,6 +529,7 @@ EOT;
return $return_value;
}
}
/**

@ -38,7 +38,7 @@ require_once 'HTML/Common.php';
* HTML_QuickForm::isTypeRegistered()
* @global array $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']
*/
$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =array(
$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] = array(
'group' => array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
'hidden' => array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
'reset' => array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
@ -284,6 +284,14 @@ class HTML_QuickForm extends HTML_Common
*/
var $_flagSubmitted = false;
/**
* If false disables the fckeditor HTML and show textareas
* @var bool
*/
public $allowRichEditorInForm = true;
public $allowedRichEditorList = array();
// }}}
// {{{ constructor
@ -1717,10 +1725,19 @@ class HTML_QuickForm extends HTML_Common
*/
function accept(&$renderer) {
$renderer->startForm($this);
foreach (array_keys($this->_elements) as $key) {
$element =& $this->_elements[$key];
$element = &$this->_elements[$key];
$elementName = $element->getName();
$required = ($this->isElementRequired($elementName) && !$element->isFrozen());
if ($this->getAllowRichEditorInForm() == false) {
if ($element->getType() == 'html_editor') {
if (!in_array($elementName, $this->getAllowedRichEditorList())) {
$element->setRichEditorStatus(false);
}
}
}
$required = $this->isElementRequired($elementName) && !$element->isFrozen();
$error = $this->getElementError($elementName);
$element->accept($renderer, $required, $error);
}
@ -2057,6 +2074,30 @@ class HTML_QuickForm extends HTML_Common
return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
} // end func errorMessage
/**
*
* @param bool $status
*/
public function setAllowRichEditorInForm($status) {
$this->allowRichEditorInForm = (bool)$status;
}
/**
* Returns the rich editor status
* @return bool status
*/
public function getAllowRichEditorInForm() {
return $this->allowRichEditorInForm;
}
public function setAllowedRichEditorList($array) {
$this->allowedRichEditorList = $array;
}
public function getAllowedRichEditorList() {
return $this->allowedRichEditorList;
}
// }}}
} // end class HTML_QuickForm
@ -2100,5 +2141,4 @@ class HTML_QuickForm_Error extends PEAR_Error {
}
}
// }}}
} // end class HTML_QuickForm_Error
?>
} // end class HTML_QuickForm_Error

@ -130,6 +130,7 @@ class HTML_QuickForm_element extends HTML_Common
return $this->_type;
} // end func getType
// }}}
// {{{ setName()
@ -238,7 +239,7 @@ class HTML_QuickForm_element extends HTML_Common
// Modified by Ivan Tcholakov, 16-MAR-2010.
//return ('' != $value? htmlspecialchars($value): '&nbsp;') .
// $this->_getPersistantData();
$value = ('' != $value ? @htmlspecialchars($value, ENT_COMPAT, HTML_Common::charset()): '&nbsp;') .
$this->_getPersistantData();
return '<span class="freeze">'.$value.'</span>';
@ -378,7 +379,7 @@ class HTML_QuickForm_element extends HTML_Common
switch ($event) {
case 'createElement':
$className = get_class($this);
$this->$className($arg[0], $arg[1], $arg[2], $arg[3], $arg[4]);
break;
case 'addElement':

Loading…
Cancel
Save