diff --git a/essaydownload_form.php b/essaydownload_form.php
index 35b5f1f..e7f5681 100644
--- a/essaydownload_form.php
+++ b/essaydownload_form.php
@@ -87,6 +87,17 @@ protected function standard_preference_fields(MoodleQuickForm $mform) {
$mform->addElement('advcheckbox', 'responsetext', get_string('includeresponsetext', 'quiz_essaydownload'));
$mform->addElement('advcheckbox', 'attachments', get_string('includeattachments', 'quiz_essaydownload'));
+ $mform->addElement(
+ 'select',
+ 'nameordering',
+ get_string('nameordering', 'quiz_essaydownload'),
+ [
+ 'lastfirst' => get_string('lastfirst', 'quiz_essaydownload'),
+ 'firstlast' => get_string('firstlast', 'quiz_essaydownload'),
+ ]
+ );
+ $mform->setType('nameordering', PARAM_ALPHA);
+
$mform->addElement(
'advcheckbox',
'shortennames',
diff --git a/essaydownload_options.php b/essaydownload_options.php
index 5ea30ac..75d1d21 100644
--- a/essaydownload_options.php
+++ b/essaydownload_options.php
@@ -54,12 +54,15 @@ class quiz_essaydownload_options extends quiz_essaydownload_options_parent_class
/** @var bool whether to include attachments (if there are) in the archive */
public $attachments = true;
- /** @var bool whether to shorten file and path names to workaround a Windows issue */
+ /** @var string whether to shorten file and path names to workaround a Windows issue */
public $shortennames = false;
/** @var string how to organise the sub folders in the archive (by question or by attempt) */
public $groupby = 'byattempt';
+ /** @var string whether to have the last name or the first name first */
+ public $nameordering = 'lastfirst';
+
/**
* Constructor
*
@@ -86,6 +89,7 @@ public function get_initial_form_data() {
$toform->attachments = $this->attachments;
$toform->shortennames = $this->shortennames;
$toform->groupby = $this->groupby;
+ $toform->nameordering = $this->nameordering;
return $toform;
}
@@ -101,6 +105,7 @@ public function setup_from_form_data($fromform): void {
$this->attachments = $fromform->attachments;
$this->shortennames = $fromform->shortennames;
$this->groupby = $fromform->groupby;
+ $this->nameordering = $fromform->nameordering;
}
/**
@@ -112,6 +117,7 @@ public function setup_from_params() {
$this->attachments = optional_param('attachments', $this->attachments, PARAM_BOOL);
$this->shortennames = optional_param('shortennames', $this->shortennames, PARAM_BOOL);
$this->groupby = optional_param('groupby', $this->groupby, PARAM_ALPHA);
+ $this->nameordering = optional_param('nameordering', $this->nameordering, PARAM_ALPHA);
}
/**
diff --git a/lang/en/quiz_essaydownload.php b/lang/en/quiz_essaydownload.php
index 03c91e6..a21ac1c 100644
--- a/lang/en/quiz_essaydownload.php
+++ b/lang/en/quiz_essaydownload.php
@@ -29,11 +29,14 @@
$string['errorfilename'] = 'error-{$a}.txt';
$string['errormessage'] = 'An internal error occurred. The archive is probably incomplete. Please contact the developers of the Essay responses downloader plugin (quiz_essaydownload) and send them the details below:';
$string['essaydownload'] = 'Download essay responses';
+$string['firstlast'] = 'First name - Last name';
$string['groupby'] = 'Group by';
$string['groupby_help'] = 'The archive can be structured by question or by attempt:
- If you group by question, the archive will have a folder for every question. Inside each folder, you will have a folder for every attempt.
- If you group by attempt, the archive will have a folder for every attempt. Inside each folder, you will have a folder for every question.
';
$string['includeattachments'] = 'Include attachments, if there are any';
$string['includequestiontext'] = 'Include question text';
$string['includeresponsetext'] = 'Include response text';
+$string['lastfirst'] = 'Last name - First name';
+$string['nameordering'] = 'Name format';
$string['noessayquestion'] = 'This quiz does not contain any essay questions.';
$string['nothingtodownload'] = 'Nothing to download';
$string['options'] = 'Options';
diff --git a/report.php b/report.php
index dbcaa31..b57053b 100644
--- a/report.php
+++ b/report.php
@@ -250,8 +250,15 @@ public function get_attempts_and_names(sql_join $joins): array {
$result->firstname = substr($result->firstname, 0, 40);
}
+ // The user can choose whether to start with the first name or the last name.
+ if ($this->options->nameordering === 'firstlast') {
+ $name = $result->firstname . '_' . $result->lastname;
+ } else {
+ $name = $result->lastname . '_' . $result->firstname;
+ }
+
// Build the path for this attempt: __.
- $path = $result->lastname . '_' . $result->firstname . '_' . $result->attemptid;
+ $path = $name . '_' . $result->attemptid;
$path = $path . '_' . date('Ymd_His', $result->timefinish);
$path = self::clean_filename($path);
diff --git a/tests/report_test.php b/tests/report_test.php
index 12fc025..62558b6 100644
--- a/tests/report_test.php
+++ b/tests/report_test.php
@@ -174,6 +174,63 @@ public function test_long_names_being_shortened(): void {
}
}
+ public function test_custom_name_order(): void {
+ $this->resetAfterTest();
+
+ // Create a course and a quiz with an essay question.
+ $generator = $this->getDataGenerator();
+ $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
+ $course = $generator->create_course();
+ $quiz = $this->create_test_quiz($course);
+ $quiz->name = 'ThisQuizHasAnExtremelyLongTitleBecauseLongTitlesAreJustSoCoolToHave';
+ quiz_essaydownload_test_helper::add_essay_question($questiongenerator, $quiz);
+
+ // Add a student and an attempt.
+ $student = \phpunit_util::get_data_generator()->create_user(['firstname' => 'First', 'lastname' => 'Last']);
+ \phpunit_util::get_data_generator()->enrol_user($student->id, $course->id, 'student');
+ $attempt = $this->attempt_quiz($quiz, $student);
+
+ $cm = get_coursemodule_from_id('quiz', $quiz->cmid);
+ $report = new quiz_essaydownload_report();
+
+ list($currentgroup, $allstudentjoins, $groupstudentjoins, $allowedjoins) =
+ $report->init('essaydownload', 'quiz_essaydownload_form', $quiz, $cm, $course);
+
+ // Use reflection to force other name format.
+ $reflectedreport = new \ReflectionClass($report);
+ $reflectedoptions = $reflectedreport->getProperty('options');
+ $reflectedoptions->setAccessible(true);
+ $options = new quiz_essaydownload_options('essaydownload', $quiz, $cm, $course);
+ $options->nameordering = 'firstlast';
+ $reflectedoptions->setValue($report, $options);
+
+ // Fetch the attemps using the report's API.
+ $fetchedattempts = $report->get_attempts_and_names($groupstudentjoins);
+
+ // There should be exactly one attempt.
+ self::assertCount(1, $fetchedattempts);
+
+ $i = 0;
+ foreach ($fetchedattempts as $fetchedid => $fetchedname) {
+ // The attempt is stored in a somewhat obscure way.
+ $attemptobj = $attempt[2]->get_attempt();
+
+ $id = $attemptobj->id;
+ self::assertEquals($id, $fetchedid);
+
+ $firstname = clean_filename(str_replace(' ', '_', $student->firstname));
+ $lastname = clean_filename(str_replace(' ', '_', $student->lastname));
+
+ $name = $firstname . '_' . $lastname . '_' . $id . '_' . date('Ymd_His', $attemptobj->timefinish);
+
+ // We will not compare the minutes and seconds, because there might be a small difference and
+ // we don't really care. If the timestamp is correct up to the hours, we can safely assume the
+ // conversion worked.
+ self::assertStringStartsWith(substr($name, 0, -4), $fetchedname);
+ $i++;
+ }
+ }
+
public function test_get_attempts_and_names_without_groups(): void {
$this->resetAfterTest();