diff --git a/element/daterange/classes/element.php b/element/daterange/classes/element.php new file mode 100644 index 0000000..d8d326d --- /dev/null +++ b/element/daterange/classes/element.php @@ -0,0 +1,438 @@ +. + +/** + * This file contains the customcert date range element. + * + * @package customcertelement_daterange + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace customcertelement_daterange; + +use \mod_customcert\element_helper; + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +require_once($CFG->dirroot . '/lib/grade/constants.php'); + +/** + * The customcert date range element. + * + * @package customcertelement_daterange + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class element extends \mod_customcert\element { + /** + * Default max number of dateranges per element. + */ + const DEFAULT_MAX_RANGES = 10; + + /** + * Date - Issue + */ + const DATE_ISSUE = 1; + + /** + * Date - Completion + */ + const DATE_COMPLETION = 2; + + /** + * Date - Course start + */ + const DATE_COURSE_START = 3; + + /** + * Date - Course end + */ + const DATE_COURSE_END = 4; + + /** + * Date - Course grade date + */ + const DATE_COURSE_GRADE = 5; + + /** + * This function renders the form elements when adding a customcert element. + * + * @param \mod_customcert\edit_element_form $mform the edit_form instance + */ + public function render_form_elements($mform) { + global $COURSE; + + // Get the possible date options. + $dateoptions = array(); + $dateoptions[self::DATE_ISSUE] = get_string('issueddate', 'customcertelement_daterange'); + $dateoptions[self::DATE_COMPLETION] = get_string('completiondate', 'customcertelement_daterange'); + $dateoptions[self::DATE_COURSE_START] = get_string('coursestartdate', 'customcertelement_daterange'); + $dateoptions[self::DATE_COURSE_END] = get_string('courseenddate', 'customcertelement_daterange'); + $dateoptions[self::DATE_COURSE_GRADE] = get_string('coursegradedate', 'customcertelement_daterange'); + + $dateoptions = $dateoptions + element_helper::get_grade_items($COURSE); + + $mform->addElement('select', 'dateitem', get_string('dateitem', 'customcertelement_daterange'), $dateoptions); + $mform->addHelpButton('dateitem', 'dateitem', 'customcertelement_daterange'); + + parent::render_form_elements($mform); + + $mform->addElement('header', 'dateranges', get_string('dateranges', 'customcertelement_daterange')); + $mform->addElement('static', 'help', '', get_string('help', 'customcertelement_daterange')); + + $mform->addElement('text', 'fallbackstring', get_string('fallbackstring', 'customcertelement_daterange')); + $mform->addHelpButton('fallbackstring', 'fallbackstring', 'customcertelement_daterange'); + $mform->setType('fallbackstring', PARAM_NOTAGS); + + if (!$maxranges = get_config('customcertelement_daterange', 'maxranges')) { + $maxranges = self::DEFAULT_MAX_RANGES; + } + + if (!empty($this->get_data())) { + if ($maxranges < $this->get_decoded_data()->numranges) { + $maxranges = $this->get_decoded_data()->numranges; + } + } + + $mform->addElement('hidden', 'numranges', $maxranges); + $mform->setType('numranges', PARAM_INT); + + for ($i = 0; $i < $maxranges; $i++) { + $datarange = array(); + + $datarange[] = $mform->createElement( + 'date_selector', + $this->build_element_name('startdate', $i), + get_string('start', 'customcertelement_daterange') + ); + + $datarange[] = $mform->createElement( + 'date_selector', + $this->build_element_name('enddate', $i), + get_string('end', 'customcertelement_daterange') + ); + + $datarange[] = $mform->createElement( + 'text', + $this->build_element_name('datestring', $i), + get_string('datestring', 'customcertelement_daterange') + ); + + $datarange[] = $mform->createElement( + 'checkbox', + $this->build_element_name('enabled', $i), + get_string('enable') + ); + + $mform->addElement( + 'group', + $this->build_element_name('group', $i), + get_string('daterange', 'customcertelement_daterange', $i + 1), + $datarange, '', false); + + $mform->disabledIf($this->build_element_name('group', $i), $this->build_element_name('enabled', $i), 'notchecked'); + $mform->setType($this->build_element_name('datestring', $i), PARAM_NOTAGS); + } + } + + /** + * A helper function to build consistent form element name. + * + * @param string $name + * @param string $num + * + * @return string + */ + protected function build_element_name($name, $num) { + return $name . $num; + } + + /** + * Get decoded data stored in DB. + * + * @return \stdClass + */ + protected function get_decoded_data() { + return json_decode($this->get_data()); + } + + /** + * Sets the data on the form when editing an element. + * + * @param \mod_customcert\edit_element_form $mform the edit_form instance + */ + public function definition_after_data($mform) { + if (!empty($this->get_data()) && !$mform->isSubmitted()) { + $element = $mform->getElement('dateitem'); + $element->setValue($this->get_decoded_data()->dateitem); + + $element = $mform->getElement('fallbackstring'); + $element->setValue($this->get_decoded_data()->fallbackstring); + + $element = $mform->getElement('numranges'); + $numranges = $element->getValue(); + if ($numranges < $this->get_decoded_data()->numranges) { + $element->setValue($this->get_decoded_data()->numranges); + } + + foreach ($this->get_decoded_data()->dateranges as $key => $range) { + $groupelement = $mform->getElement($this->build_element_name('group', $key)); + $groupelements = $groupelement->getElements(); + $mform->setDefault($groupelements[0]->getName(), $range->startdate); + $mform->setDefault($groupelements[1]->getName(), $range->enddate); + $mform->setDefault($groupelements[2]->getName(), $range->datestring); + $mform->setDefault($groupelements[3]->getName(), $range->enabled); + } + } + + parent::definition_after_data($mform); + } + + /** + * Performs validation on the element values. + * + * @param array $data the submitted data + * @param array $files the submitted files + * @return array the validation errors + */ + public function validate_form_elements($data, $files) { + $errors = parent::validate_form_elements($data, $files); + + // Check if at least one range is set. + $error = get_string('error:enabled', 'customcertelement_daterange'); + for ($i = 0; $i < $data['numranges']; $i++) { + if (!empty($data[$this->build_element_name('enabled', $i)])) { + $error = ''; + } + } + + if (!empty($error)) { + $errors['help'] = $error; + } + + // Check that datestring is set for enabled dataranges. + for ($i = 0; $i < $data['numranges']; $i++) { + $enabled = $this->build_element_name('enabled', $i); + $datestring = $this->build_element_name('datestring', $i); + if (!empty($data[$enabled]) && empty($data[$datestring])) { + $errors[$this->build_element_name('group', $i)] = get_string('error:datestring', 'customcertelement_daterange'); + } + } + + // Check that date is correctly set. + for ($i = 0; $i < $data['numranges']; $i++) { + $enabled = $this->build_element_name('enabled', $i); + $startdate = $this->build_element_name('startdate', $i); + $enddate = $this->build_element_name('enddate', $i); + + if (!empty($data[$enabled]) && $data[$startdate] >= $data[$enddate] ) { + $errors[$this->build_element_name('group', $i)] = get_string('error:enddate', 'customcertelement_daterange'); + } + } + + return $errors; + } + + /** + * This will handle how form data will be saved into the data column in the + * customcert_elements table. + * + * @param \stdClass $data the form data + * @return string the json encoded array + */ + public function save_unique_data($data) { + $arrtostore = array( + 'dateitem' => $data->dateitem, + 'fallbackstring' => $data->fallbackstring, + 'numranges' => 0, + 'dateranges' => [], + ); + + for ($i = 0; $i < $data->numranges; $i++) { + $startdate = $this->build_element_name('startdate', $i); + $enddate = $this->build_element_name('enddate', $i); + $datestring = $this->build_element_name('datestring', $i); + $enabled = $this->build_element_name('enabled', $i); + + if (!empty($data->$datestring)) { + $arrtostore['dateranges'][] = [ + 'startdate' => $data->$startdate, + 'enddate' => $data->$enddate, + 'datestring' => $data->$datestring, + 'enabled' => !empty($data->$enabled), + ]; + $arrtostore['numranges']++; + } + } + + // Encode these variables before saving into the DB. + return json_encode($arrtostore); + } + + /** + * Handles rendering the element on the pdf. + * + * @param \pdf $pdf the pdf object + * @param bool $preview true if it is a preview, false otherwise + * @param \stdClass $user the user we are rendering this for + */ + public function render($pdf, $preview, $user) { + global $DB; + + // If there is no element data, we have nothing to display. + if (empty($this->get_data())) { + return; + } + + $courseid = element_helper::get_courseid($this->id); + $dateitem = $this->get_decoded_data()->dateitem; + + // If we are previewing this certificate then just show a demonstration date. + if ($preview) { + $date = time(); + } else { + // Get the page. + $page = $DB->get_record('customcert_pages', array('id' => $this->get_pageid()), '*', MUST_EXIST); + // Get the customcert this page belongs to. + $customcert = $DB->get_record('customcert', array('templateid' => $page->templateid), '*', MUST_EXIST); + // Now we can get the issue for this user. + $issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id), + '*', MUST_EXIST); + + switch ($dateitem) { + case self::DATE_ISSUE: + $date = $issue->timecreated; + break; + + case self::DATE_COMPLETION: + // Get the last completion date. + $sql = "SELECT MAX(c.timecompleted) as timecompleted + FROM {course_completions} c + WHERE c.userid = :userid + AND c.course = :courseid"; + if ($timecompleted = $DB->get_record_sql($sql, array('userid' => $issue->userid, 'courseid' => $courseid))) { + if (!empty($timecompleted->timecompleted)) { + $date = $timecompleted->timecompleted; + } + } + break; + + case self::DATE_COURSE_START: + $date = $DB->get_field('course', 'startdate', array('id' => $courseid)); + break; + + case self::DATE_COURSE_END: + $date = $DB->get_field('course', 'enddate', array('id' => $courseid)); + break; + + case self::DATE_COURSE_GRADE: + $grade = element_helper::get_course_grade_info( + $courseid, + GRADE_DISPLAY_TYPE_DEFAULT, $user->id + ); + if ($grade && !empty($grade->get_dategraded())) { + $date = $grade->get_dategraded(); + } + break; + + default: + if (strpos($dateitem, 'gradeitem:') === 0) { + $gradeitemid = substr($dateitem, 10); + $grade = element_helper::get_grade_item_info( + $gradeitemid, + $dateitem, + $user->id + ); + } else { + $grade = element_helper::get_mod_grade_info( + $dateitem, + GRADE_DISPLAY_TYPE_DEFAULT, + $user->id + ); + } + if ($grade && !empty($grade->get_dategraded())) { + $date = $grade->get_dategraded(); + } + break; + } + } + + // Ensure that a date has been set. + if (!empty($date)) { + element_helper::render_content($pdf, $this, $this->get_daterange_string($date)); + } + } + + /** + * Get daterange string. + * + * @param int $date Unix stamp date. + * + * @return string + */ + protected function get_daterange_string($date) { + $outputstring = ''; + + foreach ($this->get_decoded_data()->dateranges as $key => $range) { + if ($date >= $range->startdate && $date <= $range->enddate) { + $outputstring = $range->datestring; + } + } + + if (!empty($this->get_decoded_data()->fallbackstring)) { + $outputstring = $this->get_decoded_data()->fallbackstring; + } + + return $outputstring; + } + + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + * + * @return string the html + */ + public function render_html() { + // If there is no element data, we have nothing to display. + if (empty($this->get_data())) { + return; + } + + return element_helper::render_html_content($this, get_string('preview', 'customcertelement_daterange')); + } + + /** + * This function is responsible for handling the restoration process of the element. + * + * We will want to update the course module the date element is pointing to as it will + * have changed in the course restore. + * + * @param \restore_customcert_activity_task $restore + */ + public function after_restore($restore) { + global $DB; + + $data = $this->get_decoded_data(); + if ($newitem = \restore_dbops::get_backup_ids_record($restore->get_restoreid(), 'course_module', $data->dateitem)) { + $data->dateitem = $newitem->newitemid; + $DB->set_field('customcert_elements', 'data', $this->save_unique_data($data), array('id' => $this->get_id())); + } + } + +} diff --git a/element/daterange/classes/privacy/provider.php b/element/daterange/classes/privacy/provider.php new file mode 100644 index 0000000..482f40f --- /dev/null +++ b/element/daterange/classes/privacy/provider.php @@ -0,0 +1,46 @@ +. + +/** + * Privacy Subsystem implementation for customcertelement_daterange. + * + * @package customcertelement_daterange + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace customcertelement_daterange\privacy; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Privacy Subsystem for customcertelement_daterange implementing null_provider. + * + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements \core_privacy\local\metadata\null_provider { + + /** + * Get the language string identifier with the component's language + * file to explain why this plugin stores no data. + * + * @return string + */ + public static function get_reason() : string { + return 'privacy:metadata'; + } +} diff --git a/element/daterange/lang/en/customcertelement_daterange.php b/element/daterange/lang/en/customcertelement_daterange.php new file mode 100644 index 0000000..ee5f222 --- /dev/null +++ b/element/daterange/lang/en/customcertelement_daterange.php @@ -0,0 +1,49 @@ +. + +/** + * Strings for component 'customcertelement_daterange', language 'en'. + * + * @package customcertelement_daterange + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +$string['completiondate'] = 'Completion date'; +$string['courseenddate'] = 'Course end date'; +$string['coursegradedate'] = 'Course grade date'; +$string['coursestartdate'] = 'Course start date'; +$string['dateitem'] = 'Date item'; +$string['dateitem_help'] = 'This will be the date that is printed on the certificate'; +$string['dateranges'] = 'Dateranges'; +$string['fallbackstring'] = 'Fallback string'; +$string['fallbackstring_help'] = 'This string will be displayed if no daterange applied to a date. If Fallback string is not set, then there will be no output at all.'; +$string['help'] = 'Configure a string representation for each daterange. Set start and end dates as well as a string you would like to transform each range to. Make sure your ranges do not overlap, otherwise the first detected daterange will be applied. If no daterange applied to a date, then Fallback string will be displayed. If Fallback string is not set, then there will be no output at all.'; +$string['issueddate'] = 'Issued date'; +$string['maxranges'] = 'Maximum number ranges'; +$string['maxranges_desc'] = 'Set a maximum number of date ranges per each element'; +$string['pluginname'] = 'Date range'; +$string['privacy:metadata'] = 'The Date range plugin does not store any personal data.'; +$string['start'] = 'Start'; +$string['end'] = 'End'; +$string['datestring'] = 'String representation'; +$string['daterange'] = 'Daterange {$a}'; +$string['error:enabled'] = 'You must have at least one datarange enabled'; +$string['error:datestring'] = 'You must provide string representation for the enabled datarange'; +$string['error:enddate'] = 'End date must be after Start date'; +$string['preview'] = 'Preview daterange string'; diff --git a/element/daterange/settings.php b/element/daterange/settings.php new file mode 100644 index 0000000..d369a03 --- /dev/null +++ b/element/daterange/settings.php @@ -0,0 +1,40 @@ +. + +/** + * Admin settings. + * + * @package customcertelement_daterange + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +$options = []; + +foreach (range(1, 365) as $number) { + $options[$number] = $number; +} + +$settings->add(new admin_setting_configselect( + 'customcertelement_daterange/maxranges', + get_string('maxranges', 'customcertelement_daterange'), + get_string('maxranges_desc', 'customcertelement_daterange'), + 1, + $options +)); + diff --git a/element/daterange/version.php b/element/daterange/version.php new file mode 100644 index 0000000..db9217d --- /dev/null +++ b/element/daterange/version.php @@ -0,0 +1,29 @@ +. + +/** + * This file contains the version information for the date plugin. + * + * @package customcertelement_daterange + * @copyright 2018 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +$plugin->version = 2018071700; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2018051700; // Requires this Moodle version (3.4). +$plugin->component = 'customcertelement_daterange';