Skip to content
Snippets Groups Projects
Commit 423ea2ef authored by Elmar Ludwig's avatar Elmar Ludwig
Browse files

add batch actions for exercises, fixes #38

parent cf65371b
No related branches found
No related tags found
No related merge requests found
......@@ -1006,6 +1006,33 @@ class SheetsController extends StudipController
$this->redirect($this->url_for('sheets/edit_assignment', compact('assignment_id')));
}
/**
* Deletes a list of exercises from a specific assignment.
*/
public function delete_exercises_action()
{
CSRFProtection::verifyUnsafeRequest();
vips_require_status('tutor');
$exercise_ids = Request::intArray('exercise_ids');
$assignment_id = Request::int('assignment_id');
$assignment = VipsAssignment::find($assignment_id);
check_assignment_access($assignment);
if (!$assignment->isLocked()) {
foreach ($exercise_ids as $exercise_id) {
check_exercise_assignment($exercise_id, $assignment);
$assignment->test->removeExercise($exercise_id, true);
}
}
PageLayout::postSuccess(sprintf(_vips('Es wurden %s Aufgaben gelöscht.'), count($exercise_ids)));
$this->redirect($this->url_for('sheets/edit_assignment', compact('assignment_id')));
}
/**
* SHEETS/EXAMS
......@@ -1294,6 +1321,89 @@ class SheetsController extends StudipController
$this->redirect($this->url_for('sheets/edit_assignment', compact('assignment_id')));
}
/**
* Dialog for copying a list of exercises to another assignment.
*/
public function copy_exercises_dialog_action()
{
vips_require_status('tutor');
$this->assignment_id = Request::int('assignment_id');
$this->exercise_ids = Request::intArray('exercise_ids');
$this->courses = vips_active_courses($GLOBALS['user']->id);
}
/**
* Copy a list of exercises to the specified assignment.
*/
public function copy_exercises_action()
{
CSRFProtection::verifyUnsafeRequest();
vips_require_status('tutor');
$assignment_id = Request::int('assignment_id');
$copy_assignment_id = Request::int('copy_assignment_id');
$exercise_ids = Request::intArray('exercise_ids');
$assignment = VipsAssignment::find($assignment_id);
$copy_assignment = VipsAssignment::find($copy_assignment_id);
check_assignment_access($assignment);
check_copy_assignment_access($copy_assignment);
foreach ($exercise_ids as $exercise_id) {
$exercise_ref = $assignment->test->getExerciseRef($exercise_id);
$exercise_ref->copyIntoTest($copy_assignment->test_id);
}
PageLayout::postSuccess(n_vips('Die Aufgabe wurde kopiert.', 'Die Aufgaben wurden kopiert.', count($exercise_ids)));
$this->redirect($this->url_for('sheets/edit_assignment', compact('assignment_id')));
}
/**
* Dialog for moving a list of exercises to another assignment.
*/
public function move_exercises_dialog_action()
{
vips_require_status('tutor');
$this->assignment_id = Request::int('assignment_id');
$this->exercise_ids = Request::intArray('exercise_ids');
$this->courses = vips_active_courses($GLOBALS['user']->id);
}
/**
* Move a list of exercises to the specified assignment.
*/
public function move_exercises_action()
{
CSRFProtection::verifyUnsafeRequest();
vips_require_status('tutor');
$assignment_id = Request::int('assignment_id');
$copy_assignment_id = Request::int('copy_assignment_id');
$exercise_ids = Request::intArray('exercise_ids');
$assignment = VipsAssignment::find($assignment_id);
$copy_assignment = VipsAssignment::find($copy_assignment_id);
check_assignment_access($assignment);
check_copy_assignment_access($copy_assignment);
foreach ($exercise_ids as $exercise_id) {
$exercise_ref = $assignment->test->getExerciseRef($exercise_id);
$exercise_ref->copyIntoTest($copy_assignment->test_id);
$assignment->test->removeExercise($exercise_id, true);
}
PageLayout::postSuccess(n_vips('Die Aufgabe wurde verschoben.', 'Die Aufgaben wurden verschoben.', count($exercise_ids)));
$this->redirect($this->url_for('sheets/edit_assignment', compact('assignment_id')));
}
/**
* Exports all exercises in this assignment in a plain text format.
*/
......
<form class="default" action="<?= $controller->link_for('sheets/copy_exercises') ?>" method="POST">
<?= CSRFProtection::tokenTag() ?>
<input type="hidden" name="assignment_id" value="<?= $assignment_id ?>">
<? foreach ($exercise_ids as $exercise_id): ?>
<input type="hidden" name="exercise_ids[]" value="<?= $exercise_id ?>">
<? endforeach ?>
<label>
<?= _vips('Aufgabenblatt auswählen') ?>
<select name="copy_assignment_id" class="vips_nested_select">
<? foreach ($courses as $course): ?>
<? $assignments = VipsAssignment::findBySQL('course_id = ?', [$course->id]) ?>
<? $assignments = array_filter($assignments, function($a) { return !$a->isLocked(); }) ?>
<? usort($assignments, function($a, $b) { return strcoll($a->test->title, $b->test->title); }) ?>
<? if ($assignments): ?>
<optgroup label="<?= htmlReady($course->name . ' (' . $course->start_semester->name . ')') ?>">
<? foreach ($assignments as $assignment): ?>
<option value="<?= $assignment->id ?>">
<?= htmlReady($assignment->test->title) ?>
</option>
<? endforeach ?>
</optgroup>
<? endif ?>
<? endforeach ?>
</select>
</label>
<footer data-dialog-button>
<?= Studip\Button::createAccept(_vips('Kopieren'), 'copy') ?>
</footer>
</form>
<form class="default width-1200" action="<?= $controller->link_for('sheets/store_assignment') ?>" data-secure method="POST">
<form class="default width-1200" action="<?= $controller->link_for('sheets/store_assignment') ?>" method="POST">
<?= CSRFProtection::tokenTag() ?>
<input type="hidden" name="assignment_id" value="<?= $assignment_id ?>">
<button hidden name="store"></button>
......@@ -14,18 +14,18 @@
<label>
<span class="required"><?= _vips('Titel') ?></span>
<input type="text" name="assignment_name" class="character_input size-l" value="<?= htmlReady($test->title) ?>" required>
<input type="text" name="assignment_name" class="character_input size-l" value="<?= htmlReady($test->title) ?>" data-secure required>
</label>
<label>
<?= _vips('Beschreibung') ?>
<textarea name="assignment_description" class="character_input size-l wysiwyg"><?= wysiwygReady($test->description) ?></textarea>
<textarea name="assignment_description" class="character_input size-l wysiwyg" data-secure><?= wysiwygReady($test->description) ?></textarea>
</label>
<div style="margin-top: 1em;">
<? foreach ($assignment_types as $type => $entry) : ?>
<label class="undecorated">
<input type="radio" class="assignment_type" name="assignment_type" value="<?= $type ?>" <?= $assignment->type == $type ? 'checked' : '' ?>>
<input type="radio" class="assignment_type" name="assignment_type" value="<?= $type ?>" <?= $assignment->type == $type ? 'checked' : '' ?> data-secure>
<?= htmlReady($entry['name']) ?>
</label>
<? endforeach ?>
......@@ -36,8 +36,8 @@
<span class="required"><?= _vips('Startzeitpunkt') ?></span>
</div>
<input type="text" name="start_date" class="has-date-picker size-s" value="<?= date('d.m.Y', strtotime($assignment->start)) ?>" required>
<input type="text" name="start_time" class="has-time-picker size-s" value="<?= date('H:i', strtotime($assignment->start)) ?>" required>
<input type="text" name="start_date" class="has-date-picker size-s" value="<?= date('d.m.Y', strtotime($assignment->start)) ?>" data-secure required>
<input type="text" name="start_time" class="has-time-picker size-s" value="<?= date('H:i', strtotime($assignment->start)) ?>" data-secure required>
</label>
<? $required = $assignment->type !== 'selftest' ? 'required' : '' ?>
......@@ -47,15 +47,15 @@
<span class="<?= $required ?>"><?= _vips('Endzeitpunkt') ?></span>
</div>
<input type="text" name="end_date" class="has-date-picker size-s" value="<?= $assignment->isUnlimited() ? '' : date('d.m.Y', strtotime($assignment->end)) ?>" <?= $required ?>>
<input type="text" name="end_time" class="has-time-picker size-s" value="<?= $assignment->isUnlimited() ? '' : date('H:i', strtotime($assignment->end)) ?>" <?= $required ?>>
<input type="text" name="end_date" class="has-date-picker size-s" value="<?= $assignment->isUnlimited() ? '' : date('d.m.Y', strtotime($assignment->end)) ?>" data-secure <?= $required ?>>
<input type="text" name="end_time" class="has-time-picker size-s" value="<?= $assignment->isUnlimited() ? '' : date('H:i', strtotime($assignment->end)) ?>" data-secure <?= $required ?>>
</label>
<? $disabled = $assignment->type !== 'exam' ? 'disabled' : '' ?>
<label id="exam_length" class="practice-hidden selftest-hidden">
<span class="required"><?= _vips('Dauer in Minuten') ?></span>
<input type="number" name="exam_length" value="<?= htmlReady($assignment->options['duration']) ?>" <?= $disabled ?> required>
<input type="number" name="exam_length" value="<?= htmlReady($assignment->options['duration']) ?>" <?= $disabled ?> data-secure required>
</label>
<input id="options-toggle" type="checkbox" value="on">
......@@ -71,7 +71,7 @@
<?= _vips('Block') ?>
</div>
<select name="assignment_block" style="max-width: 22.7em;">
<select name="assignment_block" style="max-width: 22.7em;" data-secure>
<option value="0">
<?= _vips('Keinem Block zuweisen') ?>
</option>
......@@ -82,46 +82,46 @@
<? endforeach ?>
</select>
<?= _vips('oder') ?>
<input type="text" name="assignment_block_name" style="max-width: 22.7em;" placeholder="<?= _vips('Neuen Block anlegen') ?>">
<input type="text" name="assignment_block_name" style="max-width: 22.7em;" placeholder="<?= _vips('Neuen Block anlegen') ?>" data-secure>
</label>
<label class="exam-hidden selftest-hidden">
<input type="checkbox" name="use_groups" value="1" <?= $assignment->options['use_groups'] !== 0 ? 'checked' : '' ?>>
<input type="checkbox" name="use_groups" value="1" <?= $assignment->options['use_groups'] !== 0 ? 'checked' : '' ?> data-secure>
<?= _vips('Aufgaben können in Übungsgruppen bearbeitet werden') ?>
<?= tooltipIcon(_vips('Hat keine Auswirkungen, wenn keine Übungsgruppen angelegt wurden.')) ?>
</label>
<label class="practice-hidden selftest-hidden">
<input type="checkbox" name="self_assessment" value="1" <?= $assignment->options['self_assessment'] ? 'checked' : '' ?>>
<input type="checkbox" name="self_assessment" value="1" <?= $assignment->options['self_assessment'] ? 'checked' : '' ?> data-secure>
<?= _vips('Testklausur zur Selbsteinschätzung der Teilnehmer') ?>
<?= tooltipIcon(_vips('Teilnehmer können beliebig oft neu starten, Ergebnisse können direkt nach Ablauf der Bearbeitungszeit zugänglich gemacht werden.')) ?>
</label>
<label class="practice-hidden selftest-hidden">
<input type="checkbox" name="shuffle_exercises" value="1" <?= $assignment->options['shuffle_exercises'] ? 'checked' : '' ?>>
<input type="checkbox" name="shuffle_exercises" value="1" <?= $assignment->options['shuffle_exercises'] ? 'checked' : '' ?> data-secure>
<?= _vips('Zufällige Reihenfolge der Aufgaben bei Anzeige der Klausur') ?>
</label>
<label class="practice-hidden selftest-hidden">
<input type="checkbox" name="shuffle_answers" value="1" <?= $assignment->options['shuffle_answers'] !== 0 ? 'checked' : '' ?>>
<input type="checkbox" name="shuffle_answers" value="1" <?= $assignment->options['shuffle_answers'] !== 0 ? 'checked' : '' ?> data-secure>
<?= _vips('Zufällige Reihenfolge der Antworten in Multiple- und Single-Choice-Aufgaben') ?>
</label>
<label class="exam-hidden practice-hidden">
<input type="checkbox" name="resets" value="1" <?= $assignment->options['resets'] !== 0 ? 'checked' : '' ?>>
<input type="checkbox" name="resets" value="1" <?= $assignment->options['resets'] !== 0 ? 'checked' : '' ?> data-secure>
<?= _vips('Teilnehmer dürfen ihre Lösungen zurücksetzen und den Test neu starten') ?>
</label>
<label class="exam-hidden practice-hidden">
<?= _vips('Anzahl der Lösungsversuche pro Aufgabe') ?>
<input type="number" name="max_tries" min="1" value="<?= $assignment->options['max_tries'] ?: 3 ?>">
<input type="number" name="max_tries" min="1" value="<?= $assignment->options['max_tries'] ?: 3 ?>" data-secure>
</label>
<label>
<? $selected[$assignment->options['evaluation_mode']] = 'selected' ?>
<?= _vips('Falsche Antworten in Multiple- und Single-Choice-Aufgaben') ?>
<select name="evaluation_mode">
<select name="evaluation_mode" data-secure>
<option value="0" <?= $selected[0] ?>>
<?= _vips('&hellip; geben keinen Punktabzug') ?>
</option>
......@@ -139,18 +139,18 @@
<label>
<?= _vips('Notizen (für Teilnehmer unsichtbar)') ?>
<textarea name="assignment_notes" class="character_input"><?= htmlReady($assignment->options['notes']) ?></textarea>
<textarea name="assignment_notes" class="character_input" data-secure><?= htmlReady($assignment->options['notes']) ?></textarea>
</label>
<label class="practice-hidden selftest-hidden">
<?= _vips('Zugangscode zur Klausur (optional)') ?>
<input type="text" name="access_code" value="<?= htmlReady($assignment->options['access_code']) ?>">
<input type="text" name="access_code" value="<?= htmlReady($assignment->options['access_code']) ?>" data-secure>
</label>
<label class="practice-hidden selftest-hidden">
<?= _vips('IP-Zugriffsbereich (optional)') ?>
<?= tooltipIcon($this->render_partial('sheets/ip_range_tooltip'), false, true) ?>
<input type="text" name="ip_range" value="<?= htmlReady($assignment->options['ip_range']) ?>">
<input type="text" name="ip_range" value="<?= htmlReady($assignment->options['ip_range']) ?>" data-secure>
</label>
<? if ($exam_rooms): ?>
......@@ -166,12 +166,15 @@
</div>
</fieldset>
<? if (count($test->exercise_refs)): ?>
<table class="default" id="exercises">
<? setlocale(LC_NUMERIC, $_SESSION['_language'] . '.UTF-8') ?>
<? if (count($test->exercise_refs)): ?>
<thead>
<tr>
<th style="padding-left: 2ex;">
<input type="checkbox" data-proxyfor=".batch_select" data-activates=".batch_action" title="<?= _vips('Alle Aufgaben auswählen') ?>">
</th>
<th></th>
<th style="width: 60%;">
<?= _vips('Aufgaben') ?>
......@@ -191,30 +194,47 @@
<tbody id="list" class="dynamic_list" data-assignment="<?= $assignment_id ?>">
<?= $this->render_partial('sheets/list_exercises') ?>
</tbody>
<? endif ?>
<tfoot>
<tr>
<td colspan="3"></td>
<td colspan="4">
<?= Studip\Button::createAccept(_vips('Speichern'), 'store') ?>
<? if ($assignment_id && !$locked): ?>
<?= Studip\LinkButton::create(_vips('Neue Aufgabe erstellen'),
$controller->url_for('sheets/add_exercise_dialog', compact('assignment_id')),
['data-dialog' => 'size=auto']) ?>
<? endif ?>
<? if (count($test->exercise_refs)): ?>
<?= Studip\Button::create(_vips('Kopieren'), 'copy_exercises', [
'class' => 'batch_action',
'formaction' => $controller->url_for('sheets/copy_exercises_dialog'),
'data-dialog' => 'size=auto'
]) ?>
<? if (!$locked): ?>
<?= Studip\Button::create(_vips('Verschieben'), 'move_exercises', [
'class' => 'batch_action',
'formaction' => $controller->url_for('sheets/move_exercises_dialog'),
'data-dialog' => 'size=auto'
]) ?>
<?= Studip\Button::create(_vips('Löschen'), 'delete_exercises', [
'class' => 'batch_action',
'formaction' => $controller->url_for('sheets/delete_exercises'),
'data-confirm' => _vips('Wollen Sie wirklich die ausgewählten Aufgaben löschen?')
]) ?>
<? endif ?>
<? endif ?>
</td>
<td colspan="2" style="padding-left: 0px;">
<? if (count($test->exercise_refs)): ?>
<div class="points">
<?= $test->getTotalPoints() ?>
</div>
<? endif ?>
</td>
</tr>
</tfoot>
<? setlocale(LC_NUMERIC, 'C') ?>
</table>
<? endif ?>
<footer>
<?= Studip\Button::createAccept(_vips('Speichern'), 'store') ?>
<? if ($assignment_id && !$locked): ?>
<?= Studip\LinkButton::create(_vips('Neue Aufgabe erstellen'), $controller->url_for('sheets/add_exercise_dialog', compact('assignment_id')), ['data-dialog' => 'size=auto']) ?>
<? elseif ($locked): ?>
<?= Studip\Button::create(_vips('Neue Aufgabe erstellen'), 'none', ['disabled' => '']) ?>
<? else: ?>
<?= Studip\Button::create(_vips('Neue Aufgabe erstellen'), 'none', ['disabled' => '', 'title' => _vips('Aufgaben können erst nach dem Speichern der Grunddaten hinzugefügt werden.')]) ?>
<? endif ?>
</footer>
</form>
......@@ -2,7 +2,10 @@
<? $exercise = $exercises[$i] ?>
<tr id="item_<?= $exercise->id ?>">
<td class="dynamic_counter vips_drag" style="padding-left: 2ex; text-align: right;">
<td class="vips_drag" style="padding-left: 2ex;">
<input type="checkbox" class="batch_select" name="exercise_ids[]" value="<?= $exercise->id ?>" aria-label="<?= _vips('Zeile auswählen') ?>">
</td>
<td class="dynamic_counter" style="text-align: right;">
<!-- position -->
</td>
<td>
......@@ -17,7 +20,7 @@
</td>
<td>
<!-- max points -->
<input name="exercise_points[<?= $exercise->id ?>]" type="text" class="points" value="<?= (float) $exercise_ref->points ?>" required>
<input name="exercise_points[<?= $exercise->id ?>]" type="text" class="points" value="<?= (float) $exercise_ref->points ?>" data-secure required>
</td>
<td class="actions">
......
<form class="default" action="<?= $controller->link_for('sheets/move_exercises') ?>" method="POST">
<?= CSRFProtection::tokenTag() ?>
<input type="hidden" name="assignment_id" value="<?= $assignment_id ?>">
<? foreach ($exercise_ids as $exercise_id): ?>
<input type="hidden" name="exercise_ids[]" value="<?= $exercise_id ?>">
<? endforeach ?>
<label>
<?= _vips('Aufgabenblatt auswählen') ?>
<select name="copy_assignment_id" class="vips_nested_select">
<? foreach ($courses as $course): ?>
<? $assignments = VipsAssignment::findBySQL('course_id = ?', [$course->id]) ?>
<? $assignments = array_filter($assignments, function($a) { return !$a->isLocked(); }) ?>
<? usort($assignments, function($a, $b) { return strcoll($a->test->title, $b->test->title); }) ?>
<? if ($assignments): ?>
<optgroup label="<?= htmlReady($course->name . ' (' . $course->start_semester->name . ')') ?>">
<? foreach ($assignments as $assignment): ?>
<option value="<?= $assignment->id ?>">
<?= htmlReady($assignment->test->title) ?>
</option>
<? endforeach ?>
</optgroup>
<? endif ?>
<? endforeach ?>
</select>
</label>
<footer data-dialog-button>
<?= Studip\Button::createAccept(_vips('Verschieben'), 'move') ?>
</footer>
</form>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment