diff --git a/classes/Report.php b/classes/Report.php
index c1753d2..ae370fc 100644
--- a/classes/Report.php
+++ b/classes/Report.php
@@ -565,7 +565,7 @@ public function generate_full_page(int $attemptid, array $sections, bool $fix_re
// Build HTML tree
$html = "";
$html .= $OUTPUT->header();
- // $html .= \quiz_archiver\local\coversheet\create_coversheet::get_coversheet($attemptid);
+ $html .= \quiz_archiver\coversheet\create_coversheet::get_coversheet($attemptid);
$html .= self::generate($attemptid, $sections);
$html .= $OUTPUT->footer();
diff --git a/classes/coversheet/create_coversheet.php b/classes/coversheet/create_coversheet.php
index 71cbf1d..7999423 100644
--- a/classes/coversheet/create_coversheet.php
+++ b/classes/coversheet/create_coversheet.php
@@ -23,9 +23,11 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-namespace quiz_archiver\local\coversheet;
+namespace quiz_archiver\coversheet;
use admin_setting_heading;
+use coding_exception;
+use quiz_archiver\external\get_attempts_metadata;
defined('MOODLE_INTERNAL') || die();
@@ -40,7 +42,7 @@
class create_coversheet {
/**
- * Create the coversheet. Only this method should be called from outside.
+ * Create the coversheet.
* @param int $attemptid
* @return string
*/
@@ -48,11 +50,137 @@ public static function get_coversheet(int $attemptid): string {
$config = get_config('quiz_archiver');
- if(empty($config->enable_pdf_coversheet)) {
+ if (empty($config->enable_pdf_coversheet)) {
return '';
}
- return $config->dynamic_pdf_content;
+ // Get all needed attempt metadata. E.g. User, timestarted, timefinished etc.
+ $attemptmetadata = self::get_attempt_metadata($attemptid);
+ // Find all placeholders.
+ preg_match_all('/{{(.*)_(.*)}}/', $config->dynamic_pdf_content, $matches);
+
+ // Now replace all placeholders.
+ $html = $config->dynamic_pdf_content;
+ foreach ($matches[0] as $key => $placeholder) {
+ $classpath = '\quiz_archiver\coversheet\placeholder\\' . $matches[1][$key];
+ $method = $matches[2][$key];
+ $replacement = self::check_class_and_method($classpath, $method, $attemptmetadata);
+ $html = preg_replace('/' . $placeholder . '/', $replacement, $html);
+ }
+
+ \local_debugger\performance\debugger::print_debug('test', 'get_coversheet', $html);
+ return '
' . $config->dynamic_pdf_content . '
';
+ }
+
+ /**
+ * Gets the metadata of all attempts made inside this quiz, excluding previews.
+ *
+ * @param array|null $filter_attemptids If given, only attempts with the given
+ * IDs will be returned.
+ *
+ * @return object
+ * @throws \dml_exception
+ */
+ private static function get_attempt_metadata(int $attemptid): object {
+ global $DB;
+
+ $fields = [
+ 'qa.id AS attemptid',
+ 'qa.userid',
+ 'qa.quiz as quizinstance',
+ 'qa.attempt as attemptnumber',
+ 'qa.state',
+ 'qa.timestart',
+ 'qa.timefinish',
+ 'q.course as courseid',
+ ];
+
+ $sql = "SELECT " . join(", ", $fields) .
+ " FROM {quiz_attempts} qa " .
+ "LEFT JOIN {user} u ON qa.userid = u.id " .
+ "LEFT JOIN {quiz} q on q.id = qa.quiz " .
+ "WHERE qa.id = :qaid";
+
+ // Get all requested attempt
+ return $DB->get_record_sql($sql, ["qaid" => $attemptid]);
+ }
+
+ /**
+ * Checks and executes the callback method.
+ * @param string $classpath
+ * @param string $method
+ * @param string|int|object|array $params
+ * @return string
+ */
+ private static function check_class_and_method(string $classpath, string $method, string|int|object|array $params): string {
+
+ if (!class_exists($classpath)) {
+ return 'Class ' . $classpath . ' not found.';
+ }
+
+ $class = new $classpath();
+ if (!method_exists($class, $method)) {
+ return 'Placeholder for ' . $method . ' not found.';
+ }
+
+ return $class::$method($params);
+ }
+
+ /**
+ * Get all possible placeholders in a mustache context format.
+ *
+ * @return array
+ */
+ public static function get_possible_placeholders(): array {
+ global $CFG;
+ $placeholders = [];
+ $dir = $CFG->dirroot . "/mod/quiz/report/archiver/classes/coversheet/placeholder";
+ $basenames = self::get_all_files_in_directory($dir);
+ foreach ($basenames as $basename) {
+ $placeholders[] = [
+ 'placeholders' => self::get_placeholders($basename, "\quiz_archiver\coversheet\placeholder\\$basename"),
+ 'metadata' => [
+ 'tabid' => 'qa_' . $basename . '_tab',
+ 'tab' => get_string($basename, 'quiz_archiver'),
+ ],
+ ];
+ }
+
+ return $placeholders;
+ }
+
+ /**
+ * Get the array of the placeholders.
+ *
+ * @param string $basename
+ * @param string $classname
+ * @return array
+ */
+ private static function get_placeholders(string $basename, string $classname): array {
+ $methods = get_class_methods($classname);
+ $placeholders = [];
+ foreach ($methods as $method) {
+ $placeholders[] = $basename . "_" . $method;
+ }
+ return $placeholders;
+ }
+
+ /**
+ * Get all basenames of files in a specific directory
+ *
+ * @param string $dir
+ * @return array
+ * @throws coding_exception
+ */
+ private static function get_all_files_in_directory(string $dir): array {
+ $files = scandir($dir);
+ $basenames = [];
+ foreach ($files as $file) {
+ if (is_file($dir . '/' . $file)) {
+ $basenames[] = basename($file, '.php');
+ }
+ }
+ return $basenames;
}
}
diff --git a/classes/coversheet/placeholder/course.php b/classes/coversheet/placeholder/course.php
new file mode 100644
index 0000000..e14d07c
--- /dev/null
+++ b/classes/coversheet/placeholder/course.php
@@ -0,0 +1,101 @@
+.
+
+/**
+ * Handles course callback to get corse info.
+ *
+ * @package quiz_archiver
+ * @copyright ISB Bayern, 2024
+ * @author Dr. Peter Mayer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace quiz_archiver\coversheet\placeholder;
+
+use dml_exception;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Handles course callback to get corse info.
+ *
+ * @package quiz_archiver
+ * @copyright ISB Bayern, 2024
+ * @author Dr. Peter Mayer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class course {
+
+ /**
+ * Get course fullname.
+ * @param object $params
+ * @return string
+ */
+ public static function fullname(object $params): string {
+ $course = get_course($params->courseid);
+ return $course->fullname;
+ }
+
+ /**
+ * Get course shortname.
+ * @param object $params
+ * @return string
+ */
+ public static function shortname(object $params): string {
+ $course = get_course($params->courseid);
+ return $course->shortname;
+ }
+
+ /**
+ * Get course summary.
+ * @param object $params
+ * @return string
+ */
+ public static function summary(object $params): string {
+ $course = get_course($params->courseid);
+ return $course->summary;
+ }
+
+ /**
+ * Get course format.
+ * @param object $params
+ * @return string
+ */
+ public static function format(object $params): string {
+ $course = get_course($params->courseid);
+ return $course->format;
+ }
+
+ /**
+ * Get course startdate.
+ * @param object $params
+ * @return string
+ */
+ public static function startdate(object $params): string {
+ $course = get_course($params->courseid);
+ return $course->startdate;
+ }
+
+ /**
+ * Get course enddate.
+ * @param object $params
+ * @return string
+ */
+ public static function enddate(object $params): string {
+ $course = get_course($params->courseid);
+ return $course->enddate;
+ }
+}
diff --git a/classes/coversheet/placeholder/profile.php b/classes/coversheet/placeholder/profile.php
index e69de29..4cc364e 100644
--- a/classes/coversheet/placeholder/profile.php
+++ b/classes/coversheet/placeholder/profile.php
@@ -0,0 +1,183 @@
+.
+
+/**
+ * Handles everything that is needed for coversheet creation.
+ *
+ * @package quiz_archiver
+ * @copyright ISB Bayern, 2024
+ * @author Dr. Peter Mayer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace quiz_archiver\coversheet\placeholder;
+
+use dml_exception;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Handles everything that is needed for coversheet creation.
+ *
+ * @package quiz_archiver
+ * @copyright ISB Bayern, 2024
+ * @author Dr. Peter Mayer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class profile {
+
+ // @var userprofilefilds
+ var $userprofilefiels = [
+ 'id',
+ 'auth',
+ 'confirmed',
+ 'policyagreed',
+ 'deleted',
+ 'suspended',
+ 'mnethostid',
+ 'username',
+ 'password',
+ 'idnumber',
+ 'firstname',
+ 'lastname',
+ 'email',
+ 'emailstop',
+ 'phone1',
+ 'phone2',
+ 'institution',
+ 'department',
+ 'address',
+ 'city',
+ 'country',
+ 'lang',
+ 'calendartype',
+ 'theme',
+ 'timezone',
+ 'firstaccess',
+ 'lastaccess',
+ 'lastlogin',
+ 'currentlogin',
+ 'lastip',
+ 'secret',
+ 'picture',
+ 'description',
+ 'descriptionformat',
+ 'mailformat',
+ 'maildigest',
+ 'maildisplay',
+ 'autosubscribe',
+ 'trackforums',
+ 'timecreated',
+ 'timemodified',
+ 'trustbitmask',
+ 'imagealt',
+ 'lastnamephonetic',
+ 'firstnamephonetic',
+ 'middlename',
+ 'alternatename',
+ 'moodlenetprofile',
+ ];
+
+ /**
+ * Get user firstname.
+ * @param object $params
+ * @return string
+ */
+ public static function firstname( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->firstname;
+ }
+
+ /**
+ * Get user lastname.
+ * @param object $params
+ * @return string
+ */
+ public static function lastname( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->lastname;
+ }
+
+ /**
+ * Get user fullname.
+ * @param object $params
+ * @return string
+ */
+ public static function userfullname( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return \core_user::get_fullname($user);
+ }
+
+ /**
+ * Get user institution.
+ * @param object $params
+ * @return string
+ */
+ public static function institution( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->institution;
+ }
+
+ /**
+ * Get user department.
+ * @param object $params
+ * @return string
+ */
+ public static function department( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->department;
+ }
+
+ /**
+ * Get user address.
+ * @param object $params
+ * @return string
+ */
+ public static function address( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->address;
+ }
+
+ /**
+ * Get user city.
+ * @param object $params
+ * @return string
+ */
+ public static function city( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->city;
+ }
+
+ /**
+ * Get user country.
+ * @param object $params
+ * @return string
+ */
+ public static function country( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->country;
+ }
+
+ /**
+ * Get user language.
+ * @param object $params
+ * @return string
+ */
+ public static function language( object $params): string {
+ $user = \core_user::get_user($params->userid);
+ return $user->lang;
+ }
+}
diff --git a/classes/local/admin/setting/setting_button.php b/classes/local/admin/setting/setting_button.php
index d225417..25f1bf7 100644
--- a/classes/local/admin/setting/setting_button.php
+++ b/classes/local/admin/setting/setting_button.php
@@ -23,7 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-namespace quiz_archiver\local\setting\admin;
+namespace quiz_archiver\local\admin\setting;
use admin_setting_heading;
diff --git a/lang/en/quiz_archiver.php b/lang/en/quiz_archiver.php
index 8622ed5..3d5647f 100644
--- a/lang/en/quiz_archiver.php
+++ b/lang/en/quiz_archiver.php
@@ -83,6 +83,8 @@
$string['export_report_section_attachments_help'] = 'Include all file attachments (e.g., essay file submissions) inside the archive. Warning: This can significantly increase the archive size.';
$string['job_overview'] = 'Archives';
$string['num_attempts'] = 'Number of attempts';
+
+// Language strings for pdf cover sheet.
$string['pdfcoversheet_settings'] = 'PDF Cover Sheet';
$string['pdfcoversheet_settings_desc'] = 'Define a default cover sheet';
$string['enable_pdf_coversheet'] = 'Ad PDF cover sheet';
@@ -93,6 +95,10 @@
$string['pdf_coversheet_html_area_help'] = 'Use placeholders for dynamic cover sheet contents';
$string['pdfcoversheet_heading'] = 'PDF Cover Sheet';
$string['define_pdfcoversheet'] = 'Define Cover Sheet';
+$string['exiting_placeholders'] = 'Possible Placeholders';
+$string['exiting_placeholders_desc'] = 'You can use the placeholder by writing {{placeholder}} e.g. {{profile_userfullname}} to the template above.';
+$string['profile'] = 'Profile';
+$string['course'] = 'Course';
// Job creation form: Filename pattern
$string['archive_filename_pattern'] = 'Archive name';
diff --git a/settings_coversheet.php b/settings_coversheet.php
index 03e7e69..69d7087 100644
--- a/settings_coversheet.php
+++ b/settings_coversheet.php
@@ -90,6 +90,8 @@
if(!empty($dynamicpdfcontent = get_config('quiz_archiver', 'dynamic_pdf_content'))) {
$templatecontext['storedhtml'] = get_config('quiz_archiver', 'dynamic_pdf_content');
}
+
+$templatecontext['placeholderdata'] = \quiz_archiver\coversheet\create_coversheet::get_possible_placeholders();
// print_r(get_config('quiz_archiver', 'dynamic_pdf_content'));die;
echo $OUTPUT->header();
diff --git a/templates/define_pdfcoversheet.mustache b/templates/define_pdfcoversheet.mustache
index b142b8c..1441e75 100644
--- a/templates/define_pdfcoversheet.mustache
+++ b/templates/define_pdfcoversheet.mustache
@@ -32,12 +32,36 @@