Compare commits

...

40 commits

Author SHA1 Message Date
Mark Nelson 68b53cf67b Update CHANGES.md 2021-10-27 16:48:05 +08:00
Mark Nelson a6a7b24898 Respect multiple languages in manage template page title (#467) 2021-10-27 16:45:49 +08:00
Mark Nelson fae09fbadf Changes to make GHA happy and added usages of the coalescing operator (#121) 2021-10-27 13:20:11 +08:00
Mark Nelson 62d1bbdedc Add issue number to CHANGES.md 2021-10-27 12:25:52 +08:00
Mark Nelson f3050a3ebf Updated CHANGES.md 2021-10-25 17:29:03 +08:00
Jesús Alonso Abad 904a83e2b9 Added alignment to activity backup/cloning (#121) 2021-10-25 17:28:31 +08:00
Jesús Alonso Abad 29779a32f2 Added elements alignment support (#121) 2021-10-25 17:27:52 +08:00
Mark Nelson c3942c3b75 Do not encode html entities in emails (#457) 2021-09-28 22:24:52 +08:00
Mark Nelson 56bdfdea6a Update CHANGES.md 2021-08-06 10:57:43 +08:00
Andrew Madden 32b255cb0d Closes #449 The user id should be mapped to the equivalent user id in new sites during activity restore
As the userid in the customcert_issues table were using the userid from the site where the activity was backed up, emails were being sent to those that had already received them. Theoretically there wwere also users who should have received an email who didn't get one.
2021-08-06 10:48:59 +08:00
Mark Nelson 471b5cce73 Update CHANGES.md 2021-08-04 11:48:59 +08:00
Mark Nelson c0c6bd1d47 Minor code changes (#415) 2021-08-04 11:47:02 +08:00
Sameer Ahmed d6d10d21ac Display the course short name (#415)
- Added a new select box to choose from course name or short description to display.
2021-08-04 11:46:58 +08:00
Mark Nelson 227b162721 GHA: ROW_FORMAT=COMPRESSED deprecated in MariaDB 10.6
See MDL-72131.
2021-08-03 18:05:20 +08:00
Mark Nelson b20eafe22f Add new lines at end of files 2021-08-03 17:52:47 +08:00
Mark Nelson af28f5bad1 Remove unnecessary new line 2021-08-03 16:53:58 +08:00
Mark Nelson a07c81a409 Minor changes and CHANGES.md note (#433) 2021-08-03 16:35:56 +08:00
Michael Milette 3024bf1516 Fix for multi-language issues (#433).
- Filename of PDF when viewing/previewing PDF.
- Page title tag when viewing/previewing PDF.
- List of available templates.
- Template Load dropdown list.
2021-08-03 16:34:12 +08:00
Dani Palou e99d25a18b Adapt mobile app code to Ionic 5 (#431) 2021-07-06 17:05:15 +08:00
Mark Nelson 8effac9d93 Bump version 2021-06-13 14:08:48 +08:00
Mark Nelson c3a9172d8d Use 'cron_setup_user' when sending emails (#414) 2021-06-13 14:06:30 +08:00
Mark Nelson 8a80ab82d8 Set proper context when sending emails (#402) 2021-06-13 13:38:41 +08:00
hieuvu 135630487c Add actions title to elements table (#425) 2021-06-08 17:48:31 +08:00
Mark Nelson 39e2bdd95d Removed .travis.yml file 2021-05-27 19:21:57 +08:00
Mark Nelson 0f66037b8f Fix Moodle Code Checker complaints 2021-05-27 18:53:26 +08:00
Mark Nelson 810c4b2fcf Update CHANGES.md 2021-04-16 14:41:30 +08:00
Mikhail Golenkov 29deb01aaf Fix the issue with displaying PDF when debugging is ON (#420) 2021-04-16 14:37:20 +08:00
Mark Nelson 52be3400f7 Add type hinting to get_course_time() 2021-04-05 20:11:45 +08:00
Mark Nelson 54be90f5c4 Use instance of moodle_url() in the function notice() 2021-04-05 20:09:36 +08:00
Mark Nelson 00e9eaba4d Fix get_course_time() allowing users to view certificate early (#403) 2021-04-05 20:02:08 +08:00
Mark Nelson 4881963650 Updated CHANGES.md 2021-04-05 17:01:42 +08:00
Mark Nelson 8e2e1a2336 Add ability to choose how to deliver the certificate (#401) 2021-04-05 17:00:04 +08:00
Mark Nelson 05b523eaa6 Updated CHANGES.md 2021-04-03 11:28:17 +08:00
Mark Nelson 7df31f16fb Add ability to show description on course page (#406) 2021-04-03 10:37:34 +08:00
Mark Nelson 89516bf17d Set for version 3.9 (#407) 2021-04-03 10:14:00 +08:00
Marina Glancy dfea4af2b6 Add github actions (#407) 2021-04-03 10:13:03 +08:00
Toni Förster 4c8564a9b4 Allow managers to download certificates (#412)
Currently, it is not possible for editing teachers
and managers to download certificates for users because
we only check for $canreceive.
2021-04-02 20:53:28 +08:00
Mark Nelson 698b4563d1 Fix PHPDocs for the method get_course_field_value() 2020-11-27 20:07:38 +08:00
Mark Nelson 26e72d770c Update .travis.yml 2020-11-27 20:07:34 +08:00
Mark Nelson 63160279f5 Add missing full-stop to comment 2020-11-27 16:04:55 +08:00
40 changed files with 652 additions and 144 deletions

120
.github/workflows/moodle-ci.yml vendored Normal file
View file

@ -0,0 +1,120 @@
name: Moodle Plugin CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:9.6
env:
POSTGRES_USER: 'postgres'
POSTGRES_HOST_AUTH_METHOD: 'trust'
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
mariadb:
image: mariadb:10.5
env:
MYSQL_USER: 'root'
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3
strategy:
fail-fast: false
matrix:
include:
- php: '7.4'
moodle-branch: 'MOODLE_39_STABLE'
database: pgsql
- php: '7.4'
moodle-branch: 'MOODLE_39_STABLE'
database: mariadb
- php: '7.2'
moodle-branch: 'MOODLE_39_STABLE'
database: pgsql
- php: '7.2'
moodle-branch: 'MOODLE_39_STABLE'
database: mariadb
steps:
- name: Check out repository code
uses: actions/checkout@v2
with:
path: plugin
- name: Setup PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- name: Initialise moodle-plugin-ci
run: |
composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
echo $(cd ci/bin; pwd) >> $GITHUB_PATH
echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
sudo locale-gen en_AU.UTF-8
echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV
- name: Install moodle-plugin-ci
run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1
env:
DB: ${{ matrix.database }}
MOODLE_BRANCH: ${{ matrix.moodle-branch }}
MUSTACHE_IGNORE_NAMES: 'mobile_*.mustache'
- name: PHP Lint
if: ${{ always() }}
run: moodle-plugin-ci phplint
- name: PHP Copy/Paste Detector
continue-on-error: true # This step will show errors but will not fail
if: ${{ always() }}
run: moodle-plugin-ci phpcpd
- name: PHP Mess Detector
continue-on-error: true # This step will show errors but will not fail
if: ${{ always() }}
run: moodle-plugin-ci phpmd
- name: Moodle Code Checker
if: ${{ always() }}
run: moodle-plugin-ci codechecker --max-warnings 0
- name: Moodle PHPDoc Checker
if: ${{ always() }}
run: moodle-plugin-ci phpdoc
- name: Validating
if: ${{ always() }}
run: moodle-plugin-ci validate
- name: Check upgrade savepoints
if: ${{ always() }}
run: moodle-plugin-ci savepoints
- name: Mustache Lint
if: ${{ always() }}
run: moodle-plugin-ci mustache
- name: Grunt
if: ${{ always() }}
run: moodle-plugin-ci grunt --max-lint-warnings 0
- name: PHPUnit tests
if: ${{ always() }}
run: |
moodle-plugin-ci phpunit
cd moodle
vendor/bin/phpunit --fail-on-risky --disallow-test-output -v admin/tool/dataprivacy/tests/metadata_registry_test.php
vendor/bin/phpunit --fail-on-risky --disallow-test-output -v lib/tests/externallib_test.php
vendor/bin/phpunit --fail-on-risky --disallow-test-output -v privacy/tests/provider_test.php
- name: Behat features
if: ${{ always() }}
run: moodle-plugin-ci behat --profile chrome

View file

@ -1,52 +0,0 @@
language: php
# For javascript behat tests we need sudo
sudo: true
dist: trusty
cache:
directories:
- $HOME/.composer/cache
- $HOME/.npm
php:
- 7.2
- 7.4
addons:
firefox: 47.0.1
postgresql: 9.5
apt:
packages:
- openjdk-8-jre-headless
env:
global:
- MOODLE_BRANCH=MOODLE_39_STABLE
- IGNORE_NAMES=mobile_*.mustache # Mobile mustache has specific syntax, ignore their templates
matrix:
- DB=pgsql
- DB=mysqli
before_install:
- phpenv config-rm xdebug.ini
- nvm install 14.0
- nvm use 14.0
- cd ../..
- composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
install:
- moodle-plugin-ci install
script:
- moodle-plugin-ci phplint
# - moodle-plugin-ci phpcpd # subplugins often have similar code and cause "duplicated code" errors
# - moodle-plugin-ci phpmd # too much noise from this check, maybe, some day...
- moodle-plugin-ci codechecker
- moodle-plugin-ci validate
- moodle-plugin-ci savepoints
- moodle-plugin-ci mustache
- moodle-plugin-ci grunt -t stylelint:css -t js
- moodle-plugin-ci phpunit
- moodle-plugin-ci behat

View file

@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
Note - All hash comments refer to the issue number. Eg. #169 refers to https://github.com/mdjnelson/moodle-mod_customcert/issues/169.
## [3.9.2] - 2021-??-??
### Fixed
- Fix places not using the multi-language filter (#433).
- Fix user IDs in the issue table not being mapped during restore (#449).
- Fix emails displaying HTML entities encoded (#457).
- Respect multiple languages in manage template page title (#467).
### Added
- You can now choose the course short or full name to display (#415).
- You can now select the alignment for all text elements (#121).
## [3.9.1] - 2021-06-13
### Added
- Usage of github actions (#407).
- The ability to show the description on the course page (#406).
- The ability to choose how to deliver the certificate (#401).
### Fixed
- Managers are now able to download their students' certificates (#412).
- Users being able to view the certificate before the required time set (#403).
- Fixed the issue with displaying PDF when debugging is ON (#420).
- Using incorrect context when sending emails (#402).
- Use `cron_setup_user` when sending emails (#414).
## [3.8.5] - 2020-11-26
### Added

View file

@ -54,4 +54,4 @@ foreach ($values as $value) {
$element->posx = $value->posx;
$element->posy = $value->posy;
$DB->update_record('customcert_elements', $element);
}
}

View file

@ -56,7 +56,7 @@ class backup_customcert_activity_task extends backup_activity_task {
* @param string $content
* @return mixed|string
*/
static public function encode_content_links($content) {
public static function encode_content_links($content) {
global $CFG;
$base = preg_quote($CFG->wwwroot, "/");

View file

@ -59,7 +59,7 @@ class backup_customcert_activity_structure_step extends backup_activity_structur
$element = new backup_nested_element('element', array('id'), array(
'pageid', 'name', 'element', 'data', 'font', 'fontsize',
'colour', 'posx', 'posy', 'width', 'refpoint', 'sequence',
'timecreated', 'timemodified'));
'alignment', 'timecreated', 'timemodified'));
// The issues.
$issues = new backup_nested_element('issues');

View file

@ -53,7 +53,7 @@ class restore_customcert_activity_task extends restore_activity_task {
/**
* Define the contents in the activity that must be processed by the link decoder.
*/
static public function define_decode_contents() {
public static function define_decode_contents() {
$contents = array();
$contents[] = new restore_decode_content('customcert', array('intro'), 'customcert');
@ -64,7 +64,7 @@ class restore_customcert_activity_task extends restore_activity_task {
/**
* Define the decoding rules for links belonging to the activity to be executed by the link decoder.
*/
static public function define_decode_rules() {
public static function define_decode_rules() {
$rules = array();
$rules[] = new restore_decode_rule('CUSTOMCERTVIEWBYID', '/mod/customcert/view.php?id=$1', 'course_module');
@ -75,12 +75,12 @@ class restore_customcert_activity_task extends restore_activity_task {
}
/**
* Define the restore log rules that will be applied by the {@link restore_logs_processor} when restoring
* customcert logs. It must return one array of {@link restore_log_rule} objects.
* Define the restore log rules that will be applied by the {@see restore_logs_processor} when restoring
* customcert logs. It must return one array of {@see restore_log_rule} objects.
*
* @return array the restore log rules
*/
static public function define_restore_log_rules() {
public static function define_restore_log_rules() {
$rules = array();
$rules[] = new restore_log_rule('customcert', 'add', 'view.php?id={course_module}', '{customcert}');

View file

@ -159,6 +159,7 @@ class restore_customcert_activity_structure_step extends restore_activity_struct
$data->customcertid = $this->get_new_parentid('customcert');
$data->timecreated = $this->apply_date_offset($data->timecreated);
$data->userid = $this->get_mappingid('user', $data->userid);
$newitemid = $DB->insert_record('customcert_issues', $data);
$this->set_mapping('customcert_issue', $oldid, $newitemid);

View file

@ -80,4 +80,4 @@ class admin_setting_link extends \admin_setting_configtext {
return format_admin_setting($this, $this->visiblename,
\html_writer::link($this->link, $this->linkname), $this->description, true, '', null, $query);
}
}
}

View file

@ -37,6 +37,16 @@ defined('MOODLE_INTERNAL') || die();
*/
class certificate {
/**
* Send the file inline to the browser.
*/
const DELIVERY_OPTION_INLINE = 'I';
/**
* Send to the browser and force a file download
*/
const DELIVERY_OPTION_DOWNLOAD = 'D';
/**
* @var string the print protection variable
*/
@ -165,7 +175,7 @@ class certificate {
* @param int $userid
* @return int the total time spent in seconds
*/
public static function get_course_time($courseid, $userid = 0) {
public static function get_course_time(int $courseid, int $userid = 0): int {
global $CFG, $DB, $USER;
if (empty($userid)) {
@ -217,7 +227,7 @@ class certificate {
$totaltime = 0;
}
$delay = $log->$timefield - $lasthit;
if ($delay > ($CFG->sessiontimeout * 60)) {
if ($delay > $CFG->sessiontimeout) {
// The difference between the last log and the current log is more than
// the timeout Register session value so that we have found a session!
$login = $log->$timefield;

View file

@ -247,7 +247,7 @@ class edit_form extends \moodleform {
// Create a table to display these elements.
$table = new \html_table();
$table->attributes = array('class' => 'generaltable elementstable');
$table->head = array(get_string('name', 'customcert'), get_string('type', 'customcert'), '');
$table->head = array(get_string('name', 'customcert'), get_string('type', 'customcert'), get_string('actions'));
$table->align = array('left', 'left', 'left');
// Loop through and add the elements to the table.
foreach ($elements as $element) {

View file

@ -37,6 +37,21 @@ defined('MOODLE_INTERNAL') || die();
*/
abstract class element {
/**
* @var string The left alignment constant.
*/
const ALIGN_LEFT = 'L';
/**
* @var string The centered alignment constant.
*/
const ALIGN_CENTER = 'C';
/**
* @var string The right alignment constant.
*/
const ALIGN_RIGHT = 'R';
/**
* @var \stdClass $element The data for the element we are adding - do not use, kept for legacy reasons.
*/
@ -97,6 +112,11 @@ abstract class element {
*/
protected $refpoint;
/**
* @var string The alignment.
*/
protected $alignment;
/**
* @var bool $showposxy Show position XY form elements?
*/
@ -130,6 +150,7 @@ abstract class element {
$this->width = $element->width;
$this->refpoint = $element->refpoint;
$this->showposxy = isset($showposxy) && $showposxy;
$this->set_alignment($element->alignment ?? self::ALIGN_LEFT);
}
/**
@ -231,6 +252,31 @@ abstract class element {
return $this->refpoint;
}
/**
* Returns the alignment.
*
* @return string The current alignment value.
*/
public function get_alignment() {
return $this->alignment ?? self::ALIGN_LEFT;
}
/**
* Sets the alignment.
*
* @param string $alignment The new alignment.
*
* @throws \InvalidArgumentException if the provided new alignment is not valid.
*/
protected function set_alignment(string $alignment) {
$validvalues = array(self::ALIGN_LEFT, self::ALIGN_CENTER, self::ALIGN_RIGHT);
if (!in_array($alignment, $validvalues)) {
throw new \InvalidArgumentException("'$alignment' is not a valid alignment value. It has to be one of " .
implode(', ', $validvalues));
}
$this->alignment = $alignment;
}
/**
* This function renders the form elements when adding a customcert element.
* Can be overridden if more functionality is needed.
@ -246,6 +292,7 @@ abstract class element {
}
element_helper::render_form_element_width($mform);
element_helper::render_form_element_refpoint($mform);
element_helper::render_form_element_alignment($mform);
}
/**
@ -265,7 +312,8 @@ abstract class element {
'posx' => $this->posx,
'posy' => $this->posy,
'width' => $this->width,
'refpoint' => $this->refpoint
'refpoint' => $this->refpoint,
'alignment' => $this->get_alignment()
];
foreach ($properties as $property => $value) {
if (!is_null($value) && $mform->elementExists($property)) {
@ -311,15 +359,16 @@ abstract class element {
$element = new \stdClass();
$element->name = $data->name;
$element->data = $this->save_unique_data($data);
$element->font = (isset($data->font)) ? $data->font : null;
$element->fontsize = (isset($data->fontsize)) ? $data->fontsize : null;
$element->colour = (isset($data->colour)) ? $data->colour : null;
$element->font = $data->font ?? null;
$element->fontsize = $data->fontsize ?? null;
$element->colour = $data->colour ?? null;
if ($this->showposxy) {
$element->posx = (isset($data->posx)) ? $data->posx : null;
$element->posy = (isset($data->posy)) ? $data->posy : null;
$element->posx = $data->posx ?? null;
$element->posy = $data->posy ?? null;
}
$element->width = (isset($data->width)) ? $data->width : null;
$element->refpoint = (isset($data->refpoint)) ? $data->refpoint : null;
$element->width = $data->width ?? null;
$element->refpoint = $data->refpoint ?? null;
$element->alignment = $data->alignment ?? self::ALIGN_LEFT;
$element->timemodified = time();
// Check if we are updating, or inserting a new element.
@ -377,7 +426,7 @@ abstract class element {
* @param bool $preview true if it is a preview, false otherwise
* @param \stdClass $user the user we are rendering this for
*/
public abstract function render($pdf, $preview, $user);
abstract public function render($pdf, $preview, $user);
/**
* Render the element in html.
@ -389,7 +438,7 @@ abstract class element {
*
* @return string the html
*/
public abstract function render_html();
abstract public function render_html();
/**
* Handles deleting any data this element may have introduced.

View file

@ -59,6 +59,7 @@ class element_factory {
$data->posy = $element->posy ?? null;
$data->width = $element->width ?? null;
$data->refpoint = $element->refpoint ?? null;
$data->alignment = $element->alignment ?? null;
// Ensure the necessary class exists.
if (class_exists($classname)) {

View file

@ -74,6 +74,7 @@ class element_helper {
$w = $element->get_width();
$refpoint = $element->get_refpoint();
$actualwidth = $pdf->GetStringWidth($content);
$alignment = $element->get_alignment();
if ($w and $w < $actualwidth) {
$actualwidth = $w;
@ -104,7 +105,7 @@ class element_helper {
$w += 0.0001;
}
$pdf->setCellPaddings(0, 0, 0, 0);
$pdf->writeHTMLCell($w, 0, $x, $y, $content, 0, 0, false, true);
$pdf->writeHTMLCell($w, 0, $x, $y, $content, 0, 0, false, true, $alignment);
}
/**
@ -205,6 +206,23 @@ class element_helper {
$mform->addHelpButton('refpoint', 'refpoint', 'customcert');
}
/**
* Helper function to render the alignment form element.
*
* @param \MoodleQuickForm $mform the edit_form instance.
*/
public static function render_form_element_alignment($mform) {
$alignmentoptions = array();
$alignmentoptions[element::ALIGN_LEFT] = get_string('alignleft', 'customcert');
$alignmentoptions[element::ALIGN_CENTER] = get_string('aligncenter', 'customcert');
$alignmentoptions[element::ALIGN_RIGHT] = get_string('alignright', 'customcert');
$mform->addElement('select', 'alignment', get_string('alignment', 'customcert'), $alignmentoptions);
$mform->setType('alignment', PARAM_ALPHA);
$mform->setDefault('alignment', element::ALIGN_LEFT);
$mform->addHelpButton('alignment', 'alignment', 'customcert');
}
/**
* Helper function to performs validation on the colour element.
*

View file

@ -43,10 +43,20 @@ class course_module_viewed extends \core\event\course_module_viewed {
parent::init();
}
/**
* {@inheritdoc}
*
* @return string[]
*/
public static function get_objectid_mapping() {
return array('db' => 'customcert', 'restore' => 'customcert');
}
/**
* {@inheritdoc}
*
* @return bool
*/
public static function get_other_mapping() {
// No need to map.
return false;

View file

@ -47,19 +47,23 @@ class load_template_form extends \moodleform {
// Get the context.
$context = $this->_customdata['context'];
$syscontext = \context_system::instance();
$mform->addElement('header', 'loadtemplateheader', get_string('loadtemplate', 'customcert'));
// Display a link to the manage templates page.
if ($context->contextlevel != CONTEXT_SYSTEM && has_capability('mod/customcert:manage', \context_system::instance())) {
if ($context->contextlevel != CONTEXT_SYSTEM && has_capability('mod/customcert:manage', $syscontext)) {
$link = \html_writer::link(new \moodle_url('/mod/customcert/manage_templates.php'),
get_string('managetemplates', 'customcert'));
$mform->addElement('static', 'managetemplates', '', $link);
}
$templates = $DB->get_records_menu('customcert_templates',
array('contextid' => \context_system::instance()->id), 'name ASC', 'id, name');
if ($templates) {
$arrtemplates = $DB->get_records_menu('customcert_templates', ['contextid' => $syscontext->id], 'name ASC', 'id, name');
if ($arrtemplates) {
$templates = [];
foreach ($arrtemplates as $key => $template) {
$templates[$key] = format_string($template, true, ['context' => $context]);
}
$group = array();
$group[] = $mform->createElement('select', 'ltid', '', $templates);
$group[] = $mform->createElement('submit', 'loadtemplatesubmit', get_string('load', 'customcert'));

View file

@ -77,7 +77,7 @@ class manage_templates_table extends \table_sql {
* @return string
*/
public function col_name($template) {
return $template->name;
return format_string($template->name, true, ['context' => $this->context]);
}
/**

View file

@ -44,9 +44,10 @@ class mobile {
global $OUTPUT, $DB, $USER;
$args = (object) $args;
$versionname = $args->appversioncode >= 3950 ? 'latest' : 'ionic3';
$cmid = $args->cmid;
$groupid = empty($args->group) ? 0 : $args->group; // By default, group 0.
$groupid = empty($args->group) ? 0 : (int) $args->group; // By default, group 0.
// Capabilities check.
$cm = get_coursemodule_from_id('customcert', $cmid);
@ -114,6 +115,7 @@ class mobile {
'showreport' => $showreport,
'hasrecipients' => !empty($recipients),
'recipients' => array_values($recipients),
'numrecipients' => count($recipients),
'currenttimestamp' => time()
];
@ -121,11 +123,13 @@ class mobile {
'templates' => [
[
'id' => 'main',
'html' => $OUTPUT->render_from_template('mod_customcert/mobile_view_activity_page', $data),
'html' => $OUTPUT->render_from_template("mod_customcert/mobile_view_activity_page_$versionname", $data),
],
],
'javascript' => '',
'otherdata' => ''
'otherdata' => [
'group' => $groupid,
]
];
}

View file

@ -84,6 +84,9 @@ class email_certificate_task extends \core\task\scheduled_task {
// Get the context.
$context = \context::instance_by_id($customcert->contextid);
// Set the $PAGE context - this ensure settings, such as language, are kept and don't default to the site settings.
$PAGE->set_context($context);
// Get the person we are going to send this email on behalf of.
$userfrom = \core_user::get_noreply_user();
@ -176,6 +179,9 @@ class email_certificate_task extends \core\task\scheduled_task {
// Now, email the people we need to.
foreach ($issuedusers as $user) {
// Set up the user.
cron_setup_user($user);
$userfullname = fullname($user);
$info->userfullname = $userfullname;
@ -205,7 +211,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$subject = get_string('emailstudentsubject', 'customcert', $info);
$message = $textrenderer->render($renderable);
$messagehtml = $htmlrenderer->render($renderable);
email_to_user($user, fullname($userfrom), $subject, $message, $messagehtml, $tempfile, $filename);
email_to_user($user, fullname($userfrom), html_entity_decode($subject), $message, $messagehtml,
$tempfile, $filename);
}
if ($customcert->emailteachers) {
@ -216,8 +223,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$message = $textrenderer->render($renderable);
$messagehtml = $htmlrenderer->render($renderable);
foreach ($teachers as $teacher) {
email_to_user($teacher, fullname($userfrom), $subject, $message, $messagehtml, $tempfile,
$filename);
email_to_user($teacher, fullname($userfrom), html_entity_decode($subject), $message, $messagehtml,
$tempfile, $filename);
}
}
@ -236,8 +243,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$emailuser = new \stdClass();
$emailuser->id = -1;
$emailuser->email = $email;
email_to_user($emailuser, fullname($userfrom), $subject, $message, $messagehtml, $tempfile,
$filename);
email_to_user($emailuser, fullname($userfrom), html_entity_decode($subject), $message,
$messagehtml, $tempfile, $filename);
}
}
}

View file

@ -253,7 +253,7 @@ class template {
* @param bool $return Do we want to return the contents of the PDF?
* @return string|void Can return the PDF in string format if specified.
*/
public function generate_pdf($preview = false, $userid = null, $return = false) {
public function generate_pdf(bool $preview = false, int $userid = null, bool $return = false) {
global $CFG, $DB, $USER;
if (empty($userid)) {
@ -269,20 +269,27 @@ class template {
// Create the pdf object.
$pdf = new \pdf();
$customcert = $DB->get_record('customcert', ['templateid' => $this->id]);
// If the template belongs to a certificate then we need to check what permissions we set for it.
if ($protection = $DB->get_field('customcert', 'protection', array('templateid' => $this->id))) {
if (!empty($protection)) {
$protection = explode(', ', $protection);
$pdf->SetProtection($protection);
}
if (!empty($customcert->protection)) {
$protection = explode(', ', $customcert->protection);
$pdf->SetProtection($protection);
}
if (empty($customcert->deliveryoption)) {
$deliveryoption = certificate::DELIVERY_OPTION_INLINE;
} else {
$deliveryoption = $customcert->deliveryoption;
}
// Remove full-stop at the end, if it exists, to avoid "..pdf" being created and being filtered by clean_filename.
$filename = rtrim(format_string($this->name, true, ['context' => $this->get_context()]), '.');
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->SetTitle($this->name);
$pdf->SetTitle($filename);
$pdf->SetAutoPageBreak(true, 0);
// Remove full-stop at the end, if it exists, to avoid "..pdf" being created and being filtered by clean_filename.
$filename = rtrim($this->name, '.');
// This is the logic the TCPDF library uses when processing the name. This makes names
// such as 'الشهادة' become empty, so set a default name in these cases.
@ -320,7 +327,7 @@ class template {
return $pdf->Output('', 'S');
}
$pdf->Output($filename, 'I');
$pdf->Output($filename, $deliveryoption);
}
}

View file

@ -14,6 +14,7 @@
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="requiredtime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="verifyany" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="deliveryoption" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="emailstudents" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="emailteachers" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="emailothers" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
@ -84,6 +85,7 @@
<FIELD NAME="posy" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="width" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="refpoint" TYPE="int" LENGTH="4" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="alignment" TYPE="char" LENGTH="1" NOTNULL="true" DEFAULT="L" SEQUENCE="false"/>
<FIELD NAME="sequence" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>

View file

@ -166,5 +166,26 @@ function xmldb_customcert_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2019111803, 'customcert');
}
if ($oldversion < 2020061501) {
$table = new xmldb_table('customcert');
$field = new xmldb_field('deliveryoption', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'verifyany');
// Conditionally launch add field.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2020061501, 'customcert');
}
if ($oldversion < 2020061503) {
$table = new xmldb_table('customcert_elements');
$field = new xmldb_field('alignment', XMLDB_TYPE_CHAR, '1', null, XMLDB_NOTNULL, null, 'L', 'refpoint');
$dbman->add_field($table, $field);
upgrade_mod_savepoint(true, 2020061503, 'customcert'); // Replace with the actual version number.
}
return true;
}

View file

@ -123,7 +123,7 @@ class element extends \mod_customcert\element {
/**
* Helper function that returns the field value in a human-readable format.
*
* @param \stdClass $user the user we are rendering this for
* @param \stdClass $course the course we are rendering this for
* @param bool $preview Is this a preview?
* @return string
*/

View file

@ -35,6 +35,42 @@ defined('MOODLE_INTERNAL') || die();
*/
class element extends \mod_customcert\element {
/**
* The course short name.
*/
const COURSE_SHORT_NAME = 1;
/**
* The course fullname.
*/
const COURSE_FULL_NAME = 2;
/**
* This function renders the form elements when adding a customcert element.
*
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
// The course name display options.
$mform->addElement('select', 'coursenamedisplay', get_string('coursenamedisplay', 'customcertelement_coursename'),
self::get_course_name_display_options());
$mform->setType('coursenamedisplay', PARAM_INT);
$mform->addHelpButton('coursenamedisplay', 'coursenamedisplay', 'customcertelement_coursename');
parent::render_form_elements($mform);
}
/**
* 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 text
*/
public function save_unique_data($data) {
return $data->coursenamedisplay;
}
/**
* Handles rendering the element on the pdf.
*
@ -43,7 +79,7 @@ class element extends \mod_customcert\element {
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_course_name());
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_course_name_detail());
}
/**
@ -55,19 +91,52 @@ class element extends \mod_customcert\element {
* @return string the html
*/
public function render_html() {
return \mod_customcert\element_helper::render_html_content($this, $this->get_course_name());
return \mod_customcert\element_helper::render_html_content($this, $this->get_course_name_detail());
}
/**
* Helper function that returns the category name.
* Sets the data on the form when editing an element.
*
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function definition_after_data($mform) {
if (!empty($this->get_data())) {
$element = $mform->getElement('coursenamedisplay');
$element->setValue($this->get_data());
}
parent::definition_after_data($mform);
}
/**
* Helper function that returns the selected course name detail (i.e. name or short description) for display.
*
* @return string
*/
protected function get_course_name() : string {
protected function get_course_name_detail(): string {
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$course = get_course($courseid);
$context = \mod_customcert\element_helper::get_context($this->get_id());
return format_string($course->fullname, true, ['context' => $context]);
// The name field to display.
$field = $this->get_data();
// The name value to display.
$value = $course->fullname;
if ($field == self::COURSE_SHORT_NAME) {
$value = $course->shortname;
}
return format_string($value, true, ['context' => $context]);
}
/**
* Helper function to return all the possible name display options.
*
* @return array returns an array of name options
*/
public static function get_course_name_display_options(): array {
return [
self::COURSE_FULL_NAME => get_string('coursefullname', 'customcertelement_coursename'),
self::COURSE_SHORT_NAME => get_string('courseshortname', 'customcertelement_coursename')
];
}
}

View file

@ -22,5 +22,10 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['coursenamedisplay'] = 'Type';
$string['coursenamedisplay_help'] = 'Display the course full name or short name?';
$string['coursefullname'] = 'Full name';
$string['courseshortname'] = 'Short name';
$string['pluginname'] = 'Course name';
$string['privacy:metadata'] = 'The Course name plugin does not store any personal data.';

View file

@ -25,6 +25,11 @@
$string['activity'] = 'Activity';
$string['addcertpage'] = 'Add page';
$string['addelement'] = 'Add element';
$string['aligncenter'] = 'Centered';
$string['alignleft'] = 'Left alignment';
$string['alignment'] = 'Alignment';
$string['alignment_help'] = 'This property sets the horizontal alignment of the element. Some elements may not support this, while the behaviour of others may differ.';
$string['alignright'] = 'Right alignment';
$string['awardedto'] = 'Awarded to';
$string['cannotverifyallcertificates'] = 'You do not have the permission to verify all certificates on the site.';
$string['certificate'] = 'Certificate';
@ -57,6 +62,9 @@ $string['deleteissueconfirm'] = 'Are you sure you want to delete this certificat
$string['deleteissuedcertificates'] = 'Delete issued certificates';
$string['deletepageconfirm'] = 'Are you sure you want to delete this certificate page?';
$string['deletetemplateconfirm'] = 'Are you sure you want to delete this certificate template?';
$string['deliveryoptiondownload'] = 'Send to the browser and force a file download';
$string['deliveryoptioninline'] = 'Send the file inline to the browser';
$string['deliveryoptions'] = 'Delivery options';
$string['description'] = 'Description';
$string['duplicate'] = 'Duplicate';
$string['duplicateconfirm'] = 'Duplicate confirmation';

View file

@ -264,6 +264,8 @@ function customcert_supports($feature) {
return true;
case FEATURE_MOD_INTRO:
return true;
case FEATURE_SHOW_DESCRIPTION:
return true;
case FEATURE_COMPLETION_TRACKS_VIEWS:
return true;
case FEATURE_BACKUP_MOODLE2:

View file

@ -97,4 +97,4 @@ $PAGE->navbar->add(get_string('loadtemplate', 'customcert'));
echo $OUTPUT->header();
echo $OUTPUT->heading($heading);
echo $OUTPUT->confirm(get_string('loadtemplatemsg', 'customcert'), $yesurl, $nourl);
echo $OUTPUT->footer();
echo $OUTPUT->footer();

View file

@ -47,7 +47,6 @@ require_login();
require_capability('mod/customcert:manage', $context);
$title = $SITE->fullname;
$heading = $title;
// Set up the page.
$pageurl = new moodle_url('/mod/customcert/manage_templates.php');
@ -61,6 +60,8 @@ if ($tid && $action && confirm_sesskey()) {
$PAGE->navbar->add(get_string('managetemplates', 'customcert'));
}
$heading = format_string($title, true, ['context' => $context]);
if ($tid) {
if ($action && confirm_sesskey()) {
$nourl = new moodle_url('/mod/customcert/manage_templates.php');

View file

@ -22,6 +22,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use mod_customcert\certificate;
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
require_once($CFG->dirroot.'/course/moodleform_mod.php');
@ -55,14 +57,20 @@ class mod_customcert_mod_form extends moodleform_mod {
$this->standard_intro_elements(get_string('description', 'customcert'));
$optionsheader = $mform->createElement('header', 'options', get_string('options', 'customcert'));
$mform->addElement('header', 'options', get_string('options', 'customcert'));
$deliveryoptions = [
certificate::DELIVERY_OPTION_INLINE => get_string('deliveryoptioninline', 'customcert'),
certificate::DELIVERY_OPTION_DOWNLOAD => get_string('deliveryoptiondownload', 'customcert')
];
$mform->addElement('select', 'deliveryoption', get_string('deliveryoptions', 'customcert'), $deliveryoptions);
$mform->setDefault('deliveryoption', certificate::DELIVERY_OPTION_INLINE);
if (has_capability('mod/customcert:manageemailstudents', $this->get_context())) {
$mform->addElement('selectyesno', 'emailstudents', get_string('emailstudents', 'customcert'));
$mform->setDefault('emailstudents', get_config('customcert', 'emailstudents'));
$mform->addHelpButton('emailstudents', 'emailstudents', 'customcert');
$mform->setType('emailstudents', PARAM_INT);
$firstoption = 'emailstudents';
}
if (has_capability('mod/customcert:manageemailteachers', $this->get_context())) {
@ -70,7 +78,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->setDefault('emailteachers', get_config('customcert', 'emailteachers'));
$mform->addHelpButton('emailteachers', 'emailteachers', 'customcert');
$mform->setType('emailteachers', PARAM_INT);
$firstoption = empty($firstoption) ? 'emailteachers' : $firstoption;
}
if (has_capability('mod/customcert:manageemailothers', $this->get_context())) {
@ -78,7 +85,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->addHelpButton('emailothers', 'emailothers', 'customcert');
$mform->setDefault('emailothers', get_config('customcert', 'emailothers'));
$mform->setType('emailothers', PARAM_TEXT);
$firstoption = empty($firstoption) ? 'emailothers' : $firstoption;
}
if (has_capability('mod/customcert:manageverifyany', $this->get_context())) {
@ -86,7 +92,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->addHelpButton('verifyany', 'verifycertificateanyone', 'customcert');
$mform->setDefault('verifyany', get_config('customcert', 'verifyany'));
$mform->setType('verifyany', PARAM_INT);
$firstoption = empty($firstoption) ? 'verifyany' : $firstoption;
}
if (has_capability('mod/customcert:managerequiredtime', $this->get_context())) {
@ -94,7 +99,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->addHelpButton('requiredtime', 'coursetimereq', 'customcert');
$mform->setDefault('requiredtime', get_config('customcert', 'requiredtime'));
$mform->setType('requiredtime', PARAM_INT);
$firstoption = empty($firstoption) ? 'requiredtime' : $firstoption;
}
if (has_capability('mod/customcert:manageprotection', $this->get_context())) {
@ -106,11 +110,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->setType('protection_print', PARAM_BOOL);
$mform->setType('protection_modify', PARAM_BOOL);
$mform->setType('protection_copy', PARAM_BOOL);
$firstoption = empty($firstoption) ? 'protection_print' : $firstoption;
}
if (!empty($firstoption)) {
$mform->insertElementBefore($optionsheader, $firstoption);
}
$this->standard_coursemodule_elements();
@ -227,13 +226,13 @@ class mod_customcert_mod_form extends moodleform_mod {
$protection = explode(', ', $protection);
if (in_array(\mod_customcert\certificate::PROTECTION_PRINT, $protection)) {
if (in_array(certificate::PROTECTION_PRINT, $protection)) {
$data->protection_print = 1;
}
if (in_array(\mod_customcert\certificate::PROTECTION_MODIFY, $protection)) {
if (in_array(certificate::PROTECTION_MODIFY, $protection)) {
$data->protection_modify = 1;
}
if (in_array(\mod_customcert\certificate::PROTECTION_COPY, $protection)) {
if (in_array(certificate::PROTECTION_COPY, $protection)) {
$data->protection_copy = 1;
}

View file

@ -49,9 +49,15 @@ $user = \core_user::get_user($userid, '*', MUST_EXIST);
// If we are viewing certificates that are not for the currently logged in user then do a capability check.
if (($userid != $USER->id) && !has_capability('mod/customcert:viewallcertificates', context_system::instance())) {
print_error('You are not allowed to view these certificates');
throw new moodle_exception('You are not allowed to view these certificates');
}
$PAGE->set_url($pageurl);
$PAGE->set_context(context_user::instance($userid));
$PAGE->set_title(get_string('mycertificates', 'customcert'));
$PAGE->set_pagelayout('standard');
$PAGE->navigation->extend_for_user($user);
// Check if we requested to download a certificate.
if ($downloadcert) {
$template = $DB->get_record('customcert_templates', array('id' => $customcert->templateid), '*', MUST_EXIST);
@ -68,12 +74,6 @@ if ($table->is_downloading()) {
exit();
}
$PAGE->set_url($pageurl);
$PAGE->set_context(context_user::instance($userid));
$PAGE->set_title(get_string('mycertificates', 'customcert'));
$PAGE->set_pagelayout('standard');
$PAGE->navigation->extend_for_user($user);
// Additional page setup.
$PAGE->navbar->add(get_string('profile'), new moodle_url('/user/profile.php', array('id' => $userid)));
$PAGE->navbar->add(get_string('mycertificates', 'customcert'));

View file

@ -112,6 +112,18 @@ if ($elements) {
default:
$class = 'element refpoint-left';
}
switch ($element->alignment) {
case \mod_customcert\element::ALIGN_CENTER:
$class .= ' align-center';
break;
case \mod_customcert\element::ALIGN_RIGHT:
$class .= ' align-right';
break;
case \mod_customcert\element::ALIGN_LEFT:
default:
$class .= ' align-left';
break;
}
$html .= html_writer::tag('div', $e->render_html(), array('class' => $class,
'data-refpoint' => $element->refpoint, 'id' => 'element-' . $element->id));
}

View file

@ -57,6 +57,18 @@
margin: -4px -5px -5px 4px;
}
#page-mod-customcert-rearrange .element.align-left {
text-align: left;
}
#page-mod-customcert-rearrange .element.align-center {
text-align: center;
}
#page-mod-customcert-rearrange .element.align-right {
text-align: right;
}
#page-mod-customcert-rearrange #pdf {
border-style: solid;
border-width: 1px;

View file

@ -0,0 +1,169 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_customcert/mobile_view_activity_page
The main page to view the custom certificate activity
Classes required for JS:
* None
Data attibutes required for JS:
* All data attributes are required
Context variables required for this template:
* certificate
* cmid
* hasissues
* issues
* showgroups
* groups
* canmanage
* requiredtimemet
* hasrecipients
* recipients
* fileurl
* showreport
* currenttimestamp
Example context (json):
{
"certificate": {
"id": "1",
"course": "2",
"name": "A rad certificate name!",
"intro": "A certificate",
"requiredtime": "60"
},
"cmid": "25",
"issue": {
"timecreated": "1528370177"
},
"showgroups": "true",
"groups": [
{
"id": "2",
"selected": "false",
"name": "Group A"
}
],
"canmanage": "true",
"requiredtimemet": "true",
"fileurl": "http://yoursite.com/mod/customcert/mobile/pluginfile.php?id=4",
"showreport": "true",
"hasrecipients": "true",
"recipients": [
{
"id": "2",
"issueid": "3",
"displayname": "Michaelangelo (Mickey)",
"fileurl": "http://yoursite.com/mod/customcert/mobile/pluginfile.php?id=4",
"timecreated": "1528370177"
}
],
"currenttimestamp": "1528370177"
}
}}
{{=<% %>=}}
<core-course-module-description description="<% certificate.intro %>" component="mod_customcert" componentId="<% cmid %>"></core-course-module-description>
<ion-list>
<%^canmanage%>
<%#requiredtimemet%>
<ion-item>
<ion-label>
{{ 'plugin.mod_customcert.receiveddate' | translate }}
<br />
<div class="timerewarded">
<%#issue%>
{{ <% timecreated %> | coreToLocaleString }}
<%/issue%>
<%^issue%>
{{ 'plugin.mod_customcert.notissued' | translate }}
<%/issue%>
</div>
</ion-label>
<div slot="end" class="flex-row">
<ion-button fill="clear" [core-download-file]="{fileurl: '<% fileurl %>', timemodified: '<% currenttimestamp %>'}" moduleId="<% cmid %>" courseId="<% certificate.course %>" component="mod_customcert" [attr.aria-label]="'core.download' | translate">
<ion-icon name="cloud-download" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</div>
</ion-item>
<%/requiredtimemet%>
<%^requiredtimemet%>
<ion-item>
<ion-label>
<p>{{ 'plugin.mod_customcert.requiredtimenotmet' | translate: {$a: { requiredtime: <% certificate.requiredtime %>} } }}</p>
</ion-label>
</ion-item>
<%/requiredtimemet%>
<%/canmanage%>
<%#canmanage%>
<ion-button expand="block" class="ion-margin" core-course-download-module-main-file moduleId="<% cmid %>" courseId="<% certificate.course %>" component="mod_customcert" [files]="[{fileurl: '<% fileurl %>', timemodified: '<% currenttimestamp %>'}]">
<ion-icon name="cloud-download" slot="start" aria-hidden="true"></ion-icon>
{{ 'plugin.mod_customcert.getcustomcert' | translate }}
</ion-button>
<%/canmanage%>
<%#showreport%>
<ion-item>
<ion-label>
{{ 'plugin.mod_customcert.listofissues' | translate: { $a: <% numrecipients %> } }}
</ion-label>
</ion-item>
<%#showgroups%>
<ion-item>
<ion-label>{{ 'plugin.mod_customcert.selectagroup' | translate }}</ion-label>
<ion-select [(ngModel)]="CONTENT_OTHERDATA.group" name="group" (ionChange)="updateContent({cmid: <% cmid %>, courseid: <% certificate.course %>, group: CONTENT_OTHERDATA.group})" interface="popover">
<%#groups%>
<ion-select-option [value]="<% id %>"><% name %></ion-select-option>
<%/groups%>
</ion-select>
</ion-item>
<%/showgroups%>
<%#hasrecipients%>
<%#recipients%>
<ion-item>
<ion-label>
<% displayname %>
<br />
<div class="timerewarded">{{ <% timecreated %> | coreToLocaleString }}</div>
</ion-label>
<div slot="end" class="flex-row">
<ion-button fill="clear" [core-download-file]="{fileurl: '<% fileurl %>', timemodified: '<% currenttimestamp %>'}" moduleId="<% cmid %>" courseId="<% certificate.course %>" component="mod_customcert" [attr.aria-label]="'core.download' | translate">
<ion-icon name="cloud-download" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<%#canmanage%>
<ion-button fill="clear" core-site-plugins-call-ws name="mod_customcert_delete_issue"
[params]="{certificateid: <% certificate.id %>, issueid: <% issueid %>}"
[preSets]="{getFromCache: 0, saveToCache: 0, typeExpected: 'boolean'}"
confirmMessage="{{ 'plugin.mod_customcert.deleteissueconfirm' | translate }}"
refreshOnSuccess="true" [attr.aria-label]="'core.delete' | translate">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<%/canmanage%>
</div>
</ion-item>
<%/recipients%>
<%/hasrecipients%>
<%^hasrecipients%>
<ion-item>
<ion-label>
{{ 'plugin.mod_customcert.nothingtodisplay' | translate }}
</ion-label>
</ion-item>
<%/hasrecipients%>
<%/showreport%>
</ion-list>

View file

@ -40,7 +40,7 @@ class behat_mod_customcert extends behat_base {
/**
* Adds an element to the specified page of a template.
*
* @codingStandardsIgnoreLine
* phpcs:ignore
* @Given /^I add the element "(?P<element_name>(?:[^"]|\\")*)" to page "(?P<page_number>\d+)" of the "(?P<template_name>(?:[^"]|\\")*)" certificate template$/
* @param string $elementname
* @param int $pagenum

View file

@ -100,20 +100,22 @@ Feature: Being able to manage elements in a certificate template
# Course name.
And I add the element "Course name" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values:
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Font | Helvetica |
| Type | Short name |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
And I should see "Course name" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Course name" "table_row"
And the following fields match these values:
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Font | Helvetica |
| Type | Short name |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
# Date.
And I add the element "Date" to page "1" of the "Custom certificate 1" certificate template

View file

@ -55,7 +55,7 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
// Create a custom certificate with no elements.
$this->getDataGenerator()->create_module('customcert', ['course' => $course->id, 'emailstudents' => 1]);
// Enrol the user as a student
// Enrol the user as a student.
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
// Run the task.
@ -416,4 +416,4 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
// Confirm no emails were sent.
$this->assertCount(0, $emails);
}
}
}

View file

@ -24,10 +24,10 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2020061500; // The current module version (Date: YYYYMMDDXX).
$plugin->version = 2020061503; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2020061500; // Requires this Moodle version (3.9).
$plugin->cron = 0; // Period for cron to check this module (secs).
$plugin->component = 'mod_customcert';
$plugin->maturity = MATURITY_STABLE;
$plugin->release = "3.9.0"; // User-friendly version number.
$plugin->release = "3.9.1"; // User-friendly version number.

View file

@ -56,7 +56,8 @@ if ($customcert->requiredtime && !$canmanage) {
if (\mod_customcert\certificate::get_course_time($course->id) < ($customcert->requiredtime * 60)) {
$a = new stdClass;
$a->requiredtime = $customcert->requiredtime;
notice(get_string('requiredtimenotmet', 'customcert', $a), "$CFG->wwwroot/course/view.php?id=$course->id");
$url = new moodle_url('/course/view.php', ['id' => $course->id]);
notice(get_string('requiredtimenotmet', 'customcert', $a), $url);
die;
}
}
@ -158,7 +159,7 @@ if (!$downloadown && !$downloadissue) {
}
echo $OUTPUT->footer($course);
exit();
} else if ($canreceive) { // Output to pdf.
} else if ($canreceive || $canmanage) { // Output to pdf.
// Set the userid value of who we are downloading the certificate for.
$userid = $USER->id;
if ($downloadown) {