Merge branch 'MDL-74957' of https://github.com/paulholden/moodle
This commit is contained in:
@@ -107,15 +107,15 @@ class task_log extends base {
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("$tablealias.classname")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function(string $value): string {
|
||||
->add_callback(static function(string $classname): string {
|
||||
$output = '';
|
||||
if (class_exists($value)) {
|
||||
$task = new $value;
|
||||
if ($task instanceof \core\task\scheduled_task) {
|
||||
if (class_exists($classname)) {
|
||||
$task = new $classname;
|
||||
if ($task instanceof \core\task\task_base) {
|
||||
$output = $task->get_name();
|
||||
}
|
||||
}
|
||||
$output .= \html_writer::tag('div', "\\{$value}", [
|
||||
$output .= \html_writer::tag('div', "\\{$classname}", [
|
||||
'class' => 'task-class',
|
||||
]);
|
||||
return $output;
|
||||
|
||||
+1
-1
@@ -2080,7 +2080,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$errorline = true;
|
||||
} else {
|
||||
if ($status) { //Handle ok result (ok)
|
||||
$status = get_string('ok');
|
||||
$status = get_string('statusok');
|
||||
} else {
|
||||
if ($environment_result->getLevel() == 'optional') {//Handle check result (warning)
|
||||
$status = get_string('check');
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
<library>
|
||||
<location>amd/src/lunr.js</location>
|
||||
<name>lunr - http://lunrjs.com</name>
|
||||
<license>MIT</license>
|
||||
<description>Provide a great search experience without the need for external, server-side, search services.</description>
|
||||
<version>2.3.8</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/olivernn/lunr.js</repository>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -344,6 +344,10 @@ class api {
|
||||
$settings->enabledashboard = $CFG->enabledashboard;
|
||||
}
|
||||
|
||||
if (empty($section) || $section === 'themesettings') {
|
||||
$settings->customusermenuitems = $CFG->customusermenuitems;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
@@ -234,6 +234,7 @@ class externallib_test extends externallib_advanced_testcase {
|
||||
$expected[] = ['name' => 'coursegraceperiodbefore', 'value' => $CFG->coursegraceperiodbefore];
|
||||
|
||||
$expected[] = ['name' => 'enabledashboard', 'value' => $CFG->enabledashboard];
|
||||
$expected[] = ['name' => 'customusermenuitems', 'value' => $CFG->customusermenuitems];
|
||||
|
||||
$this->assertCount(0, $result['warnings']);
|
||||
$this->assertEquals($expected, $result['settings']);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
This files describes changes in tool_mobile code.
|
||||
Information provided here is intended especially for developers.
|
||||
|
||||
=== 4.1 ===
|
||||
|
||||
* External function tool_mobile::get_config now returns the customusermenuitems setting.
|
||||
|
||||
=== 4.0 ===
|
||||
|
||||
* The function tool_mobile\api::get_qrlogin_key() now requires as parameter an object with all the mobile plugin settings.
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
<library>
|
||||
<location>amd/src/jquery-eu-cookie-law-popup.js</location>
|
||||
<name>jQuery EU Cookie Law popups</name>
|
||||
<license>MIT</license>
|
||||
<description>An easy-to-install jQuery plugin to create EU Cookie Law popups and for GDPR compliance.</description>
|
||||
<version>1.1.3</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/wimagguc/jquery-eu-cookie-law-popup</repository>
|
||||
<copyrights>
|
||||
<copyright>2015 Richard Dancsi</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
<library>
|
||||
<location>CAS</location>
|
||||
<name>CAS</name>
|
||||
<license>Apache</license>
|
||||
<description>phpCAS library to support CAS authentication plugin.</description>
|
||||
<version>1.4.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/apereo/phpCAS</repository>
|
||||
<copyrights>
|
||||
<copyright>2007-2020, Apereo Foundation</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -202,7 +202,7 @@ abstract class backup_cron_automated_helper {
|
||||
$message .= get_string('summary') . "\n";
|
||||
$message .= "==================================================\n";
|
||||
$message .= ' ' . get_string('courses') . ': ' . array_sum($count) . "\n";
|
||||
$message .= ' ' . get_string('ok') . ': ' . $count[self::BACKUP_STATUS_OK] . "\n";
|
||||
$message .= ' ' . get_string('statusok') . ': ' . $count[self::BACKUP_STATUS_OK] . "\n";
|
||||
$message .= ' ' . get_string('skipped') . ': ' . $count[self::BACKUP_STATUS_SKIPPED] . "\n";
|
||||
$message .= ' ' . get_string('error') . ': ' . $count[self::BACKUP_STATUS_ERROR] . "\n";
|
||||
$message .= ' ' . get_string('unfinished') . ': ' . $count[self::BACKUP_STATUS_UNFINISHED] . "\n";
|
||||
|
||||
@@ -12,9 +12,10 @@ Feature: Restore Moodle 2 course backups
|
||||
| Course 3 | C3 | 0 | topics | 2 | 0 |
|
||||
| Course 4 | C4 | 0 | topics | 20 | 0 |
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | section |
|
||||
| assign | C3 | assign1 | Test assign name | Assign description | 1 |
|
||||
| data | C3 | data1 | Test database name | Database description | 2 |
|
||||
| activity | course | idnumber | name | intro | section | externalurl |
|
||||
| assign | C3 | assign1 | Test assign name | Assign description | 1 | |
|
||||
| data | C3 | data1 | Test database name | Database description | 2 | |
|
||||
| url | C1 | url1 | Test URL name | Test URL description | 3 | http://www.moodle.org |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Forum" to section "1" and I fill the form with:
|
||||
@@ -108,11 +109,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course retaining the backup course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Confirmation | Filename | test_backup.mbz |
|
||||
@@ -134,11 +131,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course keeping the target course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Confirmation | Filename | test_backup.mbz |
|
||||
@@ -160,11 +153,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course deleting contents and retaining the backup course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Initial | Include enrolled users | 0 |
|
||||
@@ -188,11 +177,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course deleting contents and keeping the current course settings
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Initial | Include enrolled users | 0 |
|
||||
@@ -216,11 +201,7 @@ Feature: Restore Moodle 2 course backups
|
||||
|
||||
@javascript
|
||||
Scenario: Restore a backup in an existing course deleting contents decreasing the number of sections
|
||||
Given I add a "URL" to section "3" and I fill the form with:
|
||||
| Name | Test URL name |
|
||||
| Description | Test URL description |
|
||||
| id_externalurl | http://www.moodle.org |
|
||||
And I hide section "3"
|
||||
Given I hide section "3"
|
||||
And I hide section "7"
|
||||
When I backup "Course 1" course using this options:
|
||||
| Initial | Include enrolled users | 0 |
|
||||
|
||||
@@ -19,6 +19,7 @@ declare(strict_types=1);
|
||||
namespace core_badges\reportbuilder\local\entities;
|
||||
|
||||
use context_course;
|
||||
use context_helper;
|
||||
use context_system;
|
||||
use html_writer;
|
||||
use lang_string;
|
||||
@@ -48,7 +49,10 @@ class badge extends base {
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default_table_aliases(): array {
|
||||
return ['badge' => 'b'];
|
||||
return [
|
||||
'badge' => 'b',
|
||||
'context' => 'bctx',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +93,7 @@ class badge extends base {
|
||||
*/
|
||||
protected function get_all_columns(): array {
|
||||
$badgealias = $this->get_table_alias('badge');
|
||||
$contextalias = $this->get_table_alias('context');
|
||||
|
||||
// Name.
|
||||
$columns[] = (new column(
|
||||
@@ -101,7 +106,7 @@ class badge extends base {
|
||||
->add_field("{$badgealias}.name")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Description.
|
||||
// Description (note, this column contains plaintext so requires no post-processing).
|
||||
$columns[] = (new column(
|
||||
'description',
|
||||
new lang_string('description', 'core_badges'),
|
||||
@@ -137,13 +142,20 @@ class badge extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_join("LEFT JOIN {context} {$contextalias}
|
||||
ON {$contextalias}.contextlevel = " . CONTEXT_COURSE . "
|
||||
AND {$contextalias}.instanceid = {$badgealias}.courseid")
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_fields("{$badgealias}.id, {$badgealias}.type, {$badgealias}.courseid, {$badgealias}.imagecaption")
|
||||
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
|
||||
->set_disabled_aggregation_all()
|
||||
->add_callback(static function(int $badgeid, stdClass $badge): string {
|
||||
$context = $badge->type == BADGE_TYPE_SITE
|
||||
? context_system::instance()
|
||||
: context_course::instance($badge->courseid);
|
||||
if ($badge->type == BADGE_TYPE_SITE) {
|
||||
$context = context_system::instance();
|
||||
} else {
|
||||
context_helper::preload_from_record($badge);
|
||||
$context = context_course::instance($badge->courseid);
|
||||
}
|
||||
|
||||
$badgeimage = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $badgeid, '/', 'f2');
|
||||
return html_writer::img($badgeimage, $badge->imagecaption);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// 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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use core_badges\badge;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once("{$CFG->libdir}/badgeslib.php");
|
||||
|
||||
/**
|
||||
* Badges test generator
|
||||
*
|
||||
* @package core_badges
|
||||
* @copyright 2022 Paul Holden <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_badges_generator extends component_generator_base {
|
||||
|
||||
/**
|
||||
* Create badge
|
||||
*
|
||||
* TODO: MDL-73648 Use from Behat too
|
||||
*
|
||||
* @param array|stdClass $record
|
||||
*/
|
||||
public function create_badge($record): badge {
|
||||
global $DB, $USER;
|
||||
|
||||
$record = (object) array_merge([
|
||||
'name' => 'Test badge',
|
||||
'description' => 'Testing badges',
|
||||
'timecreated' => time(),
|
||||
'timemodified' => time(),
|
||||
'usercreated' => $USER->id,
|
||||
'usermodified' => $USER->id,
|
||||
'issuername' => 'Test issuer',
|
||||
'issuerurl' => 'http://issuer-url.domain.co.nz',
|
||||
'issuercontact' => '[email protected]',
|
||||
'expiredate' => null,
|
||||
'expireperiod' => null,
|
||||
'type' => BADGE_TYPE_SITE,
|
||||
'courseid' => null,
|
||||
'messagesubject' => 'Test message subject',
|
||||
'message' => 'Test message body',
|
||||
'attachment' => 1,
|
||||
'notification' => 0,
|
||||
'status' => BADGE_STATUS_ACTIVE,
|
||||
'version' => OPEN_BADGES_V2,
|
||||
'language' => 'en',
|
||||
'imageauthorname' => 'Image author',
|
||||
'imageauthoremail' => '[email protected]',
|
||||
'imageauthorurl' => 'http://image.example.com/',
|
||||
'imagecaption' => 'Image caption'
|
||||
], (array) $record);
|
||||
|
||||
$record->id = $DB->insert_record('badge', $record);
|
||||
|
||||
return new badge($record->id);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace core_badges\reportbuilder\datasource;
|
||||
|
||||
use core_badges\badge;
|
||||
use core_badges_generator;
|
||||
use core_reportbuilder_generator;
|
||||
use core_reportbuilder_testcase;
|
||||
|
||||
@@ -56,13 +56,16 @@ class badges_test extends core_reportbuilder_testcase {
|
||||
$user1 = $this->getDataGenerator()->create_user(['firstname' => 'Alan', 'lastname' => 'Apple']);
|
||||
$user2 = $this->getDataGenerator()->create_user(['firstname' => 'Barry', 'lastname' => 'Banana']);
|
||||
|
||||
$sitebadge = $this->create_badge(['name' => 'Badge 1']);
|
||||
/** @var core_badges_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_badges');
|
||||
|
||||
$sitebadge = $generator->create_badge(['name' => 'Badge 1']);
|
||||
$sitebadge->issue($user1->id, true);
|
||||
$sitebadge->issue($user2->id, true);
|
||||
|
||||
// Another badge, in a course, no issues.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursebadge = $this->create_badge(['name' => 'Badge 2', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]);
|
||||
$coursebadge = $generator->create_badge(['name' => 'Badge 2', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]);
|
||||
|
||||
/** @var core_reportbuilder_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
|
||||
@@ -102,8 +105,11 @@ class badges_test extends core_reportbuilder_testcase {
|
||||
$course = $this->getDataGenerator()->create_course(['tags' => ['horse']]);
|
||||
$user = $this->getDataGenerator()->create_user(['interests' => ['pie']]);
|
||||
|
||||
/** @var core_badges_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_badges');
|
||||
|
||||
// Create course badge, issue to user.
|
||||
$badge = $this->create_badge(['name' => 'Course badge', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]);
|
||||
$badge = $generator->create_badge(['name' => 'Course badge', 'type' => BADGE_TYPE_COURSE, 'courseid' => $course->id]);
|
||||
$badge->issue($user->id, true);
|
||||
|
||||
/** @var core_reportbuilder_generator $generator */
|
||||
@@ -128,45 +134,4 @@ class badges_test extends core_reportbuilder_testcase {
|
||||
'pie',
|
||||
], array_values($content[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a badge
|
||||
*
|
||||
* @param array $params
|
||||
* @return badge
|
||||
*/
|
||||
protected function create_badge(array $params = []): badge {
|
||||
global $DB, $USER;
|
||||
|
||||
$record = (object) array_merge([
|
||||
'name' => 'Test badge',
|
||||
'description' => 'Testing badges',
|
||||
'timecreated' => time(),
|
||||
'timemodified' => time(),
|
||||
'usercreated' => $USER->id,
|
||||
'usermodified' => $USER->id,
|
||||
'issuername' => 'Test issuer',
|
||||
'issuerurl' => 'http://issuer-url.domain.co.nz',
|
||||
'issuercontact' => '[email protected]',
|
||||
'expiredate' => null,
|
||||
'expireperiod' => null,
|
||||
'type' => BADGE_TYPE_SITE,
|
||||
'courseid' => null,
|
||||
'messagesubject' => 'Test message subject',
|
||||
'message' => 'Test message body',
|
||||
'attachment' => 1,
|
||||
'notification' => 0,
|
||||
'status' => BADGE_STATUS_ACTIVE,
|
||||
'version' => OPEN_BADGES_V2,
|
||||
'language' => 'en',
|
||||
'imageauthorname' => 'Image author',
|
||||
'imageauthoremail' => '[email protected]',
|
||||
'imageauthorurl' => 'http://image.example.com/',
|
||||
'imagecaption' => 'Image caption'
|
||||
], $params);
|
||||
|
||||
$record->id = $DB->insert_record('badge', $record);
|
||||
|
||||
return new badge($record->id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,18 @@ Feature: Add URL to main menu block
|
||||
And I am on site homepage
|
||||
And I turn editing mode on
|
||||
And I add the "Main menu" block
|
||||
When I add a "URL" to section "0" and I fill the form with:
|
||||
| Name | reference link |
|
||||
| Description | mooooooooodle |
|
||||
| External URL | http://www.moodle.com |
|
||||
| id_display | In pop-up |
|
||||
And the following "activity" exists:
|
||||
| activity | url |
|
||||
| course | Acceptance test site |
|
||||
| name | reference link |
|
||||
| intro | mooooooooodle |
|
||||
| externalurl | http://www.moodle.com |
|
||||
| section | 0 |
|
||||
When I am on the "reference link" "url activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the following fields to these values:
|
||||
| id_display | In pop-up |
|
||||
And I press "Save and return to course"
|
||||
Then "reference link" "link" should exist in the "Main menu" "block"
|
||||
And "Add an activity or resource" "button" should exist in the "Main menu" "block"
|
||||
|
||||
@@ -33,11 +40,18 @@ Feature: Add URL to main menu block
|
||||
And I set the following fields to these values:
|
||||
| Page contexts | Display throughout the entire site |
|
||||
And I press "Save changes"
|
||||
And I add a "URL" to section "0" and I fill the form with:
|
||||
| Name | reference link |
|
||||
| Description | mooooooooodle |
|
||||
| External URL | http://www.moodle.com |
|
||||
| id_display | Embed |
|
||||
And the following "activity" exists:
|
||||
| activity | url |
|
||||
| course | Acceptance test site |
|
||||
| name | reference link |
|
||||
| intro | mooooooooodle |
|
||||
| externalurl | http://www.moodle.com |
|
||||
| section | 0 |
|
||||
And I am on the "reference link" "url activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the following fields to these values:
|
||||
| id_display | Embed |
|
||||
And I press "Save and return to course"
|
||||
Then I click on "reference link" "link" in the "Main menu" "block"
|
||||
And "reference link" "link" should exist in the "Main menu" "block"
|
||||
And I am on the "C1" "Course" page
|
||||
@@ -60,11 +74,18 @@ Feature: Add URL to main menu block
|
||||
And I set the following fields to these values:
|
||||
| Page contexts | Display on the site home and any pages added to the site home. |
|
||||
And I press "Save changes"
|
||||
And I add a "URL" to section "0" and I fill the form with:
|
||||
| Name | reference link |
|
||||
| Description | mooooooooodle |
|
||||
| External URL | http://www.moodle.com |
|
||||
| id_display | Embed |
|
||||
And the following "activity" exists:
|
||||
| activity | url |
|
||||
| course | Acceptance test site |
|
||||
| name | reference link |
|
||||
| intro | mooooooooodle |
|
||||
| externalurl | http://www.moodle.com |
|
||||
| section | 0 |
|
||||
And I am on the "reference link" "url activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the following fields to these values:
|
||||
| id_display | Embed |
|
||||
And I press "Save and return to course"
|
||||
Then I click on "reference link" "link" in the "Main menu" "block"
|
||||
And "reference link" "link" should exist in the "Main menu" "block"
|
||||
And I am on the "C1" "Course" page
|
||||
@@ -87,12 +108,18 @@ Feature: Add URL to main menu block
|
||||
And I set the following fields to these values:
|
||||
| Page contexts | Display throughout the entire site |
|
||||
And I press "Save changes"
|
||||
When I am on the "C1" Course page
|
||||
And I add a "URL" to section "0" and I fill the form with:
|
||||
| Name | reference link |
|
||||
| Description | mooooooooodle |
|
||||
| External URL | http://www.moodle.com |
|
||||
| Display description on course page | 1 |
|
||||
| id_display | In pop-up |
|
||||
When the following "activity" exists:
|
||||
| activity | url |
|
||||
| course | C1 |
|
||||
| name | reference link |
|
||||
| intro | mooooooooodle |
|
||||
| externalurl | http://www.moodle.com |
|
||||
| section | 0 |
|
||||
| showdescription | 1 |
|
||||
And I am on the "reference link" "url activity editing" page
|
||||
And I expand all fieldsets
|
||||
And I set the following fields to these values:
|
||||
| id_display | In pop-up |
|
||||
And I press "Save and return to course"
|
||||
Then "reference link" "link" should not exist in the "Main menu" "block"
|
||||
And I should see "mooooooooodle" in the "region-main" "region"
|
||||
|
||||
+4
-2
@@ -3,8 +3,10 @@
|
||||
<library>
|
||||
<location>MongoDB</location>
|
||||
<name>MongoDB PHP Library</name>
|
||||
<license>Apache</license>
|
||||
<description>This library provides a high-level abstraction around the lower-level PHP driver, also known as the mongodb extension.</description>
|
||||
<version>1.8.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/mongodb/mongo-php-driver</repository>
|
||||
</library>
|
||||
</libraries>
|
||||
</libraries>
|
||||
|
||||
@@ -45,7 +45,10 @@ class cohort extends base {
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default_table_aliases(): array {
|
||||
return ['cohort' => 'c'];
|
||||
return [
|
||||
'cohort' => 'c',
|
||||
'context' => 'chctx',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,6 +89,7 @@ class cohort extends base {
|
||||
*/
|
||||
protected function get_all_columns(): array {
|
||||
$tablealias = $this->get_table_alias('cohort');
|
||||
$contextalias = $this->get_table_alias('context');
|
||||
|
||||
// Category/context column.
|
||||
$columns[] = (new column(
|
||||
@@ -94,11 +98,13 @@ class cohort extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_join("JOIN {context} {$contextalias} ON {$contextalias}.id = {$tablealias}.contextid")
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields("{$tablealias}.contextid")
|
||||
->add_fields("{$tablealias}.contextid, " . context_helper::get_preload_record_columns_sql($contextalias))
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function($contextid): string {
|
||||
return context::instance_by_id((int) $contextid)->get_context_name(false);
|
||||
->add_callback(static function($contextid, stdClass $cohort): string {
|
||||
context_helper::preload_from_record($cohort);
|
||||
return context::instance_by_id($cohort->contextid)->get_context_name(false);
|
||||
});
|
||||
|
||||
// Name column.
|
||||
@@ -130,8 +136,10 @@ class cohort extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_join("JOIN {context} {$contextalias} ON {$contextalias}.id = {$tablealias}.contextid")
|
||||
->set_type(column::TYPE_LONGTEXT)
|
||||
->add_fields("{$tablealias}.description, {$tablealias}.descriptionformat, {$tablealias}.id, {$tablealias}.contextid")
|
||||
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
|
||||
->add_callback(static function(?string $description, stdClass $cohort): string {
|
||||
global $CFG;
|
||||
require_once("{$CFG->libdir}/filelib.php");
|
||||
@@ -140,12 +148,14 @@ class cohort extends base {
|
||||
return '';
|
||||
}
|
||||
|
||||
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $cohort->contextid, 'cohort',
|
||||
context_helper::preload_from_record($cohort);
|
||||
$context = context::instance_by_id($cohort->contextid);
|
||||
|
||||
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $context->id, 'cohort',
|
||||
'description', $cohort->id);
|
||||
|
||||
return format_text($description, $cohort->descriptionformat, ['context' => $cohort->contextid]);
|
||||
})
|
||||
->set_is_sortable(false);
|
||||
return format_text($description, $cohort->descriptionformat, ['context' => $context->id]);
|
||||
});
|
||||
|
||||
// Visible column.
|
||||
$columns[] = (new column(
|
||||
|
||||
@@ -18,7 +18,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace core_course\local\entities;
|
||||
|
||||
use context_coursecat;
|
||||
use context_helper;
|
||||
use html_writer;
|
||||
use lang_string;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
use core_course_category;
|
||||
use core_reportbuilder\local\entities\base;
|
||||
@@ -101,6 +105,26 @@ class course_category extends base {
|
||||
})
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Category name with link column.
|
||||
$columns[] = (new column(
|
||||
'namewithlink',
|
||||
new lang_string('namewithlink', 'core_course'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_join($this->get_context_join())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields("{$tablealias}.name, {$tablealias}.id")
|
||||
->add_fields(context_helper::get_preload_record_columns_sql($tablealiascontext))
|
||||
->add_callback(static function(string $name, stdClass $category): string {
|
||||
context_helper::preload_from_record($category);
|
||||
$context = context_coursecat::instance($category->id);
|
||||
$url = new moodle_url('/course/management.php', ['categoryid' => $category->id]);
|
||||
return html_writer::link($url,
|
||||
format_string($category->name, true, ['context' => $context]));
|
||||
})
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Path column.
|
||||
$columns[] = (new column(
|
||||
'path',
|
||||
@@ -134,12 +158,10 @@ class course_category extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_join("
|
||||
JOIN {context} {$tablealiascontext}
|
||||
ON {$tablealiascontext}.instanceid = {$tablealias}.id
|
||||
AND {$tablealiascontext}.contextlevel = " . CONTEXT_COURSECAT)
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields("{$tablealias}.description, {$tablealias}.descriptionformat, {$tablealiascontext}.id AS contextid")
|
||||
->add_join($this->get_context_join())
|
||||
->set_type(column::TYPE_LONGTEXT)
|
||||
->add_fields("{$tablealias}.description, {$tablealias}.descriptionformat, {$tablealias}.id")
|
||||
->add_fields(context_helper::get_preload_record_columns_sql($tablealiascontext))
|
||||
->add_callback(static function(?string $description, stdClass $category): string {
|
||||
global $CFG;
|
||||
require_once("{$CFG->libdir}/filelib.php");
|
||||
@@ -148,12 +170,14 @@ class course_category extends base {
|
||||
return '';
|
||||
}
|
||||
|
||||
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $category->contextid, 'coursecat',
|
||||
context_helper::preload_from_record($category);
|
||||
$context = context_coursecat::instance($category->id);
|
||||
|
||||
$description = file_rewrite_pluginfile_urls($description, 'pluginfile.php', $context->id, 'coursecat',
|
||||
'description', null);
|
||||
|
||||
return format_text($description, $category->descriptionformat, ['context' => $category->contextid]);
|
||||
})
|
||||
->set_is_sortable(false);
|
||||
return format_text($description, $category->descriptionformat, ['context' => $context->id]);
|
||||
});
|
||||
|
||||
return $columns;
|
||||
}
|
||||
@@ -201,4 +225,16 @@ class course_category extends base {
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return context join used by columns
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_context_join(): string {
|
||||
$coursecategories = $this->get_table_alias('course_categories');
|
||||
$context = $this->get_table_alias('context');
|
||||
return "LEFT JOIN {context} {$context} ON {$context}.instanceid = {$coursecategories}.id
|
||||
AND {$context}.contextlevel = " . CONTEXT_COURSECAT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,63 +61,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
return $help;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a content_item object based on legacy data returned from the get_shortcuts hook implementations.
|
||||
*
|
||||
* @param \stdClass $item the stdClass of legacy data.
|
||||
* @return content_item a content item object.
|
||||
*/
|
||||
private function content_item_from_legacy_data(\stdClass $item): content_item {
|
||||
global $OUTPUT;
|
||||
|
||||
// Make sure the legacy data results in a content_item with id = 0.
|
||||
// Even with an id, we can't uniquely identify the item, because we can't guarantee what component it came from.
|
||||
// An id of -1, signifies this.
|
||||
$item->id = -1;
|
||||
|
||||
// If the module provides the helplink property, append it to the help text to match the look and feel
|
||||
// of the default course modules.
|
||||
if (isset($item->help) && isset($item->helplink)) {
|
||||
$linktext = get_string('morehelp');
|
||||
$item->help .= \html_writer::tag('div',
|
||||
$OUTPUT->doc_link($item->helplink, $linktext, true), ['class' => 'helpdoclink']);
|
||||
}
|
||||
|
||||
if (is_string($item->title)) {
|
||||
$item->title = new string_title($item->title);
|
||||
} else if ($item->title instanceof \lang_string) {
|
||||
$item->title = new lang_string_title($item->title->get_identifier(), $item->title->get_component());
|
||||
}
|
||||
|
||||
// Legacy items had names which are in one of 2 forms:
|
||||
// modname, i.e. 'assign' or
|
||||
// modname:link, i.e. lti:http://etc...
|
||||
// We need to grab the module name out to create the componentname.
|
||||
$modname = (strpos($item->name, ':') !== false) ? explode(':', $item->name)[0] : $item->name;
|
||||
$purpose = plugin_supports('mod', $modname, FEATURE_MOD_PURPOSE, MOD_PURPOSE_OTHER);
|
||||
return new content_item($item->id, $item->name, $item->title, $item->link, $item->icon, $item->help ?? '',
|
||||
$item->archetype, 'mod_' . $modname, $purpose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stdClass type object based on a content_item instance.
|
||||
*
|
||||
* @param content_item $contentitem
|
||||
* @return \stdClass the legacy data.
|
||||
*/
|
||||
private function content_item_to_legacy_data(content_item $contentitem): \stdClass {
|
||||
$item = new \stdClass();
|
||||
$item->id = $contentitem->get_id();
|
||||
$item->name = $contentitem->get_name();
|
||||
$item->title = $contentitem->get_title();
|
||||
$item->link = $contentitem->get_link();
|
||||
$item->icon = $contentitem->get_icon();
|
||||
$item->help = $contentitem->get_help();
|
||||
$item->archetype = $contentitem->get_archetype();
|
||||
$item->componentname = $contentitem->get_component_name();
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the contentitems from all subplugin hooks for a given module plugin.
|
||||
*
|
||||
@@ -165,20 +108,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
return $contentitems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to make sure any legacy items have certain properties, which, if missing are inherited from the parent module item.
|
||||
*
|
||||
* @param \stdClass $legacyitem the legacy information, a stdClass coming from get_shortcuts() hook.
|
||||
* @param content_item $modulecontentitem The module's content item information, to inherit if needed.
|
||||
* @return \stdClass the updated legacy item stdClass
|
||||
*/
|
||||
private function legacy_item_inherit_missing(\stdClass $legacyitem, content_item $modulecontentitem): \stdClass {
|
||||
// Fall back to the plugin parent value if the subtype didn't provide anything.
|
||||
$legacyitem->archetype = $legacyitem->archetype ?? $modulecontentitem->get_archetype();
|
||||
$legacyitem->icon = $legacyitem->icon ?? $modulecontentitem->get_icon();
|
||||
return $legacyitem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the available content items, not restricted to course or user.
|
||||
*
|
||||
@@ -254,9 +183,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
$modules = $DB->get_records('modules', ['visible' => 1]);
|
||||
$return = [];
|
||||
|
||||
// A moodle_url is expected and required by modules in their implementation of the hook 'get_shortcuts'.
|
||||
$urlbase = new \moodle_url('/course/mod.php', ['id' => $course->id]);
|
||||
|
||||
// Now, generate the content_items.
|
||||
foreach ($modules as $modid => $mod) {
|
||||
// Exclude modules if the code doesn't exist.
|
||||
@@ -273,7 +199,7 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
$mod->id,
|
||||
$mod->name,
|
||||
new lang_string_title("modulename", $mod->name),
|
||||
new \moodle_url($urlbase, ['add' => $mod->name]),
|
||||
new \moodle_url('/course/mod.php', ['id' => $course->id, 'add' => $mod->name]),
|
||||
$OUTPUT->pix_icon('monologo', '', $mod->name, ['class' => 'icon activityicon']),
|
||||
$help,
|
||||
$archetype,
|
||||
@@ -281,10 +207,6 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
$purpose,
|
||||
);
|
||||
|
||||
// Legacy vs new hooks.
|
||||
// If the new hook is found for a module plugin, use that path (calling mod plugins and their subplugins directly)
|
||||
// If not, check the legacy hook. This won't provide us with enough information to identify items uniquely within their
|
||||
// component (lti + lti source being an example), but we can still list these items.
|
||||
$modcontentitemreference = clone($contentitem);
|
||||
|
||||
if (component_callback_exists('mod_' . $mod->name, 'get_course_content_items')) {
|
||||
@@ -301,37 +223,8 @@ class content_item_readonly_repository implements content_item_readonly_reposito
|
||||
array_push($return, ...$subpluginitems);
|
||||
}
|
||||
|
||||
} else if (component_callback_exists('mod_' . $mod->name, 'get_shortcuts')) {
|
||||
// TODO: MDL-68011 this block needs to be removed in 4.1.
|
||||
debugging('The callback get_shortcuts has been deprecated. Please use get_course_content_items and
|
||||
get_all_content_items instead. Some features of the activity chooser, such as favourites and recommendations
|
||||
are not supported when providing content items via the deprecated callback.');
|
||||
|
||||
// If get_shortcuts() callback is defined, the default module action is not added.
|
||||
// It is a responsibility of the callback to add it to the return value unless it is not needed.
|
||||
// The legacy hook, get_shortcuts, expects a stdClass representation of the core module content_item entry.
|
||||
$modcontentitemreference = $this->content_item_to_legacy_data($contentitem);
|
||||
|
||||
$legacyitems = component_callback($mod->name, 'get_shortcuts', [$modcontentitemreference], null);
|
||||
if (!is_null($legacyitems)) {
|
||||
foreach ($legacyitems as $legacyitem) {
|
||||
|
||||
$legacyitem = $this->legacy_item_inherit_missing($legacyitem, $contentitem);
|
||||
|
||||
// All items must have different links, use them as a key in the return array.
|
||||
// If plugin returned the only one item with the same link as default item - keep $modname,
|
||||
// otherwise append the link url to the module name.
|
||||
$legacyitem->name = (count($legacyitems) == 1 &&
|
||||
$legacyitem->link->out() === $contentitem->get_link()->out()) ? $mod->name : $mod->name . ':' .
|
||||
$legacyitem->link;
|
||||
|
||||
$plugincontentitem = $this->content_item_from_legacy_data($legacyitem);
|
||||
|
||||
$return[] = $plugincontentitem;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Neither callback was found, so just use the default module content item.
|
||||
// Callback was not found, so just use the default module content item.
|
||||
$return[] = $contentitem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,11 +247,11 @@ class stateactions {
|
||||
if (!empty($modinfo->sections[$section->section])) {
|
||||
foreach ($modinfo->sections[$section->section] as $modnumber) {
|
||||
$cm = $modinfo->cms[$modnumber];
|
||||
$updates->add_cm_delete($cm->id);
|
||||
$updates->add_cm_remove($cm->id);
|
||||
}
|
||||
}
|
||||
course_delete_section($course, $section, true, true);
|
||||
$updates->add_section_delete($sectionid);
|
||||
$updates->add_section_remove($sectionid);
|
||||
}
|
||||
|
||||
// Removing a section affects the full course structure.
|
||||
|
||||
@@ -128,9 +128,21 @@ class stateupdates implements JsonSerializable {
|
||||
/**
|
||||
* Add track about a section deleted.
|
||||
*
|
||||
* @deprecated since Moodle 4.1 MDL-74925 - please call add_section_remove() instead.
|
||||
* @param int $sectionid The affected section id.
|
||||
*/
|
||||
public function add_section_delete(int $sectionid): void {
|
||||
debugging('add_section_delete() is deprecated. Please use add_section_remove() instead.', DEBUG_DEVELOPER);
|
||||
|
||||
$this->add_update('section', 'remove', (object)['id' => $sectionid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add track about a section removed.
|
||||
*
|
||||
* @param int $sectionid The affected section id.
|
||||
*/
|
||||
public function add_section_remove(int $sectionid): void {
|
||||
$this->add_update('section', 'remove', (object)['id' => $sectionid]);
|
||||
}
|
||||
|
||||
@@ -178,9 +190,21 @@ class stateupdates implements JsonSerializable {
|
||||
/**
|
||||
* Add track about a course module deleted.
|
||||
*
|
||||
* @deprecated since Moodle 4.1 MDL-74925 - please call add_cm_remove() instead.
|
||||
* @param int $cmid the affected course module id
|
||||
*/
|
||||
public function add_cm_delete(int $cmid): void {
|
||||
debugging('add_cm_delete() is deprecated. Please use add_cm_remove() instead.', DEBUG_DEVELOPER);
|
||||
|
||||
$this->add_update('cm', 'remove', (object)['id' => $cmid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add track about a course module removed.
|
||||
*
|
||||
* @param int $cmid the affected course module id
|
||||
*/
|
||||
public function add_cm_remove(int $cmid): void {
|
||||
$this->add_update('cm', 'remove', (object)['id' => $cmid]);
|
||||
}
|
||||
|
||||
@@ -188,7 +212,7 @@ class stateupdates implements JsonSerializable {
|
||||
* Add a valid update message to the update list.
|
||||
*
|
||||
* @param string $name the update name
|
||||
* @param string $action the update action (usually update, create, delete)
|
||||
* @param string $action the update action (usually update, create, remove)
|
||||
* @param stdClass $fields the object fields
|
||||
*/
|
||||
protected function add_update(string $name, string $action, stdClass $fields): void {
|
||||
|
||||
@@ -378,7 +378,6 @@ class format_singleactivity extends core_courseformat\base {
|
||||
|
||||
/**
|
||||
* Checks if the activity type has multiple items in the activity chooser.
|
||||
* This may happen as a result of defining callback modulename_get_shortcuts().
|
||||
*
|
||||
* @return bool|null (null if the check is not possible)
|
||||
*/
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ class update_course_test extends \externallib_advanced_testcase {
|
||||
'action' => 'format_do_something',
|
||||
'expected' => [
|
||||
'count' => 1,
|
||||
'action' => 'delete',
|
||||
'action' => 'remove',
|
||||
'visible' => null,
|
||||
],
|
||||
'expectexception' => false,
|
||||
|
||||
@@ -65,7 +65,7 @@ class stateactions extends core_actions {
|
||||
?int $targetcmid = null
|
||||
): void {
|
||||
|
||||
$updates->add_cm_delete(array_pop($ids));
|
||||
$updates->add_cm_remove(array_pop($ids));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -143,7 +143,7 @@ class stateactions_test extends \advanced_testcase {
|
||||
'cm' => [],
|
||||
'count' => 0,
|
||||
],
|
||||
'delete' => [
|
||||
'remove' => [
|
||||
'course' => [],
|
||||
'section' => [],
|
||||
'cm' => [],
|
||||
@@ -236,10 +236,10 @@ class stateactions_test extends \advanced_testcase {
|
||||
// Format results in a way we can compare easily.
|
||||
$results = $this->summarize_updates($updates);
|
||||
|
||||
// The state actions does not use create or delete actions because they are designed
|
||||
// The state actions does not use create or remove actions because they are designed
|
||||
// to refresh parts of the state.
|
||||
$this->assertEquals(0, $results['create']['count']);
|
||||
$this->assertEquals(0, $results['delete']['count']);
|
||||
$this->assertEquals(0, $results['remove']['count']);
|
||||
|
||||
// Validate we have all the expected entries.
|
||||
$expectedtotal = count($expectedresults['course']) + count($expectedresults['section']) + count($expectedresults['cm']);
|
||||
|
||||
@@ -186,7 +186,7 @@ class stateupdates_test extends \advanced_testcase {
|
||||
$this->assertEquals($action, $update->action);
|
||||
$this->assertEquals('section', $update->name);
|
||||
// Delete does not provide all fields.
|
||||
if ($action == 'delete') {
|
||||
if ($action == 'remove') {
|
||||
$this->assertEquals($section->id, $update->fields->id);
|
||||
} else {
|
||||
$this->assertEquals($expected, $update->fields);
|
||||
@@ -203,7 +203,7 @@ class stateupdates_test extends \advanced_testcase {
|
||||
return array_merge(
|
||||
$this->add_section_provider_helper('put'),
|
||||
$this->add_section_provider_helper('create'),
|
||||
$this->add_section_provider_helper('delete'),
|
||||
$this->add_section_provider_helper('remove'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ class stateupdates_test extends \advanced_testcase {
|
||||
*/
|
||||
private function add_section_provider_helper(string $action): array {
|
||||
// Delete does not depends on user permissions.
|
||||
if ($action == 'delete') {
|
||||
if ($action == 'remove') {
|
||||
$studentsections = [0, 1, 2];
|
||||
} else {
|
||||
$studentsections = [0, 1];
|
||||
@@ -247,7 +247,7 @@ class stateupdates_test extends \advanced_testcase {
|
||||
* @dataProvider add_cm_provider
|
||||
* @covers ::add_cm_update
|
||||
* @covers ::add_cm_create
|
||||
* @covers ::add_cm_delete
|
||||
* @covers ::add_cm_remove
|
||||
*
|
||||
* @param string $action the action name
|
||||
* @param string $role the user role name
|
||||
@@ -330,7 +330,7 @@ class stateupdates_test extends \advanced_testcase {
|
||||
$this->assertEquals($action, $update->action);
|
||||
$this->assertEquals('cm', $update->name);
|
||||
// Delete does not provide all fields.
|
||||
if ($action == 'delete') {
|
||||
if ($action == 'remove') {
|
||||
$this->assertEquals($cm->id, $update->fields->id);
|
||||
} else {
|
||||
$this->assertEquals($expected, $update->fields);
|
||||
@@ -347,7 +347,7 @@ class stateupdates_test extends \advanced_testcase {
|
||||
return array_merge(
|
||||
$this->add_cm_provider_helper('put'),
|
||||
$this->add_cm_provider_helper('create'),
|
||||
$this->add_cm_provider_helper('delete'),
|
||||
$this->add_cm_provider_helper('remove'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ class stateupdates_test extends \advanced_testcase {
|
||||
*/
|
||||
private function add_cm_provider_helper(string $action): array {
|
||||
// Delete does not depends on user permissions.
|
||||
if ($action == 'delete') {
|
||||
if ($action == 'remove') {
|
||||
$studentcms = [0, 1, 2, 3];
|
||||
} else {
|
||||
$studentcms = [0];
|
||||
|
||||
@@ -2,6 +2,10 @@ This files describes API changes for course formats
|
||||
|
||||
Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
|
||||
|
||||
=== 4.1 ===
|
||||
* New \core_courseformat\stateupdates methods add_section_remove() and add_cm_remove() have been added to replace
|
||||
the deprecated methods add_section_delete() and add_cm_delete().
|
||||
|
||||
=== 4.0 ===
|
||||
* New core_courseformat\base::uses_course_index() to define whether the course format uses course index or not.
|
||||
* New core_courseformat\base::supports_components() to specify if the format is compatible with reactive components.
|
||||
|
||||
+14
-9
@@ -2470,9 +2470,7 @@ function update_course($data, $editoroptions = NULL) {
|
||||
function average_number_of_participants(bool $onlyactive = false, int $lastloginsince = null): float {
|
||||
global $DB;
|
||||
|
||||
$params = [
|
||||
'siteid' => SITEID,
|
||||
];
|
||||
$params = [];
|
||||
|
||||
$sql = "SELECT DISTINCT ue.userid, e.courseid
|
||||
FROM {user_enrolments} ue
|
||||
@@ -2483,8 +2481,7 @@ function average_number_of_participants(bool $onlyactive = false, int $lastlogin
|
||||
$sql .= "JOIN {user} u ON u.id = ue.userid ";
|
||||
}
|
||||
|
||||
$sql .= "WHERE e.courseid <> :siteid
|
||||
AND c.visible = 1 ";
|
||||
$sql .= "WHERE e.courseid <> " . SITEID . " AND c.visible = 1 ";
|
||||
|
||||
if ($onlyactive) {
|
||||
$sql .= "AND ue.status = :active
|
||||
@@ -4109,16 +4106,24 @@ function course_classify_for_timeline($course, $user = null, $completioninfo = n
|
||||
$user = $USER;
|
||||
}
|
||||
|
||||
if ($completioninfo == null) {
|
||||
$completioninfo = new completion_info($course);
|
||||
}
|
||||
|
||||
// Let plugins override data for timeline classification.
|
||||
$pluginsfunction = get_plugins_with_function('extend_course_classify_for_timeline', 'lib.php');
|
||||
foreach ($pluginsfunction as $plugintype => $plugins) {
|
||||
foreach ($plugins as $pluginfunction) {
|
||||
$pluginfunction($course, $user, $completioninfo);
|
||||
}
|
||||
}
|
||||
|
||||
$today = time();
|
||||
// End date past.
|
||||
if (!empty($course->enddate) && (course_classify_end_date($course) < $today)) {
|
||||
return COURSE_TIMELINE_PAST;
|
||||
}
|
||||
|
||||
if ($completioninfo == null) {
|
||||
$completioninfo = new completion_info($course);
|
||||
}
|
||||
|
||||
// Course was completed.
|
||||
if ($completioninfo->is_enabled() && $completioninfo->is_course_complete($user->id)) {
|
||||
return COURSE_TIMELINE_PAST;
|
||||
|
||||
+3
-10
@@ -134,17 +134,10 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the HTML for the module chooser javascript popup
|
||||
*
|
||||
* @param array $modules A set of modules as returned form @see
|
||||
* get_module_metadata
|
||||
* @param object $course The course that will be displayed
|
||||
* @return string The composed HTML for the module
|
||||
* @deprecated since 3.9
|
||||
*/
|
||||
public function course_modchooser($modules, $course) {
|
||||
debugging('course_modchooser() is deprecated. Please use course_activitychooser() instead.', DEBUG_DEVELOPER);
|
||||
|
||||
return $this->course_activitychooser($course->id);
|
||||
public function course_modchooser() {
|
||||
throw new coding_exception('course_modchooser() can not be used anymore, please use course_activitychooser() instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ if ($mform->is_cancelled()) {
|
||||
$line = array();
|
||||
$line[] = $item['component'];
|
||||
$line[] = $item['item'];
|
||||
$line[] = ($item['error']===false) ? get_string('ok') : '<div class="notifyproblem">'.$item['error'].'</div>';
|
||||
$line[] = ($item['error'] === false) ? get_string('statusok') : '<div class="notifyproblem">'.$item['error'].'</div>';
|
||||
$data[] = $line;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
This files describes API changes in /course/*,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 4.1 ===
|
||||
* The function course_modchooser() has been finally deprecated and can not be used anymore. Please use
|
||||
course_activitychooser() instead.
|
||||
|
||||
=== 4.0 ===
|
||||
* All activity icons have been replaced with black monochrome icons. The background
|
||||
colour for these icons is defined using a new 'FEATURE_MOD_PURPOSE' support variable in the module lib.php file
|
||||
|
||||
@@ -55,6 +55,8 @@ if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', cont
|
||||
$PAGE->set_course($course);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
$PAGE->set_url('/enrol/index.php', array('id'=>$course->id));
|
||||
$PAGE->set_secondary_navigation(false);
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
// do not allow enrols when in login-as session
|
||||
if (\core\session\manager::is_loggedinas() and $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
|
||||
|
||||
@@ -92,7 +92,8 @@ class published_resources_table extends \table_sql {
|
||||
* @return string
|
||||
*/
|
||||
public function col_name($tool) {
|
||||
$name = helper::get_name($tool);
|
||||
$toolcontext = \context::instance_by_id($tool->contextid, IGNORE_MISSING);
|
||||
$name = $toolcontext ? helper::get_name($tool) : $this->get_deleted_activity_name_html($tool);
|
||||
|
||||
return $this->get_display_text($tool, $name);
|
||||
}
|
||||
@@ -215,9 +216,39 @@ class published_resources_table extends \table_sql {
|
||||
*/
|
||||
protected function get_display_text($tool, $text) {
|
||||
if ($tool->status != ENROL_INSTANCE_ENABLED) {
|
||||
return \html_writer::tag('span', $text, array('class' => 'dimmed_text'));
|
||||
return \html_writer::tag('div', $text, array('class' => 'dimmed_text'));
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a warning icon, with tooltip, describing enrolment instances sharing activities which have been deleted.
|
||||
*
|
||||
* @param \stdClass $tool the tool instance record.
|
||||
* @return string the HTML for the name column.
|
||||
*/
|
||||
protected function get_deleted_activity_name_html(\stdClass $tool): string {
|
||||
global $OUTPUT;
|
||||
$icon = \html_writer::tag(
|
||||
'a',
|
||||
$OUTPUT->pix_icon('enrolinstancewarning', get_string('deletedactivityalt' , 'enrol_lti'), 'enrol_lti'), [
|
||||
"class" => "btn btn-link p-0",
|
||||
"role" => "button",
|
||||
"data-container" => "body",
|
||||
"data-toggle" => "popover",
|
||||
"data-placement" => right_to_left() ? "left" : "right",
|
||||
"data-content" => get_string('deletedactivitydescription', 'enrol_lti'),
|
||||
"data-html" => "true",
|
||||
"tabindex" => "0",
|
||||
"data-trigger" => "focus"
|
||||
]
|
||||
);
|
||||
$name = \html_writer::span($icon . get_string('deletedactivity', 'enrol_lti'));
|
||||
if ($tool->name) {
|
||||
$name .= \html_writer::empty_tag('br') . \html_writer::empty_tag('br') . $tool->name;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,8 @@ class manage_table extends \table_sql {
|
||||
* @return string
|
||||
*/
|
||||
public function col_name($tool) {
|
||||
$name = helper::get_name($tool);
|
||||
$toolcontext = \context::instance_by_id($tool->contextid, IGNORE_MISSING);
|
||||
$name = $toolcontext ? helper::get_name($tool) : $this->get_deleted_activity_name_html($tool);
|
||||
|
||||
return $this->get_display_text($tool, $name);
|
||||
}
|
||||
@@ -247,9 +248,39 @@ class manage_table extends \table_sql {
|
||||
*/
|
||||
protected function get_display_text($tool, $text) {
|
||||
if ($tool->status != ENROL_INSTANCE_ENABLED) {
|
||||
return \html_writer::tag('span', $text, array('class' => 'dimmed_text'));
|
||||
return \html_writer::tag('div', $text, array('class' => 'dimmed_text'));
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a warning icon, with tooltip, describing enrolment instances sharing activities which have been deleted.
|
||||
*
|
||||
* @param \stdClass $tool the tool instance record.
|
||||
* @return string the HTML for the name column.
|
||||
*/
|
||||
protected function get_deleted_activity_name_html(\stdClass $tool): string {
|
||||
global $OUTPUT;
|
||||
$icon = \html_writer::tag(
|
||||
'a',
|
||||
$OUTPUT->pix_icon('enrolinstancewarning', get_string('deletedactivityalt' , 'enrol_lti'), 'enrol_lti'), [
|
||||
"class" => "btn btn-link p-0",
|
||||
"role" => "button",
|
||||
"data-container" => "body",
|
||||
"data-toggle" => "popover",
|
||||
"data-placement" => right_to_left() ? "left" : "right",
|
||||
"data-content" => get_string('deletedactivitydescription', 'enrol_lti'),
|
||||
"data-html" => "true",
|
||||
"tabindex" => "0",
|
||||
"data-trigger" => "focus"
|
||||
]
|
||||
);
|
||||
$name = \html_writer::span($icon . get_string('deletedactivity', 'enrol_lti'));
|
||||
if ($tool->name) {
|
||||
$name .= \html_writer::empty_tag('br') . \html_writer::empty_tag('br') . $tool->name;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,5 +450,17 @@ function xmldb_enrol_lti_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2022061500) {
|
||||
// Disable all orphaned enrolment method instances.
|
||||
$sql = "id IN (SELECT t.enrolid
|
||||
FROM {enrol_lti_tools} t
|
||||
LEFT JOIN {context} c ON (t.contextid = c.id)
|
||||
WHERE c.id IS NULL)";
|
||||
$DB->set_field_select('enrol', 'status', 1, $sql);
|
||||
|
||||
// Lti savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2022061500, 'enrol', 'lti');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ $string['copytoclipboard'] = 'Copy to clipboard';
|
||||
$string['couldnotestablishproxy'] = 'Could not establish proxy with consumer.';
|
||||
$string['customproperties'] = 'Custom properties';
|
||||
$string['deeplinkingurl'] = 'Deep linking URL';
|
||||
$string['deletedactivity'] = 'Deleted activity';
|
||||
$string['deletedactivityalt'] = 'The activity shared by this instance has been deleted';
|
||||
$string['deletedactivitydescription'] = 'The activity shared by this instance has been deleted. You can select another activity to share by editing the instance or, if no longer required, you can simply delete the instance. Deleting the instance will remove any associated user enrolments.';
|
||||
$string['deploymentadd'] = 'Add a deployment';
|
||||
$string['deploymentaddnotice'] = 'Deployment added';
|
||||
$string['deploymentdelete'] = 'Delete deployment';
|
||||
|
||||
+17
-1
@@ -475,6 +475,22 @@ function enrol_lti_extend_navigation_course($navigation, $course, $context) {
|
||||
function enrol_lti_get_fontawesome_icon_map() {
|
||||
return [
|
||||
'enrol_lti:managedeployments' => 'fa-sitemap',
|
||||
'enrol_lti:platformdetails' => 'fa-pencil-square-o'
|
||||
'enrol_lti:platformdetails' => 'fa-pencil-square-o',
|
||||
'enrol_lti:enrolinstancewarning' => 'fa-exclamation-circle text-danger',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-delete course module hook which disables any methods referring to the deleted module, preventing launches and allowing remap.
|
||||
*
|
||||
* @param stdClass $cm The deleted course module record.
|
||||
*/
|
||||
function enrol_lti_pre_course_module_delete(stdClass $cm) {
|
||||
global $DB;
|
||||
$sql = "id IN (SELECT t.enrolid
|
||||
FROM {enrol_lti_tools} t
|
||||
JOIN {context} c ON (t.contextid = c.id)
|
||||
WHERE c.contextlevel = :contextlevel
|
||||
AND c.instanceid = :cmid)";
|
||||
$DB->set_field_select('enrol', 'status', ENROL_INSTANCE_DISABLED, $sql, ['contextlevel' => CONTEXT_MODULE, 'cmid' => $cm->id]);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 339 B |
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet"><path d="M896 128q209 0 385.5 103t279.5 279.5 103 385.5-103 385.5-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103zm128 1247v-190q0-14-9-23.5t-22-9.5h-192q-13 0-23 10t-10 23v190q0 13 10 23t23 10h192q13 0 22-9.5t9-23.5zm-2-344l18-621q0-12-10-18-10-8-24-8h-220q-14 0-24 8-10 6-10 18l17 621q0 10 10 17.5t24 7.5h185q14 0 23.5-7.5t10.5-17.5z" fill="#999"/></svg>
|
||||
|
After Width: | Height: | Size: 560 B |
@@ -216,4 +216,36 @@ class lib_test extends \lti_advantage_testcase {
|
||||
// LTI enrolment has 1 enrol actions for active users -- unenrol.
|
||||
$this->assertCount(1, $actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of an enrolment method when the activity to which it provides access is deleted.
|
||||
*
|
||||
* @covers \enrol_lti_pre_course_module_delete
|
||||
*/
|
||||
public function test_course_module_deletion() {
|
||||
// Create two modules and publish them.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$mod = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$mod2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$tooldata = [
|
||||
'cmid' => $mod->cmid,
|
||||
'courseid' => $course->id,
|
||||
];
|
||||
$tool = $this->getDataGenerator()->create_lti_tool((object)$tooldata);
|
||||
$tooldata['cmid'] = $mod2->cmid;
|
||||
$tool2 = $this->getDataGenerator()->create_lti_tool((object)$tooldata);
|
||||
|
||||
// Verify the instances are both enabled.
|
||||
$modinstance = helper::get_lti_tool($tool->id);
|
||||
$mod2instance = helper::get_lti_tool($tool2->id);
|
||||
$this->assertEquals(ENROL_INSTANCE_ENABLED, $modinstance->status);
|
||||
$this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status);
|
||||
|
||||
// Delete a module and verify the associated instance is disabled.
|
||||
course_delete_module($mod->cmid);
|
||||
$modinstance = helper::get_lti_tool($tool->id);
|
||||
$mod2instance = helper::get_lti_tool($tool2->id);
|
||||
$this->assertEquals(ENROL_INSTANCE_DISABLED, $modinstance->status);
|
||||
$this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,15 +229,16 @@ class resource_link_test extends \advanced_testcase {
|
||||
* @covers ::add_user
|
||||
*/
|
||||
public function test_add_user() {
|
||||
global $CFG;
|
||||
$reslinkwithid = resource_link::create('res-link-id-123', 24, 44, 66, 33);
|
||||
$user = $reslinkwithid->add_user(2, 'platform-user-id-123', 'en', 'Sydney', 'AU', 'Test university', '99');
|
||||
$user = $reslinkwithid->add_user(2, 'platform-user-id-123', $CFG->lang, 'Sydney', 'AU', 'Test university', '99');
|
||||
$this->assertInstanceOf(user::class, $user);
|
||||
$this->assertEquals(33, $user->get_resourcelinkid());
|
||||
|
||||
$reslinkwithoutid = resource_link::create('res-link-id-123', 24, 44);
|
||||
$this->expectException(\coding_exception::class);
|
||||
$this->expectExceptionMessage("Can't add user to a resource_link that hasn't first been saved");
|
||||
$reslinkwithoutid->add_user(2, 'platform-user-id-123', 'en', 'Sydney', 'Australia', 'Test university', '99');
|
||||
$reslinkwithoutid->add_user(2, 'platform-user-id-123', $CFG->lang, 'Sydney', 'Australia', 'Test university', '99');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,6 +65,7 @@ class user_test extends \advanced_testcase {
|
||||
* @return array the data for testing.
|
||||
*/
|
||||
public function create_data_provider(): array {
|
||||
global $CFG;
|
||||
return [
|
||||
'Valid create, only required args provided' => [
|
||||
'args' => [
|
||||
@@ -72,7 +73,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99'
|
||||
],
|
||||
'expectations' => [
|
||||
@@ -80,7 +81,7 @@ class user_test extends \advanced_testcase {
|
||||
'resourceid' => 22,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => '',
|
||||
'country' => '',
|
||||
@@ -99,7 +100,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'AU',
|
||||
@@ -115,7 +116,7 @@ class user_test extends \advanced_testcase {
|
||||
'resourceid' => 22,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'AU',
|
||||
@@ -134,7 +135,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'AU',
|
||||
@@ -151,7 +152,7 @@ class user_test extends \advanced_testcase {
|
||||
'resourceid' => 22,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'AU',
|
||||
@@ -164,19 +165,19 @@ class user_test extends \advanced_testcase {
|
||||
'id' => null
|
||||
]
|
||||
],
|
||||
'Invalid create, lang with bad value (fr not installed)' => [
|
||||
'Invalid create, lang with bad value (vvvv not installed)' => [
|
||||
'args' => [
|
||||
'resourceid' => 22,
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'fr',
|
||||
'lang' => 'vvvv',
|
||||
'timezone' => '99',
|
||||
],
|
||||
'expectations' => [
|
||||
'valid' => false,
|
||||
'exception' => \coding_exception::class,
|
||||
'exceptionmessage' => "Invalid lang 'fr' provided."
|
||||
'exceptionmessage' => "Invalid lang 'vvvv' provided."
|
||||
]
|
||||
],
|
||||
'Invalid create, timezone with bad value' => [
|
||||
@@ -185,7 +186,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => 'NOT/FOUND',
|
||||
],
|
||||
'expectations' => [
|
||||
@@ -200,7 +201,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => '',
|
||||
'country' => 'FFF',
|
||||
@@ -217,7 +218,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => '',
|
||||
'country' => '',
|
||||
@@ -272,6 +273,7 @@ class user_test extends \advanced_testcase {
|
||||
* @return array the data for testing.
|
||||
*/
|
||||
public function create_from_resource_link_data_provider(): array {
|
||||
global $CFG;
|
||||
return [
|
||||
'Valid creation, all args provided explicitly' => [
|
||||
'args' => [
|
||||
@@ -280,7 +282,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'AU',
|
||||
@@ -295,7 +297,7 @@ class user_test extends \advanced_testcase {
|
||||
'resourceid' => 22,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'AU',
|
||||
@@ -312,7 +314,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => 'UTC'
|
||||
],
|
||||
'expectations' => [
|
||||
@@ -323,7 +325,7 @@ class user_test extends \advanced_testcase {
|
||||
'resourceid' => 22,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => 'UTC',
|
||||
'city' => '',
|
||||
'country' => '',
|
||||
@@ -340,7 +342,7 @@ class user_test extends \advanced_testcase {
|
||||
'user' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => '',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => 'UTC'
|
||||
],
|
||||
'expectations' => [
|
||||
@@ -372,7 +374,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => ''
|
||||
],
|
||||
'expectations' => [
|
||||
@@ -381,20 +383,20 @@ class user_test extends \advanced_testcase {
|
||||
'exceptionmessage' => 'Invalid timezone value. Cannot be an empty string.'
|
||||
]
|
||||
],
|
||||
'Invalid creation, only required args provided, invalid lang (fr not installed)' => [
|
||||
'Invalid creation, only required args provided, invalid lang (vvvv not installed)' => [
|
||||
'args' => [
|
||||
'resourcelinkid' => 11,
|
||||
'resourceid' => 22,
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'fr',
|
||||
'lang' => 'vvvv',
|
||||
'timezone' => 'UTC'
|
||||
],
|
||||
'expectations' => [
|
||||
'valid' => false,
|
||||
'exception' => \coding_exception::class,
|
||||
'exceptionmessage' => "Invalid lang 'fr' provided."
|
||||
'exceptionmessage' => "Invalid lang 'vvvv' provided."
|
||||
]
|
||||
],
|
||||
'Invalid creation, only required args provided, invalid timezone' => [
|
||||
@@ -404,7 +406,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => 'NOT/FOUND'
|
||||
],
|
||||
'expectations' => [
|
||||
@@ -420,7 +422,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'FFF',
|
||||
@@ -440,7 +442,7 @@ class user_test extends \advanced_testcase {
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => '99',
|
||||
'city' => 'Melbourne',
|
||||
'country' => 'AU',
|
||||
@@ -462,13 +464,14 @@ class user_test extends \advanced_testcase {
|
||||
* @return user a user instance.
|
||||
*/
|
||||
protected function create_test_user(): user {
|
||||
global $CFG;
|
||||
$args = [
|
||||
'resourcelinkid' => 11,
|
||||
'resourceid' => 22,
|
||||
'userid' => 2,
|
||||
'deploymentid' => 33,
|
||||
'sourceid' => 'user-id-123',
|
||||
'lang' => 'en',
|
||||
'lang' => $CFG->lang,
|
||||
'timezone' => 'UTC'
|
||||
];
|
||||
return user::create_from_resource_link(...array_values($args));
|
||||
@@ -509,6 +512,7 @@ class user_test extends \advanced_testcase {
|
||||
* @return array the array of test data.
|
||||
*/
|
||||
public function setters_getters_data_provider(): array {
|
||||
global $CFG;
|
||||
return [
|
||||
'Testing set_resourcelinkid with valid id' => [
|
||||
'methodname' => 'resourcelinkid',
|
||||
@@ -641,7 +645,7 @@ class user_test extends \advanced_testcase {
|
||||
],
|
||||
'Testing set_lang with valid lang code' => [
|
||||
'methodname' => 'lang',
|
||||
'arg' => 'en',
|
||||
'arg' => $CFG->lang,
|
||||
'expectations' => [
|
||||
'valid' => true,
|
||||
]
|
||||
|
||||
@@ -197,6 +197,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
* @covers ::find_by_resource_and_user
|
||||
*/
|
||||
public function test_find_by_resource_and_user() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$resourcelink = $this->generate_resource_link();
|
||||
$repository = new resource_link_repository();
|
||||
@@ -214,7 +215,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
$user = $newreslink->add_user(
|
||||
$user1->id,
|
||||
'platform-user-id-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'Sydney',
|
||||
'AU',
|
||||
'Test university',
|
||||
@@ -227,7 +228,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
$user2 = $newreslink3->add_user(
|
||||
$user2->id,
|
||||
'platform-user-id-777',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'Melbourne',
|
||||
'AU',
|
||||
'Test university',
|
||||
@@ -255,6 +256,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
* @covers ::delete
|
||||
*/
|
||||
public function test_delete() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$resourcelink = $this->generate_resource_link();
|
||||
$repository = new resource_link_repository();
|
||||
@@ -265,7 +267,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
$user = $newreslink->add_user(
|
||||
2,
|
||||
'source-id-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'Perth',
|
||||
'AU',
|
||||
'An Example Institution',
|
||||
@@ -293,6 +295,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
* @covers ::delete_by_resource
|
||||
*/
|
||||
public function test_delete_by_resource() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$resourcelink = $this->generate_resource_link();
|
||||
$repository = new resource_link_repository();
|
||||
@@ -302,7 +305,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
$user = $newreslink->add_user(
|
||||
2,
|
||||
'source-id-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'Perth',
|
||||
'AU',
|
||||
'An Example Institution',
|
||||
@@ -337,6 +340,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
* @covers ::delete_by_deployment
|
||||
*/
|
||||
public function test_delete_by_deployment() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$resourcelink = $this->generate_resource_link();
|
||||
$repository = new resource_link_repository();
|
||||
@@ -347,7 +351,7 @@ class resource_link_repository_test extends \advanced_testcase {
|
||||
$user = $newreslink->add_user(
|
||||
2,
|
||||
'source-id-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'Perth',
|
||||
'AU',
|
||||
'An Example Institution',
|
||||
|
||||
@@ -34,6 +34,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
* @return user a user instance
|
||||
*/
|
||||
protected function generate_user(int $mockresourceid = 1): user {
|
||||
global $CFG;
|
||||
$registration = application_registration::create(
|
||||
'Test',
|
||||
'a2c94a2c94',
|
||||
@@ -66,7 +67,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
$ltiuser = $savedresourcelink->add_user(
|
||||
$user->id,
|
||||
'source-id-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'Perth',
|
||||
'AU',
|
||||
'An Example Institution',
|
||||
@@ -215,6 +216,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
* @covers ::save
|
||||
*/
|
||||
public function test_save_stale_id() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$instructoruser = $this->getDataGenerator()->create_user();
|
||||
$userrepo = new user_repository();
|
||||
@@ -223,7 +225,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
$instructoruser->id,
|
||||
5,
|
||||
'source-id-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'99',
|
||||
'',
|
||||
'',
|
||||
@@ -280,6 +282,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
* @covers ::find_by_resource
|
||||
*/
|
||||
public function test_find_by_resource() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$user = $this->generate_user();
|
||||
$userrepo = new user_repository();
|
||||
@@ -291,7 +294,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
$instructoruser->id,
|
||||
$saveduser->get_deploymentid(),
|
||||
'another-user-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'99',
|
||||
'Perth',
|
||||
'AU',
|
||||
@@ -314,6 +317,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
* @covers ::find_by_resource_link
|
||||
*/
|
||||
public function test_find_by_resource_link() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$user = $this->generate_user();
|
||||
$user->set_resourcelinkid(33);
|
||||
@@ -326,7 +330,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
$instructoruser->id,
|
||||
$saveduser->get_deploymentid(),
|
||||
'another-user-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'99',
|
||||
'Perth',
|
||||
'AU',
|
||||
@@ -390,6 +394,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
* @covers ::delete_by_deployment
|
||||
*/
|
||||
public function test_delete_by_deployment() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$user = $this->generate_user();
|
||||
$userrepo = new user_repository();
|
||||
@@ -402,7 +407,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
$instructoruser->id,
|
||||
$saveduser->get_deploymentid(),
|
||||
'another-user-123',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'99',
|
||||
'Perth',
|
||||
'AU',
|
||||
@@ -415,7 +420,7 @@ class user_repository_test extends \advanced_testcase {
|
||||
$instructor2user->id,
|
||||
$saveduser->get_deploymentid() + 1,
|
||||
'another-user-678',
|
||||
'en',
|
||||
$CFG->lang,
|
||||
'99',
|
||||
'Melbourne',
|
||||
'AU',
|
||||
|
||||
@@ -412,6 +412,35 @@ class sync_grades_test extends \lti_advantage_testcase {
|
||||
$task->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test syncing grades when the enrolment instance is disabled.
|
||||
*
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_sync_grades_disabled_instance() {
|
||||
$this->resetAfterTest();
|
||||
global $DB;
|
||||
|
||||
[$course, $resource, $resource2, $resource3] = $this->create_test_environment();
|
||||
|
||||
// Disable resource 1.
|
||||
$enrol = (object) ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_DISABLED];
|
||||
$DB->update_record('enrol', $enrol);
|
||||
|
||||
// Delete the activity being shared by resource 2, leaving resource 2 disabled as a result.
|
||||
$modcontext = \context::instance_by_id($resource2->contextid);
|
||||
course_delete_module($modcontext->instanceid);
|
||||
|
||||
// Only the enabled resource 3 should sync grades.
|
||||
$task = $this->get_task_with_mocked_grade_service();
|
||||
$this->expectOutputRegex(
|
||||
"/^Starting - LTI Advantage grade sync for shared resource '$resource3->id' in course '$course->id'.\n".
|
||||
"Completed - Synced grades for tool '$resource3->id' in the course '$course->id'. Processed 0 users; ".
|
||||
"sent 0 grades.\n$/"
|
||||
);
|
||||
$task->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the grade sync when the context has been deleted in between launch and when the grade sync task is run.
|
||||
*
|
||||
@@ -419,6 +448,8 @@ class sync_grades_test extends \lti_advantage_testcase {
|
||||
*/
|
||||
public function test_sync_grades_deleted_context() {
|
||||
$this->resetAfterTest();
|
||||
global $DB;
|
||||
|
||||
[$course, $resource] = $this->create_test_environment();
|
||||
$launchservice = $this->get_tool_launch_service();
|
||||
|
||||
@@ -427,10 +458,11 @@ class sync_grades_test extends \lti_advantage_testcase {
|
||||
$instructoruser = $this->getDataGenerator()->create_user();
|
||||
[$userid] = $launchservice->user_launches_tool($instructoruser, $teachermocklaunch);
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
// Delete the activity, then enable the enrolment method (it is disabled during activity deletion).
|
||||
$modcontext = \context::instance_by_id($resource->contextid);
|
||||
course_delete_module($modcontext->instanceid);
|
||||
$enrol = (object) ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_ENABLED];
|
||||
$DB->update_record('enrol', $enrol);
|
||||
|
||||
$task = $this->get_task_with_mocked_grade_service();
|
||||
$this->expectOutputRegex(
|
||||
|
||||
@@ -609,6 +609,38 @@ class sync_members_test extends \lti_advantage_testcase {
|
||||
$this->assertCount(1, $userrepo->find_by_resource($resource->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test syncing members when the enrolment instance is disabled.
|
||||
*
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_sync_members_disabled_instance() {
|
||||
$this->resetAfterTest();
|
||||
global $DB;
|
||||
|
||||
[$course, $resource, $resource2, $resource3] = $this->create_test_environment();
|
||||
$userrepo = new user_repository();
|
||||
|
||||
// Disable resource 1.
|
||||
$enrol = (object) ['id' => $resource->enrolid, 'status' => ENROL_INSTANCE_DISABLED];
|
||||
$DB->update_record('enrol', $enrol);
|
||||
|
||||
// Delete the activity being shared by resource2, leaving resource 2 disabled as a result.
|
||||
$modcontext = \context::instance_by_id($resource2->contextid);
|
||||
course_delete_module($modcontext->instanceid);
|
||||
|
||||
// Only the enabled resource 3 should sync members.
|
||||
$task = $this->get_mock_task_with_users($this->get_mock_members_with_ids(range(1, 1)));
|
||||
$task->execute();
|
||||
|
||||
$this->expectOutputRegex(
|
||||
"/^Starting - Member sync for published resource '$resource3->id' for course '$course->id'.\n".
|
||||
"Completed - Synced members for tool '$resource3->id' in the course '$course->id'. Processed 0 users; ".
|
||||
"enrolled 0 members; unenrolled 0 members.\n$/"
|
||||
);
|
||||
$this->assertCount(0, $userrepo->find_by_resource($resource->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test syncing members for a membersync-enabled resource when the launch omits the NRPS service endpoints.
|
||||
*
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
<library>
|
||||
<location>ims-blti</location>
|
||||
<name>IMS-BLTI</name>
|
||||
<description>Set of IMS-LTI tools originaly provided by IMS.</description>
|
||||
<version>1.0</version>
|
||||
<license>MIT</license>
|
||||
<version></version>
|
||||
<licenseversion></licenseversion>
|
||||
<repository>https://github.com/jfederico/ims-dev</repository>
|
||||
<customised/>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2022041900; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2022061500; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2022041200; // Requires this Moodle version.
|
||||
$plugin->component = 'enrol_lti'; // Full name of the plugin (used for diagnostics).
|
||||
$plugin->dependencies = [
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
<library>
|
||||
<location>AlgParser.pm</location>
|
||||
<name>WeBWorK</name>
|
||||
<license>GPL</license>
|
||||
<version>2.4.9+</version>
|
||||
<license>GPL</license>
|
||||
<licenseversion>2.0+</licenseversion>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
<library>
|
||||
<location>mimetex.*</location>
|
||||
<name>mimeTeX</name>
|
||||
<license>GPL</license>
|
||||
<description>Compiled C program to convert TeX into GIFs</description>
|
||||
<version>1.77</version>
|
||||
<license>GPL</license>
|
||||
<licenseversion>3.0+</licenseversion>
|
||||
<repository>https://github.com/icaoberg/mimetex</repository>
|
||||
<copyrights>
|
||||
<copyright>2002-2004 John Forkosh Associates, Inc</copyright>
|
||||
</copyrights>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,13 +3,25 @@
|
||||
<library>
|
||||
<location>joubel/core</location>
|
||||
<name>h5p-php-library</name>
|
||||
<license>GPL-3.0</license>
|
||||
<description>The general H5P library.</description>
|
||||
<version>1.24.3</version>
|
||||
<license>GPL</license>
|
||||
<licenseversion>3.0+</licenseversion>
|
||||
<repository>https://github.com/h5p/h5p-php-library/</repository>
|
||||
<copyrights>
|
||||
<copyright>Joubel</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>joubel/editor</location>
|
||||
<name>h5p-editor-php-library</name>
|
||||
<license>GPL-3.0</license>
|
||||
<description>A general library that is supposed to be used in most PHP implementations of H5P.</description>
|
||||
<version>moodle-1.20.2</version>
|
||||
<license>GPL</license>
|
||||
<licenseversion>3.0+</licenseversion>
|
||||
<repository>https://github.com/h5p/h5p-editor-php-library/</repository>
|
||||
<copyrights>
|
||||
<copyright>Joubel</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -31,4 +31,4 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = 'de';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Deutsch (Du)';
|
||||
$string['thislanguage'] = 'Deutsch (du)';
|
||||
|
||||
@@ -91,6 +91,7 @@ $string['favourite'] = 'Starred course';
|
||||
$string['gradetopassnotset'] = 'This course does not have a grade to pass set. It may be set in the grade item of the course (Gradebook setup).';
|
||||
$string['informationformodule'] = 'Information about the {$a} activity';
|
||||
$string['module'] = 'Activity';
|
||||
$string['namewithlink'] = 'Category name with link';
|
||||
$string['nocourseactivity'] = 'Not enough course activity between the start and the end of the course';
|
||||
$string['nocourseendtime'] = 'The course does not have an end time';
|
||||
$string['nocoursesections'] = 'No course sections';
|
||||
|
||||
@@ -255,6 +255,7 @@ $string['sorting'] = 'Sorting';
|
||||
$string['sorting_help'] = 'Sorting defines the initial sort order of columns in the report. The order can be reversed by toggling the Up/down icon. Users can then define their own sort order by clicking on a column name.';
|
||||
$string['switchedit'] = 'Switch to edit mode';
|
||||
$string['switchpreview'] = 'Switch to preview mode';
|
||||
$string['tasksendschedule'] = 'Send report schedule';
|
||||
$string['tasksendschedules'] = 'Send report schedules';
|
||||
$string['timeadded'] = 'Time added';
|
||||
$string['timecreated'] = 'Time created';
|
||||
|
||||
+1
-1
@@ -5,6 +5,6 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @module core/chart_output_chartjs
|
||||
*/
|
||||
define("core/chart_output_chartjs",["jquery","core/chartjs","core/chart_axis","core/chart_bar","core/chart_output_base","core/chart_line","core/chart_pie","core/chart_series"],(function($,Chartjs,Axis,Bar,Base,Line,Pie,Series){var makeAxisId=function(xy,index){return"axis-"+xy+"-"+index};function Output(){Base.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=$("<canvas>"),this._node.append(this._canvas)),this._build()}return Output.prototype=Object.create(Base.prototype),Output.prototype._config=null,Output.prototype._chartjs=null,Output.prototype._canvas=null,Output.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new Chartjs(this._canvas[0],this._config)},Output.prototype._cleanData=function(data){return data instanceof Array?data.map((function(value){return $("<span>").html(value).text()})):$("<span>").html(data).text()},Output.prototype._getChartType=function(){var type=this._chart.getType();return this._chart.getType()===Bar.prototype.TYPE&&!0===this._chart.getHorizontal()?type="horizontalBar":this._chart.getType()===Pie.prototype.TYPE&&!0===this._chart.getDoughnut()&&(type="doughnut"),type},Output.prototype._makeAxisConfig=function(axis,xy,index){var scaleData={id:makeAxisId(xy,index)};return axis.getPosition()!==Axis.prototype.POS_DEFAULT&&(scaleData.position=axis.getPosition()),null!==axis.getLabel()&&(scaleData.scaleLabel={display:!0,labelString:this._cleanData(axis.getLabel())}),null!==axis.getStepSize()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.stepSize=axis.getStepSize()),null!==axis.getMax()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.max=axis.getMax()),null!==axis.getMin()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.min=axis.getMin()),scaleData},Output.prototype._makeConfig=function(){var config={type:this._getChartType(),data:{labels:this._cleanData(this._chart.getLabels()),datasets:this._makeDatasetsConfig()},options:{title:{display:null!==this._chart.getTitle(),text:this._cleanData(this._chart.getTitle())}}},legendOptions=this._chart.getLegendOptions();return legendOptions&&(config.options.legend=legendOptions),this._chart.getXAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.xAxes=config.options.scales.xAxes||[],config.options.scales.xAxes[i]=this._makeAxisConfig(axis,"x",i),null!==axisLabels&&(config.options.scales.xAxes[i].ticks.callback=function(value,index){return axisLabels[index]||""}),config.options.scales.xAxes[i].stacked=this._isStacked()}.bind(this)),this._chart.getYAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.yAxes=config.options.scales.yAxes||[],config.options.scales.yAxes[i]=this._makeAxisConfig(axis,"y",i),null!==axisLabels&&(config.options.scales.yAxes[i].ticks.callback=function(value){return axisLabels[parseInt(value,10)]||""}),config.options.scales.yAxes[i].stacked=this._isStacked()}.bind(this)),config.options.tooltips={callbacks:{label:this._makeTooltip.bind(this)}},config},Output.prototype._makeDatasetsConfig=function(){return this._chart.getSeries().map(function(series){var colors=series.hasColoredValues()?series.getColors():series.getColor(),dataset={label:this._cleanData(series.getLabel()),data:series.getValues(),type:series.getType(),fill:series.getFill(),backgroundColor:colors,borderColor:this._chart.getType()==Pie.prototype.TYPE?"#fff":colors,lineTension:this._isSmooth(series)?.3:0};return null!==series.getXAxis()&&(dataset.xAxisID=makeAxisId("x",series.getXAxis())),null!==series.getYAxis()&&(dataset.yAxisID=makeAxisId("y",series.getYAxis())),dataset}.bind(this))},Output.prototype._makeTooltip=function(tooltipItem,data){var series=this._chart.getSeries()[tooltipItem.datasetIndex],serieLabel=series.getLabel(),serieLabels=series.getLabels(),tooltipData=data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index],tooltip=[];if(""==tooltipItem.xLabel&&""==tooltipItem.yLabel){var chartLabels=this._cleanData(this._chart.getLabels());tooltip.push(chartLabels[tooltipItem.index])}return null!==serieLabels?tooltip.push(this._cleanData(serieLabels[tooltipItem.index])):tooltip.push(this._cleanData(serieLabel)+": "+tooltipData),tooltip},Output.prototype._isSmooth=function(series){var smooth=!1;return this._chart.getType()===Line.prototype.TYPE?null===(smooth=series.getSmooth())&&(smooth=this._chart.getSmooth()):series.getType()===Series.prototype.TYPE_LINE&&(smooth=series.getSmooth()),smooth},Output.prototype._isStacked=function(){var stacked=!1;return this._chart.getType()===Bar.prototype.TYPE&&(stacked=this._chart.getStacked()),stacked},Output.prototype.update=function(){$.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},Output}));
|
||||
define("core/chart_output_chartjs",["jquery","core/chartjs","core/chart_axis","core/chart_bar","core/chart_output_base","core/chart_line","core/chart_pie","core/chart_series"],(function($,Chartjs,Axis,Bar,Base,Line,Pie,Series){var makeAxisId=function(xy,index){return"axis-"+xy+"-"+index};function Output(){Base.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=$("<canvas>"),this._node.append(this._canvas)),this._build()}return Output.prototype=Object.create(Base.prototype),Output.prototype._config=null,Output.prototype._chartjs=null,Output.prototype._canvas=null,Output.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new Chartjs(this._canvas[0],this._config)},Output.prototype._cleanData=function(data){return data instanceof Array?data.map((function(value){return $("<span>").html(value).text()})):$("<span>").html(data).text()},Output.prototype._getChartType=function(){var type=this._chart.getType();return this._chart.getType()===Bar.prototype.TYPE&&!0===this._chart.getHorizontal()?type="horizontalBar":this._chart.getType()===Pie.prototype.TYPE&&!0===this._chart.getDoughnut()&&(type="doughnut"),type},Output.prototype._makeAxisConfig=function(axis,xy,index){var scaleData={id:makeAxisId(xy,index)};return axis.getPosition()!==Axis.prototype.POS_DEFAULT&&(scaleData.position=axis.getPosition()),null!==axis.getLabel()&&(scaleData.title={display:!0,text:this._cleanData(axis.getLabel())}),null!==axis.getStepSize()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.stepSize=axis.getStepSize()),null!==axis.getMax()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.max=axis.getMax()),null!==axis.getMin()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.min=axis.getMin()),scaleData},Output.prototype._makeConfig=function(){var charType=this._getChartType(),config={type:charType,data:{labels:this._cleanData(this._chart.getLabels()),datasets:this._makeDatasetsConfig()},options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:null!==this._chart.getTitle(),text:this._cleanData(this._chart.getTitle())}}}};"horizontalBar"===charType&&(config.type="bar",config.options.indexAxis="y");var legendOptions=this._chart.getLegendOptions();return legendOptions&&(config.options.plugins.legend=legendOptions),this._chart.getXAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.x=config.options.scales.x||{},config.options.scales.x[i]=this._makeAxisConfig(axis,"x",i),null!==axisLabels&&(config.options.scales.x[i].ticks.callback=function(value,index){return axisLabels[index]||""}),config.options.scales.x.stacked=this._isStacked()}.bind(this)),this._chart.getYAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.y=config.options.scales.yAxes||{},config.options.scales.y[i]=this._makeAxisConfig(axis,"y",i),null!==axisLabels&&(config.options.scales.y[i].ticks.callback=function(value){return axisLabels[parseInt(value,10)]||""}),config.options.scales.y.stacked=this._isStacked()}.bind(this)),config.options.plugins.tooltip={callbacks:{label:this._makeTooltip.bind(this)}},config},Output.prototype._makeDatasetsConfig=function(){return this._chart.getSeries().map(function(series){var colors=series.hasColoredValues()?series.getColors():series.getColor(),dataset={label:this._cleanData(series.getLabel()),data:series.getValues(),type:series.getType(),fill:series.getFill(),backgroundColor:colors,borderColor:this._chart.getType()==Pie.prototype.TYPE?"#fff":colors,tension:this._isSmooth(series)?.3:0};return null!==series.getXAxis()&&(dataset.xAxisID=makeAxisId("x",series.getXAxis())),null!==series.getYAxis()&&(dataset.yAxisID=makeAxisId("y",series.getYAxis())),dataset}.bind(this))},Output.prototype._makeTooltip=function(tooltipItem){var serieLabel=this._chart.getSeries()[tooltipItem.datasetIndex].getLabel(),tooltipData=tooltipItem.dataset.data[tooltipItem.dataIndex],tooltip=[];if(this._chart.getType()===Pie.prototype.TYPE){var chartLabels=this._cleanData(this._chart.getLabels());tooltip.push(chartLabels[tooltipItem.dataIndex]+" - "+this._cleanData(serieLabel)+": "+tooltipData)}else tooltip.push(this._cleanData(serieLabel)+": "+tooltipData);return tooltip},Output.prototype._isSmooth=function(series){var smooth=!1;return this._chart.getType()===Line.prototype.TYPE?null===(smooth=series.getSmooth())&&(smooth=this._chart.getSmooth()):series.getType()===Series.prototype.TYPE_LINE&&(smooth=series.getSmooth()),smooth},Output.prototype._isStacked=function(){var stacked=!1;return this._chart.getType()===Bar.prototype.TYPE&&(stacked=this._chart.getStacked()),stacked},Output.prototype.update=function(){$.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},Output}));
|
||||
|
||||
//# sourceMappingURL=chart_output_chartjs.min.js.map
|
||||
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -156,9 +156,9 @@ define([
|
||||
}
|
||||
|
||||
if (axis.getLabel() !== null) {
|
||||
scaleData.scaleLabel = {
|
||||
scaleData.title = {
|
||||
display: true,
|
||||
labelString: this._cleanData(axis.getLabel())
|
||||
text: this._cleanData(axis.getLabel())
|
||||
};
|
||||
}
|
||||
|
||||
@@ -187,22 +187,33 @@ define([
|
||||
* @return {Object} The axis config.
|
||||
*/
|
||||
Output.prototype._makeConfig = function() {
|
||||
var charType = this._getChartType();
|
||||
var config = {
|
||||
type: this._getChartType(),
|
||||
type: charType,
|
||||
data: {
|
||||
labels: this._cleanData(this._chart.getLabels()),
|
||||
datasets: this._makeDatasetsConfig()
|
||||
},
|
||||
options: {
|
||||
title: {
|
||||
display: this._chart.getTitle() !== null,
|
||||
text: this._cleanData(this._chart.getTitle())
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: this._chart.getTitle() !== null,
|
||||
text: this._cleanData(this._chart.getTitle())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (charType === 'horizontalBar') {
|
||||
config.type = 'bar';
|
||||
config.options.indexAxis = 'y';
|
||||
}
|
||||
|
||||
var legendOptions = this._chart.getLegendOptions();
|
||||
if (legendOptions) {
|
||||
config.options.legend = legendOptions;
|
||||
config.options.plugins.legend = legendOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -210,33 +221,33 @@ define([
|
||||
var axisLabels = axis.getLabels();
|
||||
|
||||
config.options.scales = config.options.scales || {};
|
||||
config.options.scales.xAxes = config.options.scales.xAxes || [];
|
||||
config.options.scales.xAxes[i] = this._makeAxisConfig(axis, 'x', i);
|
||||
config.options.scales.x = config.options.scales.x || {};
|
||||
config.options.scales.x[i] = this._makeAxisConfig(axis, 'x', i);
|
||||
|
||||
if (axisLabels !== null) {
|
||||
config.options.scales.xAxes[i].ticks.callback = function(value, index) {
|
||||
config.options.scales.x[i].ticks.callback = function(value, index) {
|
||||
return axisLabels[index] || '';
|
||||
};
|
||||
}
|
||||
config.options.scales.xAxes[i].stacked = this._isStacked();
|
||||
config.options.scales.x.stacked = this._isStacked();
|
||||
}.bind(this));
|
||||
|
||||
this._chart.getYAxes().forEach(function(axis, i) {
|
||||
var axisLabels = axis.getLabels();
|
||||
|
||||
config.options.scales = config.options.scales || {};
|
||||
config.options.scales.yAxes = config.options.scales.yAxes || [];
|
||||
config.options.scales.yAxes[i] = this._makeAxisConfig(axis, 'y', i);
|
||||
config.options.scales.y = config.options.scales.yAxes || {};
|
||||
config.options.scales.y[i] = this._makeAxisConfig(axis, 'y', i);
|
||||
|
||||
if (axisLabels !== null) {
|
||||
config.options.scales.yAxes[i].ticks.callback = function(value) {
|
||||
config.options.scales.y[i].ticks.callback = function(value) {
|
||||
return axisLabels[parseInt(value, 10)] || '';
|
||||
};
|
||||
}
|
||||
config.options.scales.yAxes[i].stacked = this._isStacked();
|
||||
config.options.scales.y.stacked = this._isStacked();
|
||||
}.bind(this));
|
||||
|
||||
config.options.tooltips = {
|
||||
config.options.plugins.tooltip = {
|
||||
callbacks: {
|
||||
label: this._makeTooltip.bind(this)
|
||||
}
|
||||
@@ -262,7 +273,7 @@ define([
|
||||
backgroundColor: colors,
|
||||
// Pie charts look better without borders.
|
||||
borderColor: this._chart.getType() == Pie.prototype.TYPE ? '#fff' : colors,
|
||||
lineTension: this._isSmooth(series) ? 0.3 : 0
|
||||
tension: this._isSmooth(series) ? 0.3 : 0
|
||||
};
|
||||
|
||||
if (series.getXAxis() !== null) {
|
||||
@@ -280,32 +291,25 @@ define([
|
||||
/**
|
||||
* Get the chart data, add labels and rebuild the tooltip.
|
||||
*
|
||||
* @param {Object[]} tooltipItem The tooltip item data.
|
||||
* @param {Object[]} data The chart data.
|
||||
* @returns {String}
|
||||
* @param {Object[]} tooltipItem The tooltip item object.
|
||||
* @returns {*[]}
|
||||
* @protected
|
||||
*/
|
||||
Output.prototype._makeTooltip = function(tooltipItem, data) {
|
||||
Output.prototype._makeTooltip = function(tooltipItem) {
|
||||
|
||||
// Get series and chart data to rebuild the tooltip and add labels.
|
||||
var series = this._chart.getSeries()[tooltipItem.datasetIndex];
|
||||
var serieLabel = series.getLabel();
|
||||
var serieLabels = series.getLabels();
|
||||
var chartData = data.datasets[tooltipItem.datasetIndex].data;
|
||||
var tooltipData = chartData[tooltipItem.index];
|
||||
var chartData = tooltipItem.dataset.data;
|
||||
var tooltipData = chartData[tooltipItem.dataIndex];
|
||||
|
||||
// Build default tooltip.
|
||||
var tooltip = [];
|
||||
|
||||
// Pie and doughnut charts does not have axis.
|
||||
if (tooltipItem.xLabel == '' && tooltipItem.yLabel == '') {
|
||||
// Pie and doughnut charts tooltip are different.
|
||||
if (this._chart.getType() === Pie.prototype.TYPE) {
|
||||
var chartLabels = this._cleanData(this._chart.getLabels());
|
||||
tooltip.push(chartLabels[tooltipItem.index]);
|
||||
}
|
||||
|
||||
// Add series labels to the tooltip if any.
|
||||
if (serieLabels !== null) {
|
||||
tooltip.push(this._cleanData(serieLabels[tooltipItem.index]));
|
||||
tooltip.push(chartLabels[tooltipItem.dataIndex] + ' - ' + this._cleanData(serieLabel) + ': ' + tooltipData);
|
||||
} else {
|
||||
tooltip.push(this._cleanData(serieLabel) + ': ' + tooltipData);
|
||||
}
|
||||
|
||||
+13184
-20575
File diff suppressed because it is too large
Load Diff
@@ -55,8 +55,9 @@ class mustache_shorten_text_helper {
|
||||
$length = trim($length);
|
||||
$text = trim($text);
|
||||
|
||||
// Allow mustache tags in the text.
|
||||
// Allow mustache tags in the length and text.
|
||||
$text = $helper->render($text);
|
||||
$length = $helper->render($length);
|
||||
|
||||
return shorten_text($text, $length);
|
||||
}
|
||||
|
||||
@@ -1320,7 +1320,7 @@ class core_plugin_manager {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ok = get_string('ok', 'core');
|
||||
$ok = get_string('statusok', 'core');
|
||||
|
||||
// Let admins know they can expect more verbose output.
|
||||
$silent or $this->mtrace(get_string('packagesdebug', 'core_plugin'), PHP_EOL, DEBUG_NORMAL);
|
||||
|
||||
@@ -46,6 +46,20 @@ abstract class adhoc_task extends task_base {
|
||||
/** @var \core\lock\lock The concurrency task lock for this task. */
|
||||
private $concurrencylock = null;
|
||||
|
||||
/**
|
||||
* Provide default implementation of the task name for backward compatibility. Extending classes are expected to implement
|
||||
* this method to provide a descriptive name for the task (shown to admins)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
$classparts = explode('\\', get_called_class());
|
||||
$classname = end($classparts);
|
||||
|
||||
// Try to make human readable, capitalized and with spaces.
|
||||
return ucfirst(str_replace('_', ' ', $classname));
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for $id.
|
||||
* @param int|null $id
|
||||
|
||||
@@ -562,12 +562,4 @@ abstract class scheduled_task extends task_base {
|
||||
public static function get_html_id(string $classname): string {
|
||||
return str_replace('\\', '-', ltrim($classname, '\\'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a descriptive name for this task (shown to admins).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
}
|
||||
|
||||
@@ -62,6 +62,13 @@ abstract class task_base {
|
||||
/** @var int $pid - PHP process ID that is running the task */
|
||||
private $pid = null;
|
||||
|
||||
/**
|
||||
* Get a descriptive name for the task (shown to admins)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
/**
|
||||
* Set the current lock for this task.
|
||||
* @param \core\lock\lock $lock
|
||||
|
||||
@@ -2781,6 +2781,12 @@ $functions = array(
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
],
|
||||
'core_reportbuilder_set_filters' => [
|
||||
'classname' => 'core_reportbuilder\external\filters\set',
|
||||
'description' => 'Set filter values for given report',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
],
|
||||
'core_dynamic_tabs_get_content' => [
|
||||
'classname' => 'core\external\dynamic_tabs_get_content',
|
||||
'description' => 'Returns the content for a dynamic tab',
|
||||
@@ -2806,6 +2812,12 @@ $functions = array(
|
||||
'type' => 'read',
|
||||
'ajax' => true,
|
||||
],
|
||||
'core_reportbuilder_view_report' => [
|
||||
'classname' => 'core_reportbuilder\external\reports\view',
|
||||
'description' => 'Trigger custom report viewed',
|
||||
'type' => 'write',
|
||||
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
|
||||
],
|
||||
'core_reportbuilder_columns_add' => [
|
||||
'classname' => 'core_reportbuilder\external\columns\add',
|
||||
'description' => 'Add column to report',
|
||||
|
||||
+4
-95
@@ -2770,102 +2770,11 @@ function report_insights_context_insights(\context $context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all metadata for the requested modules
|
||||
*
|
||||
* @deprecated since 3.9.
|
||||
* @param object $course The Course
|
||||
* @param array $modnames An array containing the list of modules and their
|
||||
* names
|
||||
* @param int $sectionreturn The section to return to
|
||||
* @return array A list of stdClass objects containing metadata about each
|
||||
* module
|
||||
* @deprecated since 3.9
|
||||
*/
|
||||
function get_module_metadata($course, $modnames, $sectionreturn = null) {
|
||||
global $OUTPUT;
|
||||
|
||||
debugging('get_module_metadata is deprecated. Please use \core_course\local\service\content_item_service instead.');
|
||||
|
||||
// get_module_metadata will be called once per section on the page and courses may show
|
||||
// different modules to one another
|
||||
static $modlist = array();
|
||||
if (!isset($modlist[$course->id])) {
|
||||
$modlist[$course->id] = array();
|
||||
}
|
||||
|
||||
$return = array();
|
||||
$urlbase = new moodle_url('/course/mod.php', array('id' => $course->id, 'sesskey' => sesskey()));
|
||||
if ($sectionreturn !== null) {
|
||||
$urlbase->param('sr', $sectionreturn);
|
||||
}
|
||||
foreach($modnames as $modname => $modnamestr) {
|
||||
if (!course_allowed_module($course, $modname)) {
|
||||
continue;
|
||||
}
|
||||
if (isset($modlist[$course->id][$modname])) {
|
||||
// This module is already cached
|
||||
$return += $modlist[$course->id][$modname];
|
||||
continue;
|
||||
}
|
||||
$modlist[$course->id][$modname] = array();
|
||||
|
||||
// Create an object for a default representation of this module type in the activity chooser. It will be used
|
||||
// if module does not implement callback get_shortcuts() and it will also be passed to the callback if it exists.
|
||||
$defaultmodule = new stdClass();
|
||||
$defaultmodule->title = $modnamestr;
|
||||
$defaultmodule->name = $modname;
|
||||
$defaultmodule->link = new moodle_url($urlbase, array('add' => $modname));
|
||||
$defaultmodule->icon = $OUTPUT->pix_icon('icon', '', $defaultmodule->name, array('class' => 'icon'));
|
||||
$sm = get_string_manager();
|
||||
if ($sm->string_exists('modulename_help', $modname)) {
|
||||
$defaultmodule->help = get_string('modulename_help', $modname);
|
||||
if ($sm->string_exists('modulename_link', $modname)) { // Link to further info in Moodle docs.
|
||||
$link = get_string('modulename_link', $modname);
|
||||
$linktext = get_string('morehelp');
|
||||
$defaultmodule->help .= html_writer::tag('div',
|
||||
$OUTPUT->doc_link($link, $linktext, true), array('class' => 'helpdoclink'));
|
||||
}
|
||||
}
|
||||
$defaultmodule->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
|
||||
|
||||
// Each module can implement callback modulename_get_shortcuts() in its lib.php and return the list
|
||||
// of elements to be added to activity chooser.
|
||||
$items = component_callback($modname, 'get_shortcuts', array($defaultmodule), null);
|
||||
if ($items !== null) {
|
||||
foreach ($items as $item) {
|
||||
// Add all items to the return array. All items must have different links, use them as a key in the return array.
|
||||
if (!isset($item->archetype)) {
|
||||
$item->archetype = $defaultmodule->archetype;
|
||||
}
|
||||
if (!isset($item->icon)) {
|
||||
$item->icon = $defaultmodule->icon;
|
||||
}
|
||||
// If plugin returned the only one item with the same link as default item - cache it as $modname,
|
||||
// otherwise append the link url to the module name.
|
||||
$item->name = (count($items) == 1 &&
|
||||
$item->link->out() === $defaultmodule->link->out()) ? $modname : $modname . ':' . $item->link;
|
||||
|
||||
// If the module provides the helptext property, append it to the help text to match the look and feel
|
||||
// of the default course modules.
|
||||
if (isset($item->help) && isset($item->helplink)) {
|
||||
$linktext = get_string('morehelp');
|
||||
$item->help .= html_writer::tag('div',
|
||||
$OUTPUT->doc_link($item->helplink, $linktext, true), array('class' => 'helpdoclink'));
|
||||
}
|
||||
$modlist[$course->id][$modname][$item->name] = $item;
|
||||
}
|
||||
$return += $modlist[$course->id][$modname];
|
||||
// If get_shortcuts() callback is defined, the default module action is not added.
|
||||
// It is a responsibility of the callback to add it to the return value unless it is not needed.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The callback get_shortcuts() was not found, use the default item for the activity chooser.
|
||||
$modlist[$course->id][$modname][$modname] = $defaultmodule;
|
||||
$return[$modname] = $defaultmodule;
|
||||
}
|
||||
|
||||
core_collator::asort_objects_by_property($return, 'title');
|
||||
return $return;
|
||||
function get_module_metadata() {
|
||||
throw new coding_exception(
|
||||
'get_module_metadata() has been removed. Please use \core_course\local\service\content_item_service instead.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,15 +3,20 @@
|
||||
<library>
|
||||
<location>yui/src/codemirror</location>
|
||||
<name>codemirror</name>
|
||||
<license>MIT</license>
|
||||
<description>CodeMirror is a versatile text editor implemented in JavaScript for the browser.</description>
|
||||
<version>5.65.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/codemirror/CodeMirror</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>yui/src/beautify</location>
|
||||
<name>jsbeautify</name>
|
||||
<license>MIT</license>
|
||||
<description>Beautify HTML code in Atto.</description>
|
||||
<version>1.14.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/beautify-web/js-beautify</repository>
|
||||
<copyrights>
|
||||
<copyright>2007-2018 Einar Lielmanis, Liam Newman, and contributors.</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
<library>
|
||||
<location>yui/src/rangy/js/*.*</location>
|
||||
<name>Rangy</name>
|
||||
<license>MIT</license>
|
||||
<description>A cross-browser JavaScript range and selection library.</description>
|
||||
<version>1.3.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/timdown/rangy</repository>
|
||||
<copyrights>
|
||||
<copyright>2014 Tim Down</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
<library>
|
||||
<location>tinymce</location>
|
||||
<name>PDW Toolbar Toggle</name>
|
||||
<license>MIT</license>
|
||||
<description>A plugin for the TinyMCE WYSIWYG editor. The plugin shows and hides the toolbars just like the Kitchen Sink plugin in Wordpress.</description>
|
||||
<version>1.2</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/GuidoNeele/PDW-Toggle-Toolbars-TinyMCE</repository>
|
||||
<copyrights>
|
||||
<copyright>Guido Neele (www.neele.name)</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
<library>
|
||||
<location>rpc.php</location>
|
||||
<name>TinyMCE spellchecker</name>
|
||||
<license>LGPL</license>
|
||||
<description>TinyMCE - Spellchecker for PHP.</description>
|
||||
<version>2.0.6.1</version>
|
||||
<license>LGPL</license>
|
||||
<licenseversion>2.1+</licenseversion>
|
||||
<repository>https://github.com/tinymce/tinymce_spellchecker_php</repository>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
<library>
|
||||
<location>tiny_mce</location>
|
||||
<name>TinyMCE</name>
|
||||
<license>LGPL</license>
|
||||
<description>The JavaScript Rich Text editor.</description>
|
||||
<version>3.5.11</version>
|
||||
<license>LGPL</license>
|
||||
<licenseversion>2.1+</licenseversion>
|
||||
<repository>https://github.com/tinymce/tinymce</repository>
|
||||
<copyrights>
|
||||
<copyright>2004-2012, Moxiecode Systems AB, All rights reserved.</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
+4
-4
@@ -688,8 +688,8 @@ function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $coursei
|
||||
$orderby = "ORDER BY $sort";
|
||||
}
|
||||
|
||||
$wheres = array("c.id <> :siteid");
|
||||
$params = array('siteid'=>SITEID);
|
||||
$wheres = ['c.id <> ' . SITEID];
|
||||
$params = [];
|
||||
|
||||
if (isset($USER->loginascontext) and $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
|
||||
// list _only_ this course - anything else is asking for trouble...
|
||||
@@ -1093,7 +1093,7 @@ function enrol_get_all_users_courses($userid, $onlyactive = false, $fields = nul
|
||||
$orderby = "ORDER BY $sort";
|
||||
}
|
||||
|
||||
$params = array('siteid'=>SITEID);
|
||||
$params = [];
|
||||
|
||||
if ($onlyactive) {
|
||||
$subwhere = "WHERE ue.status = :active AND e.status = :enabled AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)";
|
||||
@@ -1119,7 +1119,7 @@ function enrol_get_all_users_courses($userid, $onlyactive = false, $fields = nul
|
||||
$subwhere
|
||||
) en ON (en.courseid = c.id)
|
||||
$ccjoin
|
||||
WHERE c.id <> :siteid
|
||||
WHERE c.id <> " . SITEID . "
|
||||
$orderby";
|
||||
$params['userid'] = $userid;
|
||||
|
||||
|
||||
+2
-2
@@ -4201,14 +4201,14 @@ class curl {
|
||||
$crlf = "\r\n";
|
||||
return preg_replace(
|
||||
// HTTP version and status code (ignore value of code).
|
||||
'~^HTTP/1\..*' . $crlf .
|
||||
'~^HTTP/[1-9](\.[0-9])?.*' . $crlf .
|
||||
// Header name: character between 33 and 126 decimal, except colon.
|
||||
// Colon. Header value: any character except \r and \n. CRLF.
|
||||
'(?:[\x21-\x39\x3b-\x7e]+:[^' . $crlf . ']+' . $crlf . ')*' .
|
||||
// Headers are terminated by another CRLF (blank line).
|
||||
$crlf .
|
||||
// Second HTTP status code, this time must be 200.
|
||||
'(HTTP/1.[01] 200 )~', '$1', $input);
|
||||
'(HTTP/[1-9](\.[0-9])? 200)~', '$2', $input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -272,7 +272,7 @@ export const isAnyWatchedFormDirty = () => {
|
||||
// Handle TinyMCE editor instances.
|
||||
// TinyMCE forms may not have been initialised at the time that startWatching is called.
|
||||
// Check whether any tinyMCE editor is dirty.
|
||||
if (typeof window.tinyMCE !== 'undefined') {
|
||||
if (typeof window.tinyMCE !== 'undefined' && window.tinyMCE.editors) {
|
||||
if (window.tinyMCE.editors.some(editor => editor.isDirty())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
<library>
|
||||
<location>phpml</location>
|
||||
<name>PHP-ML</name>
|
||||
<license>MIT</license>
|
||||
<description>Machine learning library used to generate data for the Analytics system.</description>
|
||||
<version>0.9.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://gitlab.com/php-ai/php-ml</repository>
|
||||
<copyrights>
|
||||
<copyright>2016-2020 Arkadiusz Kondas arkadiusz.kondas[at]gmail</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -721,9 +721,6 @@ function question_move_questions_to_category($questionids, $newcategoryid): bool
|
||||
question_bank::get_qtype($question->qtype)->move_files(
|
||||
$question->id, $question->contextid, $newcategorydata->contextid);
|
||||
}
|
||||
// Move set_reference records to new category.
|
||||
move_question_set_references($question->category, $newcategoryid,
|
||||
$question->contextid, $newcategorydata->contextid, true);
|
||||
// Check whether there could be a clash of idnumbers in the new category.
|
||||
list($idnumberclash, $rec) = idnumber_exist_in_question_category($question->idnumber, $newcategoryid);
|
||||
if ($idnumberclash) {
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
{{/icon}}
|
||||
</span>
|
||||
<span class="typename">{{label}}</span>
|
||||
<span class="typesummary">
|
||||
{{{description}}}
|
||||
</span>
|
||||
</label>
|
||||
<div class="typesummary">
|
||||
{{{description}}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
<input type="hidden" name="logintoken" value="{{logintoken}}">
|
||||
<input type="hidden" name="username" value="guest" />
|
||||
<input type="hidden" name="password" value="guest" />
|
||||
<button class="btn btn-secondary" type="submit">{{#str}}loginguest{{/str}}</button>
|
||||
<button class="btn btn-secondary" type="submit" id="loginguestbtn">{{#str}}loginguest{{/str}}</button>
|
||||
</form>
|
||||
{{/canloginasguest}}
|
||||
<div class="login-divider"></div>
|
||||
@@ -213,4 +213,8 @@
|
||||
{{#error}}
|
||||
document.getElementById('loginerrormessage').focus();
|
||||
{{/error}}
|
||||
require(['core_form/submit'], function(Submit) {
|
||||
Submit.init("loginbtn");
|
||||
Submit.init("loginguestbtn");
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
{{/params}}
|
||||
<button type="submit" class="btn {{#primary}}btn-primary{{/primary}}{{^primary}}btn-secondary{{/primary}}"
|
||||
id="{{id}}"
|
||||
title="{{tooltip}}"
|
||||
{{#tooltip}}title="{{tooltip}}"{{/tooltip}}
|
||||
{{#disabled}}disabled{{/disabled}}
|
||||
{{#attributes}} {{name}}="{{value}}" {{/attributes}}>{{label}}</button>
|
||||
</form>
|
||||
|
||||
@@ -38,6 +38,26 @@ require_once(__DIR__ . '/fixtures/task_fixtures.php');
|
||||
*/
|
||||
class core_adhoc_task_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test getting name of task that implements it's own get_name method
|
||||
*
|
||||
* @covers \core\task\adhoc_task::get_name
|
||||
*/
|
||||
public function test_get_name(): void {
|
||||
$task = new \core\task\adhoc_test_task();
|
||||
$this->assertEquals('Test adhoc class', $task->get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting name of task that uses the default implementation of get_name
|
||||
*
|
||||
* @covers \core\task\adhoc_task::get_name
|
||||
*/
|
||||
public function test_get_name_default(): void {
|
||||
$task = new \mod_fake\task\adhoc_component_task();
|
||||
$this->assertEquals('Adhoc component task', $task->get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic adhoc task execution.
|
||||
*/
|
||||
|
||||
@@ -976,6 +976,155 @@ EOF;
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample));
|
||||
// Test it does nothing to the 'plain' data.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
|
||||
|
||||
$httpsexample = <<<EOF
|
||||
HTTP/1.0 200 Connection established
|
||||
|
||||
HTTP/2 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
$httpsexpected = <<<EOF
|
||||
HTTP/2 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
// For HTTP, replace the \n with \r\n.
|
||||
$httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample);
|
||||
$httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected);
|
||||
|
||||
// Test stripping works OK.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample));
|
||||
// Test it does nothing to the 'plain' data.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
|
||||
|
||||
$httpsexample = <<<EOF
|
||||
HTTP/1.0 200 Connection established
|
||||
|
||||
HTTP/2.1 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
$httpsexpected = <<<EOF
|
||||
HTTP/2.1 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
// For HTTP, replace the \n with \r\n.
|
||||
$httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample);
|
||||
$httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected);
|
||||
|
||||
// Test stripping works OK.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample));
|
||||
// Test it does nothing to the 'plain' data.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
|
||||
|
||||
$httpsexample = <<<EOF
|
||||
HTTP/1.1 200 Connection established
|
||||
|
||||
HTTP/3 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
$httpsexpected = <<<EOF
|
||||
HTTP/3 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
// For HTTP, replace the \n with \r\n.
|
||||
$httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample);
|
||||
$httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected);
|
||||
|
||||
// Test stripping works OK.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample));
|
||||
// Test it does nothing to the 'plain' data.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
|
||||
|
||||
$httpsexample = <<<EOF
|
||||
HTTP/2 200 Connection established
|
||||
|
||||
HTTP/4 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
$httpsexpected = <<<EOF
|
||||
HTTP/4 200 OK
|
||||
Date: Fri, 22 Feb 2013 17:14:23 GMT
|
||||
Server: Apache/2
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze14
|
||||
Content-Type: text/xml
|
||||
Connection: close
|
||||
Content-Encoding: gzip
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<rss version="2.0">...
|
||||
EOF;
|
||||
// For HTTP, replace the \n with \r\n.
|
||||
$httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample);
|
||||
$httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected);
|
||||
|
||||
// Test stripping works OK.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample));
|
||||
// Test it does nothing to the 'plain' data.
|
||||
$this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+9
@@ -51,6 +51,15 @@ class adhoc_test_task extends \core\task\adhoc_task {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'Test adhoc class';
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute.
|
||||
*/
|
||||
|
||||
+326
-68
@@ -3,357 +3,615 @@
|
||||
<library>
|
||||
<location>adodb</location>
|
||||
<name>AdoDB</name>
|
||||
<license>BSD/LGPL</license>
|
||||
<description>Database abstraction library for MySQL, PostgreSQL, MSSQL, Oracle, Interbase, Foxpro, Access, ADO, Sybase, DB2 and ODBC.</description>
|
||||
<version>5.22.2</version>
|
||||
<license>BSD/LGPL</license>
|
||||
<licenseversion>3-Clause/2.1+</licenseversion>
|
||||
<repository>https://github.com/ADOdb/ADOdb</repository>
|
||||
<copyrights>
|
||||
<copyright>2000-2013 John Lim (jlim#natsoft.com). All rights reserved.</copyright>
|
||||
<copyright>2014 Damien Regad, Mark Newnham and the ADOdb community</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>behat/axe</location>
|
||||
<name>axe-core</name>
|
||||
<license>MPL</license>
|
||||
<description>Accessibility testing engine for websites and other HTML-based user interfaces.</description>
|
||||
<version>4.3.5</version>
|
||||
<license>MPL</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/dequelabs/axe-core</repository>
|
||||
<copyrights>
|
||||
<copyright>2005, Jon Papaioannou</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>bennu</location>
|
||||
<name>Bennu</name>
|
||||
<license>LGPL</license>
|
||||
<description>Bennu is an object-oriented library written in PHP that implements the iCalendar standard (RFC 2445).</description>
|
||||
<version>0.1-custom</version>
|
||||
<license>LGPL</license>
|
||||
<licenseversion>2.1+</licenseversion>
|
||||
<repository>https://sourceforge.net/projects/bennu/</repository>
|
||||
<copyrights>
|
||||
<copyright>2005, Jon Papaioannou</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>evalmath</location>
|
||||
<name>EvalMath</name>
|
||||
<description>Class to safely evaluate math expressions.</description>
|
||||
<version>1.0.0</version>
|
||||
<license>BSD</license>
|
||||
<version></version>
|
||||
<licenseversion></licenseversion>
|
||||
<repository>https://github.com/dbojdo/eval-math</repository>
|
||||
<copyrights>
|
||||
<copyright>Miles Kaufmann</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>phpspreadsheet</location>
|
||||
<name>PhpSpreadsheet</name>
|
||||
<license>MIT</license>
|
||||
<description>Library to read, write and create spreadsheet documents in PHP.</description>
|
||||
<version>1.21.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/PHPOffice/PhpSpreadsheet</repository>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>google</location>
|
||||
<name>Google APIs Client Library</name>
|
||||
<license>Apache</license>
|
||||
<name>Google APIs</name>
|
||||
<description>Library Google APIs Client Library for PHP</description>
|
||||
<version>1.1.7</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/googleapis/google-api-php-client</repository>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>htmlpurifier</location>
|
||||
<name>HTML Purifier</name>
|
||||
<license>LGPL</license>
|
||||
<description>Filters HTML.</description>
|
||||
<version>4.13.0</version>
|
||||
<license>LGPL</license>
|
||||
<licenseversion>2.1+</licenseversion>
|
||||
<repository>https://github.com/ezyang/htmlpurifier</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>minify/matthiasmullie-minify</location>
|
||||
<name>MatthiasMullie\Minify</name>
|
||||
<license>MIT</license>
|
||||
<description>CSS & JavaScript minifier, in PHP</description>
|
||||
<version>1.3.66</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/matthiasmullie/minify</repository>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>minify/matthiasmullie-pathconverter</location>
|
||||
<name>MatthiasMullie\PathConverter</name>
|
||||
<license>MIT</license>
|
||||
<description>CSS path converter, in PHP</description>
|
||||
<version>1.1.3</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/matthiasmullie/path-converter</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>pear/HTML/Common.php</location>
|
||||
<name>Pear_HTML_Common</name>
|
||||
<license>PHP</license>
|
||||
<description>Class with many common HTML functions (used by HTML Quickform)</description>
|
||||
<version>1.7</version>
|
||||
<license>PHP</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>http://pear.php.net/package/HTML_Common</repository>
|
||||
<copyrights>
|
||||
<copyright>2004 Adam Daniel, Bertrand Mansion, Klaus Guenther, Alexey Borzov</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
<library>
|
||||
<location>pear/HTML/QuickForm.php</location>
|
||||
<name>Pear_HTML_QuickForm.php</name>
|
||||
<license>PHP</license>
|
||||
<description>Class to write forms</description>
|
||||
<version>2.0</version>
|
||||
<license>PHP</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://pear.php.net/package/HTML_Quickform</repository>
|
||||
<copyrights>
|
||||
<copyright>2004 Bertrand Mansion, Adam Daniel, Alexey Borzov</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
<library>
|
||||
<location>pear/HTML/QuickForm</location>
|
||||
<name>Pear_HTML_QuickForm</name>
|
||||
<license>PHP</license>
|
||||
<description>Class to write forms</description>
|
||||
<version>2.0</version>
|
||||
<license>PHP</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://pear.php.net/package/HTML_Quickform</repository>
|
||||
<copyrights>
|
||||
<copyright>2004 Bertrand Mansion, Adam Daniel, Alexey Borzov</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
<library>
|
||||
<location>pear/PEAR.php</location>
|
||||
<name>Pear.php</name>
|
||||
<license>PHP</license>
|
||||
<description>Base class for other PEAR classes.</description>
|
||||
<version>1.4.5</version>
|
||||
<license>PHP</license>
|
||||
<licenseversion>3.0</licenseversion>
|
||||
<repository>https://pear.php.net/package/PEAR</repository>
|
||||
<copyrights>
|
||||
<copyright>2001-2006 The PHP Group. All rights reserved</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
<library>
|
||||
<location>phpmailer</location>
|
||||
<name>PHPMailer</name>
|
||||
<license>LGPL</license>
|
||||
<description>Class for sending email using either sendmail, PHP mail(), or SMTP. Methods are based upon the standard AspEmail(tm) classes.</description>
|
||||
<version>6.5.3</version>
|
||||
<license>LGPL</license>
|
||||
<licenseversion>2.1</licenseversion>
|
||||
<repository>https://github.com/PHPMailer/PHPMailer</repository>
|
||||
<copyrights>
|
||||
<copyright>2003 Brent R. Matzelle (bmatzelle AT yahoo DOT com)</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>simplepie</location>
|
||||
<name>SimplePie</name>
|
||||
<license>BSD</license>
|
||||
<description>Simple Pie helps with blogs.</description>
|
||||
<version>1.5.6</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>BSD</license>
|
||||
<repository>https://github.com/simplepie/simplepie</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>tcpdf</location>
|
||||
<name>TCPDF</name>
|
||||
<license>LGPL</license>
|
||||
<description>Class to generate PDF files</description>
|
||||
<version>6.4.1</version>
|
||||
<license>LGPL</license>
|
||||
<licenseversion>3.0-only</licenseversion>
|
||||
<repository>https://github.com/tecnickcom/TCPDF</repository>
|
||||
<copyrights>
|
||||
<copyright>Olivier PLATHEY</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>yuilib</location>
|
||||
<name>YUI</name>
|
||||
<license>BSD</license>
|
||||
<description>The Yahoo! User Interface Library is a set of utilities and controls, in JavaScript, for building richly interactive web applications using techniques such as DOM scripting, DHTML and AJAX. The YUI Library also includes several core CSS resources.Set of user-interface components using AJAX, DHTML etc. We use it for all our AJAX-related stuff.</description>
|
||||
<version>3.17.2</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>BSD</license>
|
||||
<repository>https://github.com/yui/yui3</repository>
|
||||
<copyrights>
|
||||
<copyright>2006, Yahoo! Inc.</copyright>
|
||||
</copyrights>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
<library>
|
||||
<location>yuilib/gallery</location>
|
||||
<name>YUI Gallery</name>
|
||||
<license>BSD</license>
|
||||
<version>gallery-2013.10.02-20-26</version>
|
||||
<licenseversion></licenseversion>
|
||||
<description>The Gallery is a collection of modules that have been developed by the YUI community to enhance the capabilities of YUI.</description>
|
||||
<repository>https://github.com/yui/yui3-gallery</repository>
|
||||
<copyrights>
|
||||
<copyright>2006, Yahoo! Inc.</copyright>
|
||||
</copyrights>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
<library>
|
||||
<location>jquery</location>
|
||||
<name>jQuery</name>
|
||||
<license>MIT</license>
|
||||
<description>jQuery is a fast, small, and feature-rich JavaScript library widely used on moodle.</description>
|
||||
<version>3.6.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/jquery/jquery</repository>
|
||||
<copyrights>
|
||||
<copyright>2016 The jQuery Foundation</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>jquery</location>
|
||||
<name>jQuery UI</name>
|
||||
<license>MIT</license>
|
||||
<description>jQuery UI is a set of user interface interactions, effects, widgets, and themes built on top of the jQuery library.</description>
|
||||
<version>1.13.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/jquery/jquery-ui</repository>
|
||||
<copyrights>
|
||||
<copyright>2016 The jQuery Foundation and other contributors</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>html2text</location>
|
||||
<name>HTML2Text</name>
|
||||
<license>GPL</license>
|
||||
<description>PHP script to convert HTML into an approximate text equivalent.</description>
|
||||
<version>4.3.1</version>
|
||||
<license>GPL</license>
|
||||
<licenseversion>2.0+</licenseversion>
|
||||
<repository>https://github.com/mtibben/html2text</repository>
|
||||
<copyrights>
|
||||
<copyright>2005-2007 Jon Abernathy [email protected]</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>markdown</location>
|
||||
<name>Markdown original</name>
|
||||
<license>BSD</license>
|
||||
<name>Markdown</name>
|
||||
<description>Functions to convert from the Markdown text format into clean XHTML.</description>
|
||||
<version>1.9.0</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>BSD</license>
|
||||
<repository>https://github.com/michelf/php-markdown</repository>
|
||||
<copyrights>
|
||||
<copyright>2004-2016 Michel Fortin https://michelf.com/projects/php-markdown/</copyright>
|
||||
<copyright>(Original Markdown) 2004-2006 John Gruber https://daringfireball.net/projects/markdown/
|
||||
All rights reserved.</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>xhprof</location>
|
||||
<name>XHProf</name>
|
||||
<license>Apache</license>
|
||||
<description>A Hierarchical Profiler for PHP.</description>
|
||||
<version>2.3.5</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/longxinH/xhprof</repository>
|
||||
<copyrights>
|
||||
<copyright>2009 Phacility</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>horde</location>
|
||||
<name>Horde</name>
|
||||
<description>Library used by the inbound e-mail handling system.</description>
|
||||
<license>LGPL/BSD</license>
|
||||
<version>5.2.23</version>
|
||||
<licenseversion>2.1</licenseversion>
|
||||
<repository>https://github.com/horde/base</repository>
|
||||
<copyrights>
|
||||
<copyright>Horde LLC</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>requirejs</location>
|
||||
<name>RequireJS</name>
|
||||
<license>MIT</license>
|
||||
<description>RequireJS is a JavaScript file and module loader.</description>
|
||||
<version>2.3.5</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/requirejs/requirejs</repository>
|
||||
<copyrights>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/loglevel.js</location>
|
||||
<name>loglevel.js</name>
|
||||
<license>MIT</license>
|
||||
<description>Minimal lightweight simple logging for JavaScript.</description>
|
||||
<version>1.7.1</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/pimterry/loglevel/</repository>
|
||||
<copyrights>
|
||||
<copyright>2019 Tim Perry</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>mustache</location>
|
||||
<name>Mustache</name>
|
||||
<license>MIT</license>
|
||||
<description>PHP library for displaying mustache templates.</description>
|
||||
<version>2.13.0</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/bobthecow/mustache.php</repository>
|
||||
<copyrights>
|
||||
<copyright>2010-2016 Justin Hileman</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/mustache.js</location>
|
||||
<name>Mustache.js</name>
|
||||
<license>MIT</license>
|
||||
<description>JS library for displaying mustache templates.</description>
|
||||
<version>3.1.0</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/janl/mustache.js</repository>
|
||||
<copyrights>
|
||||
<copyright>2009 Chris Wanstrath (Ruby)</copyright>
|
||||
<copyright>2010-2014 Jan Lehnardt (JavaScript)</copyright>
|
||||
<copyright>2010-2015 The mustache.js community</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>graphlib.php</location>
|
||||
<name>Graph Class</name>
|
||||
<license>GPL</license>
|
||||
<description>Class to draw line, point, bar, and area graphs, including numeric x-axis and double y-axis.</description>
|
||||
<version>1.6.3</version>
|
||||
<license>GPL</license>
|
||||
<copyrights>
|
||||
<copyright>2000 Herman Veluwenkamp (hermanV AT mindless DOT com)</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>php-css-parser</location>
|
||||
<name>PHP-CSS-Parser</name>
|
||||
<license>MIT</license>
|
||||
<description>A Parser for CSS Files written in PHP.</description>
|
||||
<version>8.3.1</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/sabberworm/PHP-CSS-Parser</repository>
|
||||
<copyrights>
|
||||
<copyright>2011 Raphael Schweikert, http://sabberworm.com/</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>rtlcss</location>
|
||||
<name>RTLCSS for PHP</name>
|
||||
<license>MIT</license>
|
||||
<description>RTLCSS is a framework for converting Left-To-Right (LTR) Cascading Style Sheets(CSS) to Right-To-Left (RTL).</description>
|
||||
<version>1.0.1</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/moodlehq/rtlcss-php</repository>
|
||||
<copyrights>
|
||||
<copyright>Frédéric Massart</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>scssphp</location>
|
||||
<name>scssphp</name>
|
||||
<license>MIT</license>
|
||||
<description>scssphp is a compiler for SCSS written in PHP.</description>
|
||||
<version>1.10.0</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/scssphp/scssphp/</repository>
|
||||
<copyrights>
|
||||
<copyright>2015 Leaf Corcoran</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>spout</location>
|
||||
<name>Spout</name>
|
||||
<license>Apache</license>
|
||||
<description>Library for importing and exporting csv / excel / ODS files.</description>
|
||||
<version>3.3.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/box/spout/</repository>
|
||||
<copyrights>
|
||||
<copyright>2022 Box, Inc. All rights reserved</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/chartjs-lazy.js</location>
|
||||
<name>Chart.js</name>
|
||||
<description>Simple yet flexible JavaScript charting for designers & developers.</description>
|
||||
<version>3.8.0</version>
|
||||
<license>MIT</license>
|
||||
<version>2.9.4</version>
|
||||
<repository>https://github.com/chartjs/Chart.js</repository>
|
||||
<copyrights>
|
||||
<copyright>2016 Nick Downie</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>maxmind/GeoIp2</location>
|
||||
<name>GeoIP2 PHP API</name>
|
||||
<license>Apache 2.0</license>
|
||||
<description>Library for processing of GeoIP data files.</description>
|
||||
<version>2.11.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/maxmind/GeoIP2-php</repository>
|
||||
<copyrights>
|
||||
<copyright>MaxMind</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>maxmind/MaxMind</location>
|
||||
<name>MaxMind DB Reader API</name>
|
||||
<license>Apache 2.0</license>
|
||||
<description>PHP API for reading MaxMind DB files.</description>
|
||||
<version>1.10.1</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/maxmind/MaxMind-DB-Reader-php/</repository>
|
||||
<copyrights>
|
||||
<copyright>MaxMind</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>ltiprovider</location>
|
||||
<name>LTI Tool Provider Library PHP</name>
|
||||
<license>Apache</license>
|
||||
<description>PHP library for communicating with learning tools as per the LTI specification.</description>
|
||||
<version>3.0.2</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP</repository>
|
||||
<copyrights>
|
||||
<copyright>2016 IMS Global Learning Consortium Inc. All Rights Reserved. Trademark Policy - (www.imsglobal.org/trademarks)</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>lti1p3</location>
|
||||
<name>LTI 1.3 Tool Library</name>
|
||||
<license>Apache</license>
|
||||
<description>A library used for building IMS-certified LTI 1.3 tool providers in PHP.</description>
|
||||
<version>5.1.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/packbackbooks/lti-1-3-php-library</repository>
|
||||
<copyrights>
|
||||
<copyright>MartinLenord</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/truncate.js</location>
|
||||
<name>Truncate.js</name>
|
||||
<license>MIT</license>
|
||||
<description>Dead simple HTML-safe truncation via the DOM. It truncates HTML code, and has several options such as length, finishBlock and noBreak.</description>
|
||||
<version>0.0.1</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/pathable/truncate</repository>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>fonts</location>
|
||||
<name>Font Awesome by Dave Gandy - http://fontawesome.io</name>
|
||||
<license>SIL OFL</license>
|
||||
<name>Font Awesome - http://fontawesome.io</name>
|
||||
<description>The Font Awesome font. Font Awesome is the Internet's icon library and toolkit, used by millions of designers, developers, and content creators.</description>
|
||||
<version>4.7.0</version>
|
||||
<license>SIL OFL</license>
|
||||
<licenseversion>1.1</licenseversion>
|
||||
<repository>https://github.com/FortAwesome/Font-Awesome</repository>
|
||||
<copyrights>
|
||||
<copyright>Dave Gandy</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/adapter.js</location>
|
||||
<name>WebRTC adapter</name>
|
||||
<license>BSD</license>
|
||||
<description>JavaScript library to insulate apps from spec changes and prefix differences in WebRTC. The prefix differences are mostly gone these days but differences in behaviour between browsers remain.</description>
|
||||
<version>8.0.0</version>
|
||||
<license>BSD</license>
|
||||
<licenseversion>3-Clause</licenseversion>
|
||||
<repository>https://github.com/webrtc/adapter</repository>
|
||||
<copyrights>
|
||||
<copyright>2014, The WebRTC project authors. All rights reserved. Copyright (c) 2018, The adapter.js project authors.</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>validateurlsyntax.php</location>
|
||||
<name>Url Syntax Validation using PHP and Regular Expressions</name>
|
||||
<name>validateUrlSyntax</name>
|
||||
<description>URL syntax validation using PHP and regular expressions.</description>
|
||||
<license>GPL</license>
|
||||
<licenseversion>2.0+</licenseversion>
|
||||
<copyrights>
|
||||
<copyright>2000 Rod Apeldoorn - rod(at)canowhoopass(dot)com</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
<upgradable>false</upgradable>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/popper.js</location>
|
||||
<name>Popper.js</name>
|
||||
<license>MIT</license>
|
||||
<description>A kickass library used to created Poppers in web applications.</description>
|
||||
<version>v1.12.6</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/floating-ui/floating-ui</repository>
|
||||
<copyrights>
|
||||
<copyright>2016 Federico Zivolo and contributors</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>geopattern-php</location>
|
||||
<name>Generate tiling SVG patterns from a string</name>
|
||||
<license>MIT</license>
|
||||
<name>GeoPattern</name>
|
||||
<description>Library for creating beautiful generative background images from a string.</description>
|
||||
<version>1.1.2</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/RedeyeGroup/geopattern-php</repository>
|
||||
<copyrights>
|
||||
<copyright>2015 Leaf Corcoran</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>php-jwt</location>
|
||||
<name>A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to RFC 7519</name>
|
||||
<license>BSD</license>
|
||||
<description>A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to RFC 7519</description>
|
||||
<version>6.0.0</version>
|
||||
<license>BSD</license>
|
||||
<licenseversion>3-Clause</licenseversion>
|
||||
<repository>https://github.com/firebase/php-jwt</repository>
|
||||
<copyrights>
|
||||
<copyright>2011, Neuman Vong</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>polyfills</location>
|
||||
<name>regenerator-runtime</name>
|
||||
<license>MIT</license>
|
||||
<description>This package implements a fully-functional source transformation that takes the syntax for generators/yield from ECMAScript 2015 or ES2015 and Asynchronous Iteration proposal and spits out efficient JS-of-today (ES5) that behaves the same way.</description>
|
||||
<version>0.13.7</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/facebook/regenerator/</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>polyfills</location>
|
||||
<name>core-js-bundle</name>
|
||||
<license>MIT</license>
|
||||
<description>Modular standard library for JavaScript. Includes polyfills for ECMAScript up to 2021: promises, symbols, collections, iterators, typed arrays, many other features, ECMAScript proposals, some cross-platform WHATWG / W3C features and proposals like URL. You can load only required features or use it without global namespace pollution.</description>
|
||||
<version>3.15.0</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/zloirock/core-js</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>polyfills</location>
|
||||
<name>mdn-polyfill</name>
|
||||
<license>MIT</license>
|
||||
<description>A collection of side-effect ECMAScript modules. Minimized, mangled and extremely small thanks to Rollup - next-generation ES6 module bundler.</description>
|
||||
<version>5.20.0</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/msn0/mdn-polyfills</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>emoji-data</location>
|
||||
<name>Emoji data</name>
|
||||
<license>MIT</license>
|
||||
<description>Library to parse easily data and sprite sheets for emoji.</description>
|
||||
<version>7.0.2</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/iamcal/emoji-data/</repository>
|
||||
<copyrights>
|
||||
<copyright>2013 Cal Henderson</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>polyfills</location>
|
||||
<name>url-polyfill</name>
|
||||
<license>MIT</license>
|
||||
<description>Polyfill URL and URLSearchParams to match last WHATWG specifications.</description>
|
||||
<version>1.1.12</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/lifaon74/url-polyfill</repository>
|
||||
</library>
|
||||
<library>
|
||||
<location>plist</location>
|
||||
<name>plist</name>
|
||||
<license>MIT</license>
|
||||
<description>PHP Implementation of Apple's PList (binary and XML).</description>
|
||||
<version>2.0.2</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/TECLIB/CFPropertyList</repository>
|
||||
<copyrights>
|
||||
<copyright>2018 Teclib</copyright>
|
||||
<copyright>2009 Christian Kruse, Rodney Rehm</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>zipstream</location>
|
||||
<name>ZipStream-PHP</name>
|
||||
<license>MIT</license>
|
||||
<description>PHP ZIP Streaming Library</description>
|
||||
<version>2.1.0</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/maennchen/ZipStream-PHP</repository>
|
||||
<copyrights>
|
||||
<copyright>2007-2009 Paul Duncan [email protected]</copyright>
|
||||
<copyright>2014 Jonatan Männchen [email protected]</copyright>
|
||||
<copyright>2014 Jesse G. Donat [email protected]</copyright>
|
||||
<copyright>2018 Nicolas CARPi [email protected]</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>php-enum</location>
|
||||
<name>php-enum</name>
|
||||
<license>MIT</license>
|
||||
<description>PHP Enum implementation inspired from SplEnum</description>
|
||||
<version>1.8.3</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/myclabs/php-enum</repository>
|
||||
<copyrights>
|
||||
<copyright>2015 My C-Labs</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>http-message</location>
|
||||
<name>http-message</name>
|
||||
<license>MIT</license>
|
||||
<description>Provide a set of common interfaces for HTTP messages as described in RFC 7230 and RFC 7231</description>
|
||||
<version>1.0.1</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/php-fig/http-message</repository>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -37,6 +37,10 @@ information provided here is intended especially for developers.
|
||||
* Added $CFG->proxylogunsafe and proxyfixunsafe to detect code which doesn't honor the proxy config
|
||||
* Function admin_externalpage_setup() now has additional option 'nosearch' allowing to remove Site administration search form.
|
||||
* The function print_error has been deprecated. Kindly use moodle_exception.
|
||||
* The abstract `get_name` method has been moved from `\core\task\scheduled_task` to the `\core\task\task_base` class and can now be
|
||||
implemented by adhoc tasks. For backwards compatibility, a default implementation has been added to `\core\task\adhoc_task` to
|
||||
return the class name.
|
||||
* The function get_module_metadata() has been finally deprecated and can not be used anymore.
|
||||
|
||||
=== 4.0 ===
|
||||
|
||||
|
||||
@@ -11,11 +11,14 @@ Feature: Embed videos without the media filter
|
||||
|
||||
@javascript
|
||||
Scenario: Add a video in a URL resource. Make sure media filters work
|
||||
When I add a "URL" to section "1" and I fill the form with:
|
||||
| Name | Video URL |
|
||||
| Description | Example of a video url |
|
||||
| External URL | http://download.moodle.org/mediatest/quicktime_320_180.mov |
|
||||
And I follow "Video URL"
|
||||
Given the following "activity" exists:
|
||||
| activity | url |
|
||||
| course | Acceptance test site |
|
||||
| name | Video URL |
|
||||
| intro | Example of a video url |
|
||||
| externalurl | http://download.moodle.org/mediatest/quicktime_320_180.mov |
|
||||
| section | 1 |
|
||||
When I am on the "Video URL" "url activity" page
|
||||
Then ".video-js" "css_element" should exist
|
||||
And I am on site homepage
|
||||
|
||||
|
||||
@@ -3,42 +3,71 @@
|
||||
<library>
|
||||
<location>amd/src/video-lazy.js</location>
|
||||
<name>VideoJS</name>
|
||||
<license>Apache</license>
|
||||
<description>JavaScript library that makes it easier to work with and build on HTML5 video.</description>
|
||||
<version>7.17.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/videojs/video.js</repository>
|
||||
<copyrights>
|
||||
<copyright>Brightcove, Inc</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/Youtube-lazy.js</location>
|
||||
<name>YouTube Playback Technology for Video.js</name>
|
||||
<license>MIT</license>
|
||||
<name>videojs-youtube</name>
|
||||
<description>YouTube Playback Technology for Video.js.</description>
|
||||
<version>2.6.1</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/videojs/videojs-youtube</repository>
|
||||
<copyrights>
|
||||
<copyright>2014-2015 Benoit Tremblay <[email protected]></copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>videojs</location>
|
||||
<name>VideoJS support files</name>
|
||||
<license>Apache</license>
|
||||
<description>JavaScript library that makes it easier to work with and build on HTML5 video</description>
|
||||
<version>7.17.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/videojs/video.js</repository>
|
||||
<copyrights>
|
||||
<copyright>Brightcove, Inc</copyright>
|
||||
</copyrights>
|
||||
<customised/>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/local/ogv/ogv.js</location>
|
||||
<name>ogv.js</name>
|
||||
<license>MIT</license>
|
||||
<description>Javascript media decoder and player for Ogg Vorbis/Opus/Theora and WebM VP8/VP9/AV1 video.</description>
|
||||
<version>1.8.4</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/brion/ogv.js/</repository>
|
||||
<copyrights>
|
||||
<copyright>Brion Vibber</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>ogvjs</location>
|
||||
<name>ogv.js support files</name>
|
||||
<description>JavaScript media decoder and player for Ogg Vorbis/Opus/Theora and WebM VP8/VP9/AV1 video.</description>
|
||||
<license>MIT</license>
|
||||
<version>1.8.4</version>
|
||||
<repository>https://github.com/brion/ogv.js/</repository>
|
||||
<copyrights>
|
||||
<copyright>Brion Vibber</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
<library>
|
||||
<location>amd/src/videojs-ogvjs-lazy.js</location>
|
||||
<name>ogv.js Tech plugin for Video.JS</name>
|
||||
<license>MIT</license>
|
||||
<description>The ogv.js tech plugin for VideoJS</description>
|
||||
<version>0.1.2</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/HuongNV13/videojs-ogvjs</repository>
|
||||
<copyrights>
|
||||
<copyright>2021 Huong Nguyen [email protected]</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
<library>
|
||||
<location>fpdi</location>
|
||||
<name>FPDI</name>
|
||||
<license>MIT</license>
|
||||
<description>Collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF.</description>
|
||||
<version>2.3.6</version>
|
||||
<licenseversion></licenseversion>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/Setasign/FPDI</repository>
|
||||
<copyrights>
|
||||
<copyright>2020 Setasign GmbH & Co. KG, https://www.setasign.com</copyright>
|
||||
</copyrights>
|
||||
</library>
|
||||
</libraries>
|
||||
|
||||
@@ -138,6 +138,7 @@ class custom_completion extends activity_custom_completion {
|
||||
*/
|
||||
public function get_sort_order(): array {
|
||||
return [
|
||||
'completionview',
|
||||
'completionengagementchats',
|
||||
'completionengagementtalks',
|
||||
'completionengagementraisehand',
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@ define("mod_data/templateseditor",["exports","core/str","core/notification","cor
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const selectors_toggleTemplateEditor='input[name="useeditor"]';_exports.init=(d,mode)=>{((d,mode)=>{document.querySelector(selectors_toggleTemplateEditor).addEventListener("click",(async event=>{event.preventDefault(),event.target.checked?(0,_notification.confirm)((0,_str.get_string)("confirmation","admin"),(0,_str.get_string)("enabletemplateeditorcheck","mod_data"),(0,_str.get_string)("yes","core"),(0,_str.get_string)("no","core"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:d,mode:mode,useeditor:!0})})):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:d,mode:mode,useeditor:!1})}))})(d,mode)}}));
|
||||
const selectors_toggleTemplateEditor='input[name="useeditor"]';_exports.init=(d,mode)=>{((d,mode)=>{const toggleTemplateEditor=document.querySelector(selectors_toggleTemplateEditor);toggleTemplateEditor&&toggleTemplateEditor.addEventListener("click",(async event=>{event.preventDefault(),event.target.checked?(0,_notification.confirm)((0,_str.get_string)("confirmation","admin"),(0,_str.get_string)("enabletemplateeditorcheck","mod_data"),(0,_str.get_string)("yes","core"),(0,_str.get_string)("no","core"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:d,mode:mode,useeditor:!0})})):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:d,mode:mode,useeditor:!1})}))})(d,mode)}}));
|
||||
|
||||
//# sourceMappingURL=templateseditor.min.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"templateseditor.min.js","sources":["../src/templateseditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module to control the template editor.\n *\n * @module mod_data/templateseditor\n * @copyright 2021 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {confirm as confirmDialogue} from 'core/notification';\nimport {relativeUrl} from 'core/url';\n\n/**\n * Template editor constants.\n */\nconst selectors = {\n toggleTemplateEditor: 'input[name=\"useeditor\"]',\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {int} d The database ID\n * @param {string} mode The template mode\n */\nconst registerEventListeners = (d, mode) => {\n const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);\n\n toggleTemplateEditor.addEventListener('click', async(event) => {\n event.preventDefault();\n // Whether the event action attempts to enable or disable the template editor.\n const enableTemplateEditor = event.target.checked;\n\n if (enableTemplateEditor) {\n // Display a confirmation dialog before enabling the template editor.\n confirmDialogue(\n getString('confirmation', 'admin'),\n getString('enabletemplateeditorcheck', 'mod_data'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: true});\n }\n );\n } else {\n window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: false});\n }\n });\n};\n\n/**\n * Initialize the module.\n *\n * @param {int} d The database ID\n * @param {string} mode The template mode\n */\nexport const init = (d, mode) => {\n registerEventListeners(d, mode);\n};\n"],"names":["selectors","d","mode","document","querySelector","addEventListener","async","event","preventDefault","target","checked","window","location","useeditor","registerEventListeners"],"mappings":";;;;;;;;MA8BMA,+BACoB,wCAwCN,CAACC,EAAGC,QA/BO,EAACD,EAAGC,QACFC,SAASC,cAAcJ,gCAE/BK,iBAAiB,SAASC,MAAAA,QAC3CC,MAAMC,iBAEuBD,MAAME,OAAOC,mCAKlC,mBAAU,eAAgB,UAC1B,mBAAU,4BAA6B,aACvC,mBAAU,MAAO,SACjB,mBAAU,KAAM,SAChB,KACIC,OAAOC,UAAW,oBAAY,0BAA2B,CAACX,EAAGA,EAAGC,KAAMA,KAAMW,WAAW,OAI/FF,OAAOC,UAAW,oBAAY,0BAA2B,CAACX,EAAGA,EAAGC,KAAMA,KAAMW,WAAW,QAY/FC,CAAuBb,EAAGC"}
|
||||
{"version":3,"file":"templateseditor.min.js","sources":["../src/templateseditor.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module to control the template editor.\n *\n * @module mod_data/templateseditor\n * @copyright 2021 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {confirm as confirmDialogue} from 'core/notification';\nimport {relativeUrl} from 'core/url';\n\n/**\n * Template editor constants.\n */\nconst selectors = {\n toggleTemplateEditor: 'input[name=\"useeditor\"]',\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {int} d The database ID\n * @param {string} mode The template mode\n */\nconst registerEventListeners = (d, mode) => {\n const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);\n\n if (!toggleTemplateEditor) {\n return;\n }\n\n toggleTemplateEditor.addEventListener('click', async(event) => {\n event.preventDefault();\n // Whether the event action attempts to enable or disable the template editor.\n const enableTemplateEditor = event.target.checked;\n\n if (enableTemplateEditor) {\n // Display a confirmation dialog before enabling the template editor.\n confirmDialogue(\n getString('confirmation', 'admin'),\n getString('enabletemplateeditorcheck', 'mod_data'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: true});\n }\n );\n } else {\n window.location = relativeUrl('/mod/data/templates.php', {d: d, mode: mode, useeditor: false});\n }\n });\n};\n\n/**\n * Initialize the module.\n *\n * @param {int} d The database ID\n * @param {string} mode The template mode\n */\nexport const init = (d, mode) => {\n registerEventListeners(d, mode);\n};\n"],"names":["selectors","d","mode","toggleTemplateEditor","document","querySelector","addEventListener","async","event","preventDefault","target","checked","window","location","useeditor","registerEventListeners"],"mappings":";;;;;;;;MA8BMA,+BACoB,wCA4CN,CAACC,EAAGC,QAnCO,EAACD,EAAGC,cACzBC,qBAAuBC,SAASC,cAAcL,gCAE/CG,sBAILA,qBAAqBG,iBAAiB,SAASC,MAAAA,QAC3CC,MAAMC,iBAEuBD,MAAME,OAAOC,mCAKlC,mBAAU,eAAgB,UAC1B,mBAAU,4BAA6B,aACvC,mBAAU,MAAO,SACjB,mBAAU,KAAM,SAChB,KACIC,OAAOC,UAAW,oBAAY,0BAA2B,CAACZ,EAAGA,EAAGC,KAAMA,KAAMY,WAAW,OAI/FF,OAAOC,UAAW,oBAAY,0BAA2B,CAACZ,EAAGA,EAAGC,KAAMA,KAAMY,WAAW,QAY/FC,CAAuBd,EAAGC"}
|
||||
@@ -41,6 +41,10 @@ const selectors = {
|
||||
const registerEventListeners = (d, mode) => {
|
||||
const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);
|
||||
|
||||
if (!toggleTemplateEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleTemplateEditor.addEventListener('click', async(event) => {
|
||||
event.preventDefault();
|
||||
// Whether the event action attempts to enable or disable the template editor.
|
||||
|
||||
@@ -33,6 +33,7 @@ use mod_data\external\database_summary_exporter;
|
||||
use mod_data\external\record_exporter;
|
||||
use mod_data\external\content_exporter;
|
||||
use mod_data\external\field_exporter;
|
||||
use mod_data\manager;
|
||||
|
||||
/**
|
||||
* Database module external functions
|
||||
@@ -206,11 +207,13 @@ class mod_data_external extends external_api {
|
||||
list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
|
||||
|
||||
// Call the data/lib API.
|
||||
data_view($database, $course, $cm, $context);
|
||||
$manager = manager::create_from_coursemodule($cm);
|
||||
$manager->set_module_viewed($course);
|
||||
|
||||
$result = array();
|
||||
$result['status'] = true;
|
||||
$result['warnings'] = $warnings;
|
||||
$result = [
|
||||
'status' => true,
|
||||
'warnings' => $warnings,
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -404,6 +407,8 @@ class mod_data_external extends external_api {
|
||||
}
|
||||
}
|
||||
|
||||
$manager = manager::create_from_instance($database);
|
||||
|
||||
list($records, $maxcount, $totalcount, $page, $nowperpage, $sort, $mode) =
|
||||
data_search_entries($database, $cm, $context, 'list', $groupid, '', $params['sort'], $params['order'],
|
||||
$params['page'], $params['perpage']);
|
||||
@@ -446,11 +451,8 @@ class mod_data_external extends external_api {
|
||||
|
||||
// Check if we should return the list rendered.
|
||||
if ($params['returncontents']) {
|
||||
ob_start();
|
||||
// The return parameter stops the execution after the first record.
|
||||
data_print_template('listtemplate', $records, $database, '', $page, false);
|
||||
$result['listviewcontents'] = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$parser = $manager->get_template('listtemplate', ['page' => $page]);
|
||||
$result['listviewcontents'] = $parser->parse_entries($records);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -515,6 +517,8 @@ class mod_data_external extends external_api {
|
||||
$canmanageentries = has_capability('mod/data:manageentries', $context);
|
||||
data_require_time_available($database, $canmanageentries);
|
||||
|
||||
$manager = manager::create_from_instance($database);
|
||||
|
||||
if ($record->groupid != 0) {
|
||||
if (!groups_group_visible($record->groupid, $course, $cm)) {
|
||||
throw new moodle_exception('notingroup');
|
||||
@@ -543,7 +547,8 @@ class mod_data_external extends external_api {
|
||||
// Check if we should return the entry rendered.
|
||||
if ($params['returncontents']) {
|
||||
$records = [$record];
|
||||
$result['entryviewcontents'] = data_print_template('singletemplate', $records, $database, '', 0, true);
|
||||
$parser = $manager->get_template('singletemplate');
|
||||
$result['entryviewcontents'] = $parser->parse_entries($records);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -714,6 +719,8 @@ class mod_data_external extends external_api {
|
||||
// Check database is open in time.
|
||||
data_require_time_available($database, null, $context);
|
||||
|
||||
$manager = manager::create_from_instance($database);
|
||||
|
||||
if (!empty($params['groupid'])) {
|
||||
$groupid = $params['groupid'];
|
||||
// Determine is the group is visible to user.
|
||||
@@ -779,11 +786,8 @@ class mod_data_external extends external_api {
|
||||
|
||||
// Check if we should return the list rendered.
|
||||
if ($params['returncontents']) {
|
||||
ob_start();
|
||||
// The return parameter stops the execution after the first record.
|
||||
data_print_template('listtemplate', $records, $database, '', $page, false);
|
||||
$result['listviewcontents'] = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$parser = $manager->get_template('listtemplate', ['page' => $page]);
|
||||
$result['listviewcontents'] = $parser->parse_entries($records);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
// 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/>.
|
||||
|
||||
namespace mod_data;
|
||||
|
||||
use cm_info;
|
||||
use context_module;
|
||||
use completion_info;
|
||||
use data_field_base;
|
||||
use mod_data\event\course_module_viewed;
|
||||
use mod_data\event\template_viewed;
|
||||
use mod_data\event\template_updated;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class manager for database activity
|
||||
*
|
||||
* @package mod_data
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class manager {
|
||||
|
||||
/** Module name. */
|
||||
const MODULE = 'data';
|
||||
|
||||
/** Template list with their files required to save the information of a preset. */
|
||||
const TEMPLATES_LIST = [
|
||||
'listtemplate' => 'listtemplate.html',
|
||||
'singletemplate' => 'singletemplate.html',
|
||||
'asearchtemplate' => 'asearchtemplate.html',
|
||||
'addtemplate' => 'addtemplate.html',
|
||||
'rsstemplate' => 'rsstemplate.html',
|
||||
'csstemplate' => 'csstemplate.css',
|
||||
'jstemplate' => 'jstemplate.js',
|
||||
'listtemplateheader' => 'listtemplateheader.html',
|
||||
'listtemplatefooter' => 'listtemplatefooter.html',
|
||||
'rsstitletemplate' => 'rsstitletemplate.html',
|
||||
];
|
||||
|
||||
/** @var string plugin path. */
|
||||
private $path;
|
||||
|
||||
/** @var stdClass course_module record. */
|
||||
private $instance;
|
||||
|
||||
/** @var context_module the current context. */
|
||||
private $context;
|
||||
|
||||
/** @var cm_info course_modules record. */
|
||||
private $cm;
|
||||
|
||||
/** @var array the current data_fields records.
|
||||
* Do not access this attribute directly, use $this->get_field_records instead
|
||||
*/
|
||||
private $_fieldrecords = null;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param cm_info $cm course module info object
|
||||
* @param stdClass $instance activity instance object.
|
||||
*/
|
||||
public function __construct(cm_info $cm, stdClass $instance) {
|
||||
global $CFG;
|
||||
$this->cm = $cm;
|
||||
$this->instance = $instance;
|
||||
$this->context = context_module::instance($cm->id);
|
||||
$this->instance->cmidnumber = $cm->idnumber;
|
||||
$this->path = $CFG->dirroot . '/mod/' . self::MODULE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a manager instance from an instance record.
|
||||
*
|
||||
* @param stdClass $instance an activity record
|
||||
* @return manager
|
||||
*/
|
||||
public static function create_from_instance(stdClass $instance): self {
|
||||
$cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
|
||||
// Ensure that $this->cm is a cm_info object.
|
||||
$cm = cm_info::create($cm);
|
||||
return new self($cm, $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a manager instance from a course_modules record.
|
||||
*
|
||||
* @param stdClass|cm_info $cm an activity record
|
||||
* @return manager
|
||||
*/
|
||||
public static function create_from_coursemodule($cm): self {
|
||||
global $DB;
|
||||
// Ensure that $this->cm is a cm_info object.
|
||||
$cm = cm_info::create($cm);
|
||||
$instance = $DB->get_record(self::MODULE, ['id' => $cm->instance], '*', MUST_EXIST);
|
||||
return new self($cm, $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a manager instance from a data_record entry.
|
||||
*
|
||||
* @param stdClass $record the data_record record
|
||||
* @return manager
|
||||
*/
|
||||
public static function create_from_data_record($record): self {
|
||||
global $DB;
|
||||
$instance = $DB->get_record(self::MODULE, ['id' => $record->dataid], '*', MUST_EXIST);
|
||||
$cm = get_coursemodule_from_instance(self::MODULE, $instance->id);
|
||||
$cm = cm_info::create($cm);
|
||||
return new self($cm, $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current context.
|
||||
*
|
||||
* @return context_module
|
||||
*/
|
||||
public function get_context(): context_module {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current instance.
|
||||
*
|
||||
* @return stdClass the instance record
|
||||
*/
|
||||
public function get_instance(): stdClass {
|
||||
return $this->instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current cm_info.
|
||||
*
|
||||
* @return cm_info the course module
|
||||
*/
|
||||
public function get_coursemodule(): cm_info {
|
||||
return $this->cm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger module viewed event and set the module viewed for completion.
|
||||
*
|
||||
* @param stdClass $course course object
|
||||
*/
|
||||
public function set_module_viewed(stdClass $course) {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/completionlib.php');
|
||||
|
||||
// Trigger module viewed event.
|
||||
$event = course_module_viewed::create([
|
||||
'objectid' => $this->instance->id,
|
||||
'context' => $this->context,
|
||||
]);
|
||||
$event->add_record_snapshot('course', $course);
|
||||
$event->add_record_snapshot('course_modules', $this->cm);
|
||||
$event->add_record_snapshot(self::MODULE, $this->instance);
|
||||
$event->trigger();
|
||||
|
||||
// Completion.
|
||||
$completion = new completion_info($course);
|
||||
$completion->set_module_viewed($this->cm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger module template viewed event.
|
||||
*/
|
||||
public function set_template_viewed() {
|
||||
// Trigger an event for viewing templates.
|
||||
$event = template_viewed::create([
|
||||
'context' => $this->context,
|
||||
'courseid' => $this->cm->course,
|
||||
'other' => [
|
||||
'dataid' => $this->instance->id,
|
||||
],
|
||||
]);
|
||||
$event->add_record_snapshot(self::MODULE, $this->instance);
|
||||
$event->trigger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the database fields.
|
||||
*
|
||||
* @return data_field_base[] the field instances.
|
||||
*/
|
||||
public function get_fields(): array {
|
||||
$result = [];
|
||||
$fieldrecords = $this->get_field_records();
|
||||
foreach ($fieldrecords as $fieldrecord) {
|
||||
$result[$fieldrecord->id] = $this->get_field($fieldrecord);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the field records (the current data_fields records).
|
||||
*
|
||||
* @return stdClass[] an array of records
|
||||
*/
|
||||
public function get_field_records() {
|
||||
global $DB;
|
||||
if ($this->_fieldrecords === null) {
|
||||
$this->_fieldrecords = $DB->get_records('data_fields', ['dataid' => $this->instance->id]);
|
||||
}
|
||||
return $this->_fieldrecords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specific field instance from a field record.
|
||||
*
|
||||
* @param stdClass $fieldrecord the fieldrecord to convert
|
||||
* @return data_field_base the data field class instance
|
||||
*/
|
||||
public function get_field(stdClass $fieldrecord): data_field_base {
|
||||
$filepath = "{$this->path}/field/{$fieldrecord->type}/field.class.php";
|
||||
$classname = "data_field_{$fieldrecord->type}";
|
||||
if (!file_exists($filepath) || !class_exists($classname)) {
|
||||
return new data_field_base($fieldrecord, $this->instance, $this->cm);
|
||||
}
|
||||
require_once($filepath);
|
||||
$newfield = new $classname($fieldrecord, $this->instance, $this->cm);
|
||||
return $newfield;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specific template.
|
||||
*
|
||||
* NOTE: this method returns a default template if the module template is empty.
|
||||
* However, it won't update the template database field.
|
||||
*
|
||||
* @param string $templatename
|
||||
* @param array $options extra display options array
|
||||
* @return template the template instance
|
||||
*/
|
||||
public function get_template(string $templatename, array $options = []): template {
|
||||
if ($templatename === 'single') {
|
||||
$templatename = 'singletemplate';
|
||||
}
|
||||
$instance = $this->instance;
|
||||
$templatecontent = $instance->{$templatename} ?? '';
|
||||
if (empty($templatecontent)) {
|
||||
$templatecontent = data_generate_default_template($instance, $templatename, 0, false, false);
|
||||
}
|
||||
// Some templates have extra options.
|
||||
if ($templatename === 'singletemplate') {
|
||||
$options['comments'] = true;
|
||||
$options['ratings'] = true;
|
||||
}
|
||||
return new template($this, $templatecontent, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the database templates.
|
||||
*
|
||||
* @param stdClass $newtemplates an object with all the new templates
|
||||
* @return bool if updated successfully.
|
||||
*/
|
||||
public function update_templates(stdClass $newtemplates): bool {
|
||||
global $DB;
|
||||
$record = (object)[
|
||||
'id' => $this->instance->id,
|
||||
];
|
||||
foreach (self::TEMPLATES_LIST as $templatename => $templatefile) {
|
||||
if (!isset($newtemplates->{$templatename})) {
|
||||
continue;
|
||||
}
|
||||
$record->{$templatename} = $newtemplates->{$templatename};
|
||||
}
|
||||
|
||||
// The add entry form cannot repeat tags.
|
||||
if (isset($record->addtemplate) && !data_tags_check($this->instance->id, $record->addtemplate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$DB->update_record(self::MODULE, $record);
|
||||
$this->instance = $DB->get_record(self::MODULE, ['id' => $this->cm->instance], '*', MUST_EXIST);
|
||||
|
||||
// Trigger an event for saving the templates.
|
||||
$event = template_updated::create(array(
|
||||
'context' => $this->context,
|
||||
'courseid' => $this->cm->course,
|
||||
'other' => array(
|
||||
'dataid' => $this->instance->id,
|
||||
)
|
||||
));
|
||||
$event->trigger();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
// 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/>.
|
||||
|
||||
namespace mod_data\output;
|
||||
|
||||
use templatable;
|
||||
use renderable;
|
||||
use mod_data\manager;
|
||||
use moodle_url;
|
||||
use texteditor;
|
||||
|
||||
/**
|
||||
* Renderable class for template editor.
|
||||
*
|
||||
* @package mod_data
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class template_editor implements templatable, renderable {
|
||||
|
||||
/** @var manager manager instance. */
|
||||
private $manager;
|
||||
|
||||
/** @var string the template name. */
|
||||
private $templatename;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param manager $manager the activity instance manager
|
||||
* @param string $templatename the template to edit
|
||||
*/
|
||||
public function __construct(manager $manager, string $templatename) {
|
||||
$this->manager = $manager;
|
||||
$this->templatename = $templatename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the data for the mustache template.
|
||||
*
|
||||
* @param \renderer_base $output renderer to be used to render the action bar elements.
|
||||
* @return array
|
||||
*/
|
||||
public function export_for_template(\renderer_base $output): array {
|
||||
$instance = $this->manager->get_instance();
|
||||
$cm = $this->manager->get_coursemodule();
|
||||
|
||||
$data = [
|
||||
'title' => get_string('header' . $this->templatename, 'data'),
|
||||
'sesskey' => sesskey(),
|
||||
'disableeditor' => true,
|
||||
'url' => new moodle_url('/mod/data/templates.php', ['id' => $cm->id, 'mode' => $this->templatename]),
|
||||
];
|
||||
|
||||
// Determine whether to use HTML editors.
|
||||
$usehtmleditor = false;
|
||||
$disableeditor = false;
|
||||
if (($this->templatename !== 'csstemplate') && ($this->templatename !== 'jstemplate')) {
|
||||
$usehtmleditor = data_get_config($instance, "editor_{$this->templatename}", true);
|
||||
$disableeditor = true;
|
||||
}
|
||||
$data['usehtmleditor'] = $usehtmleditor;
|
||||
// Some templates, like CSS, cannot enable the wysiwyg editor.
|
||||
$data['disableeditor'] = $disableeditor;
|
||||
|
||||
$tools = new template_editor_tools($this->manager, $this->templatename);
|
||||
$data['toolbar'] = $tools->export_for_template($output);
|
||||
$data['editors'] = $this->get_editors_data($usehtmleditor);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the editors data.
|
||||
*
|
||||
* @param bool $usehtmleditor if the user wants wysiwyg editor or not
|
||||
* @return array editors data
|
||||
*/
|
||||
private function get_editors_data(bool $usehtmleditor): array {
|
||||
global $PAGE;
|
||||
|
||||
$result = [];
|
||||
$instance = $this->manager->get_instance();
|
||||
|
||||
// Setup editor.
|
||||
editors_head_setup();
|
||||
$PAGE->requires->js_call_amd(
|
||||
'mod_data/templateseditor',
|
||||
'init',
|
||||
['d' => $instance->id, 'mode' => $this->templatename]
|
||||
);
|
||||
|
||||
$format = FORMAT_PLAIN;
|
||||
if ($usehtmleditor) {
|
||||
$format = FORMAT_HTML;
|
||||
}
|
||||
|
||||
$editor = editors_get_preferred_editor($format);
|
||||
|
||||
// Add editors.
|
||||
if ($this->templatename === 'listtemplate') {
|
||||
$result[] = $this->generate_editor_data(
|
||||
$editor,
|
||||
'header',
|
||||
'listtemplateheader',
|
||||
$instance->listtemplateheader
|
||||
);
|
||||
$maineditorname = 'multientry';
|
||||
} else {
|
||||
$maineditorname = $this->templatename;
|
||||
}
|
||||
|
||||
$value = $instance->{$this->templatename} ?? '';
|
||||
$result[] = $this->generate_editor_data(
|
||||
$editor,
|
||||
$maineditorname,
|
||||
$this->templatename,
|
||||
$value
|
||||
);
|
||||
|
||||
if ($this->templatename === 'listtemplate') {
|
||||
$result[] = $this->generate_editor_data(
|
||||
$editor,
|
||||
'footer',
|
||||
'listtemplatefooter',
|
||||
$instance->listtemplatefooter
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->templatename === 'rsstemplate') {
|
||||
$result[] = $this->generate_editor_data(
|
||||
$editor,
|
||||
'rsstitletemplate',
|
||||
'rsstitletemplate',
|
||||
$instance->rsstitletemplate
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a single editor data.
|
||||
*
|
||||
* @param texteditor $editor the editor object
|
||||
* @param string $name the editor name
|
||||
* @param string $fieldname the field name
|
||||
* @param string|null $value the current value
|
||||
* @return array the editor data
|
||||
*/
|
||||
private function generate_editor_data(
|
||||
texteditor $editor,
|
||||
string $name,
|
||||
string $fieldname,
|
||||
?string $value
|
||||
): array {
|
||||
$options = [
|
||||
'trusttext' => false,
|
||||
'forcehttps' => false,
|
||||
'subdirs' => false,
|
||||
'maxfiles' => 0,
|
||||
'maxbytes' => 0,
|
||||
'changeformat' => 0,
|
||||
'noclean' => false,
|
||||
];
|
||||
|
||||
$result = [
|
||||
'name' => get_string($name, 'data'),
|
||||
'fieldname' => $fieldname,
|
||||
'value' => $value,
|
||||
];
|
||||
$editor->set_text($value);
|
||||
$editor->use_editor($fieldname, $options);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
// 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/>.
|
||||
|
||||
namespace mod_data\output;
|
||||
|
||||
use templatable;
|
||||
use renderable;
|
||||
use core_tag_tag;
|
||||
use mod_data\manager;
|
||||
|
||||
/**
|
||||
* Renderable class for template editor tools.
|
||||
*
|
||||
* @package mod_data
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class template_editor_tools implements templatable, renderable {
|
||||
|
||||
/** @var manager manager instance. */
|
||||
private $manager;
|
||||
|
||||
/** @var string the template name. */
|
||||
private $templatename;
|
||||
|
||||
/**
|
||||
* The class constructor.
|
||||
*
|
||||
* @param manager $manager the activity instance manager
|
||||
* @param string $templatename the template to edit
|
||||
*/
|
||||
public function __construct(manager $manager, string $templatename) {
|
||||
$this->manager = $manager;
|
||||
$this->templatename = $templatename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the data for the mustache template.
|
||||
*
|
||||
* @param \renderer_base $output renderer to be used to render the action bar elements.
|
||||
* @return array
|
||||
*/
|
||||
public function export_for_template(\renderer_base $output): array {
|
||||
$tools = [
|
||||
$this->get_field_tags($this->templatename),
|
||||
$this->get_field_id_tags($this->templatename),
|
||||
$this->get_action_tags($this->templatename),
|
||||
$this->get_other_tags($this->templatename),
|
||||
];
|
||||
$tools = array_filter($tools, static function ($value) {
|
||||
return !empty($value['tags']);
|
||||
});
|
||||
return [
|
||||
'toolshelp' => $output->help_icon('availabletags', 'data'),
|
||||
'hastools' => !empty($tools),
|
||||
'tools' => array_values($tools),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the field template tags.
|
||||
*
|
||||
* @param string $templatename the template name
|
||||
* @return array|null array of tags.
|
||||
*/
|
||||
protected function get_field_tags(string $templatename): array {
|
||||
$name = get_string('fields', 'data');
|
||||
if ($templatename == 'csstemplate' || $templatename == 'jstemplate') {
|
||||
return $this->get_optgroup_data($name, []);
|
||||
}
|
||||
$taglist = [];
|
||||
$fields = $this->manager->get_fields();
|
||||
foreach ($fields as $field) {
|
||||
$fieldname = $field->get_name();
|
||||
$taglist["[[$fieldname]]"] = $fieldname;
|
||||
}
|
||||
return $this->get_optgroup_data($name, $taglist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the field IDs template tags.
|
||||
*
|
||||
* @param string $templatename the template name
|
||||
* @return array|null array of tags.
|
||||
*/
|
||||
protected function get_field_id_tags(string $templatename): array {
|
||||
$name = get_string('fieldids', 'data');
|
||||
if ($templatename != 'addtemplate') {
|
||||
return $this->get_optgroup_data($name, []);
|
||||
}
|
||||
$taglist = [];
|
||||
// Field IDs.
|
||||
$fields = $this->manager->get_fields();
|
||||
foreach ($fields as $field) {
|
||||
$fieldname = $field->get_name();
|
||||
$taglist["[[$fieldname#id]]"] = "$fieldname id";
|
||||
}
|
||||
return $this->get_optgroup_data($name, $taglist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the field action tags.
|
||||
*
|
||||
* @param string $templatename the template name
|
||||
* @return array|null array of tags.
|
||||
*/
|
||||
protected function get_action_tags(string $templatename): array {
|
||||
$name = get_string('actions');
|
||||
if ($templatename == 'addtemplate' || $templatename == 'asearchtemplate') {
|
||||
return $this->get_optgroup_data($name, []);
|
||||
}
|
||||
$taglist = [
|
||||
'##edit##' => get_string('edit', 'data'),
|
||||
'##delete##' => get_string('delete', 'data'),
|
||||
'##approve##' => get_string('approve', 'data'),
|
||||
'##disapprove##' => get_string('disapprove', 'data'),
|
||||
];
|
||||
if ($templatename != 'rsstemplate') {
|
||||
$taglist['##export##'] = get_string('export', 'data');
|
||||
}
|
||||
if ($templatename != 'singletemplate') {
|
||||
$taglist['##more##'] = get_string('more', 'data');
|
||||
$taglist['##moreurl##'] = get_string('moreurl', 'data');
|
||||
$taglist['##delcheck##'] = get_string('delcheck', 'data');
|
||||
}
|
||||
return $this->get_optgroup_data($name, $taglist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the available other tags
|
||||
*
|
||||
* @param string $templatename the template name
|
||||
* @return array associative array of tags => tag name
|
||||
*/
|
||||
protected function get_other_tags(string $templatename): array {
|
||||
$name = get_string('other', 'data');
|
||||
$taglist = [];
|
||||
if ($templatename == 'asearchtemplate') {
|
||||
$taglist['##firstname##'] = get_string('firstname');
|
||||
$taglist['##lastname##'] = get_string('lastname');
|
||||
return $this->get_optgroup_data($name, $taglist);
|
||||
}
|
||||
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
$taglist['##tags##'] = get_string('tags');
|
||||
}
|
||||
if ($templatename == 'addtemplate') {
|
||||
return $this->get_optgroup_data($name, $taglist);
|
||||
}
|
||||
$taglist['##timeadded##'] = get_string('timeadded', 'data');
|
||||
$taglist['##timemodified##'] = get_string('timemodified', 'data');
|
||||
$taglist['##user##'] = get_string('user');
|
||||
$taglist['##userpicture##'] = get_string('userpic');
|
||||
$taglist['##approvalstatus##'] = get_string('approvalstatus', 'data');
|
||||
$taglist['##id##'] = get_string('id', 'data');
|
||||
|
||||
if ($templatename == 'singletemplate') {
|
||||
return $this->get_optgroup_data($name, $taglist);
|
||||
}
|
||||
|
||||
$taglist['##comments##'] = get_string('comments', 'data');
|
||||
|
||||
return $this->get_optgroup_data($name, $taglist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a valid optgroup data.
|
||||
*
|
||||
* @param string $name the optgroup name
|
||||
* @param array $taglist the indexed array of taglists ($tag => $tagname)
|
||||
* @return array of optgroup data
|
||||
*/
|
||||
protected function get_optgroup_data (string $name, array $taglist): array {
|
||||
$tags = [];
|
||||
foreach ($taglist as $tag => $tagname) {
|
||||
$tags[] = [
|
||||
'tag' => "$tag",
|
||||
'tagname' => $tagname . ' - ' . $tag,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'name' => $name,
|
||||
'tags' => $tags,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
// 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/>.
|
||||
|
||||
namespace mod_data;
|
||||
|
||||
use core\output\checkbox_toggleall;
|
||||
use html_writer;
|
||||
use mod_data\manager;
|
||||
use moodle_url;
|
||||
use pix_icon;
|
||||
use stdClass;
|
||||
use user_picture;
|
||||
use core_user;
|
||||
use portfolio_add_button;
|
||||
use data_portfolio_caller;
|
||||
use comment;
|
||||
use core_tag_tag;
|
||||
|
||||
/**
|
||||
* Class template for database activity
|
||||
*
|
||||
* @package mod_data
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class template {
|
||||
|
||||
/** @var manager the current instance manager. */
|
||||
private $manager;
|
||||
|
||||
/** @var stdClass the current instance record. */
|
||||
private $instance;
|
||||
|
||||
/** @var string the template. */
|
||||
private $templatecontent;
|
||||
|
||||
/** @var moodle_url the base url. */
|
||||
private $baseurl;
|
||||
|
||||
/** @var string the current search if any. */
|
||||
private $search;
|
||||
|
||||
/** @var bool if ratings must be added. */
|
||||
private $ratings;
|
||||
|
||||
/** @var bool if comments must be added if not present in the template. */
|
||||
private $forcecomments;
|
||||
|
||||
/** @var bool if the current user can manage entries. */
|
||||
private $canmanageentries = null;
|
||||
|
||||
/** @var array if icons HTML. */
|
||||
private $icons = [];
|
||||
|
||||
/** @var array All template tags (calculated in load_template_tags). */
|
||||
protected $tags = [];
|
||||
|
||||
/**
|
||||
* Class contructor.
|
||||
*
|
||||
* See the add_options method for the available display options.
|
||||
*
|
||||
* @param manager $manager the current instance manager
|
||||
* @param string $templatecontent the template string to use
|
||||
* @param array $options an array of extra diplay options
|
||||
*/
|
||||
public function __construct(manager $manager, string $templatecontent, array $options = []) {
|
||||
$this->manager = $manager;
|
||||
$this->instance = $manager->get_instance();
|
||||
$this->templatecontent = $templatecontent;
|
||||
|
||||
$context = $manager->get_context();
|
||||
$this->canmanageentries = has_capability('mod/data:manageentries', $context);
|
||||
$this->icons = $this->get_icons();
|
||||
$this->add_options($options);
|
||||
$this->load_template_tags($templatecontent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra display options.
|
||||
*
|
||||
* The extra options are:
|
||||
* - page: the current pagination page
|
||||
* - search: the current search text
|
||||
* - baseurl: an alternative entry url (moodle_url)
|
||||
* - comments: if comments must be added if not present
|
||||
* - ratings: if ratings must be added
|
||||
*
|
||||
* @param array $options the array of options.
|
||||
*/
|
||||
public function add_options(array $options = []) {
|
||||
$cm = $this->manager->get_coursemodule();
|
||||
$baseurl = $options['baseurl'] ?? new moodle_url('/mod/data/view.php', ['id' => $cm->id]);
|
||||
if (isset($options['page'])) {
|
||||
$baseurl->params([
|
||||
'page' => $options['page'],
|
||||
]);
|
||||
}
|
||||
$this->baseurl = $baseurl;
|
||||
|
||||
// Save options.
|
||||
$this->search = $options['search'] ?? null;
|
||||
$this->ratings = $options['ratings'] ?? false;
|
||||
$this->forcecomments = $options['comments'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the template tags.
|
||||
*
|
||||
* This method detects which tags are used in this template and store them
|
||||
* in the $this->tags attribute. This attribute will be used to determine
|
||||
* which replacements needs to be calculated.
|
||||
*
|
||||
* @param string $templatecontent the current template
|
||||
*/
|
||||
protected function load_template_tags(string $templatecontent) {
|
||||
// Detect action tags.
|
||||
$pattern = '/##(?P<tags>\w+?)##/';
|
||||
$matches = [];
|
||||
preg_match_all($pattern, $templatecontent, $matches);
|
||||
if (!isset($matches['tags']) || empty($matches['tags'])) {
|
||||
return;
|
||||
}
|
||||
$this->tags = $matches['tags'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the list of action icons.
|
||||
*
|
||||
* @return pix_icon[] icon name => pix_icon
|
||||
*/
|
||||
protected function get_icons() {
|
||||
$attrs = ['class' => 'iconsmall'];
|
||||
return [
|
||||
'edit' => new pix_icon('t/edit', get_string('edit'), '', $attrs),
|
||||
'delete' => new pix_icon('t/delete', get_string('delete'), '', $attrs),
|
||||
'more' => new pix_icon('t/preview', get_string('more', 'data'), '', $attrs),
|
||||
'approve' => new pix_icon('t/approve', get_string('approve', 'data'), '', $attrs),
|
||||
'disapprove' => new pix_icon('t/block', get_string('disapprove', 'data'), '', $attrs),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parsed entry using a template.
|
||||
*
|
||||
* This method apply a template replacing all necessary tags.
|
||||
*
|
||||
* @param array $entries of entres to parse
|
||||
* @return string the entries outputs using the template
|
||||
*/
|
||||
public function parse_entries(array $entries): string {
|
||||
if (empty($entries)) {
|
||||
return '';
|
||||
}
|
||||
$result = '';
|
||||
foreach ($entries as $entry) {
|
||||
$result .= $this->parse_entry($entry);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single entry.
|
||||
*
|
||||
* @param stdClass $entry the entry to parse
|
||||
* @return string the parsed entry
|
||||
*/
|
||||
private function parse_entry(stdClass $entry): string {
|
||||
if (empty($this->templatecontent)) {
|
||||
return '';
|
||||
}
|
||||
$context = $this->manager->get_context();
|
||||
$canmanageentry = data_user_can_manage_entry($entry, $this->instance, $context);
|
||||
|
||||
// Load all replacements for the entry.
|
||||
$fields = $this->get_fields_replacements($entry);
|
||||
$tags = $this->get_tags_replacements($entry, $canmanageentry);
|
||||
$replacements = array_merge($fields, $tags);
|
||||
|
||||
$patterns = array_keys($replacements);
|
||||
$replacement = array_values($replacements);
|
||||
$result = str_ireplace($patterns, $replacement, $this->templatecontent);
|
||||
|
||||
return $this->post_parse($result, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all field replacements.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @return array of pattern => replacement
|
||||
*/
|
||||
private function get_fields_replacements(stdClass $entry): array {
|
||||
$result = [];
|
||||
$fields = $this->manager->get_fields();
|
||||
foreach ($fields as $field) {
|
||||
// Field value.
|
||||
$pattern = '[[' . $field->field->name . ']]';
|
||||
$result[$pattern] = highlight(
|
||||
$this->search,
|
||||
$field->display_browse_field($entry->id, $this->templatecontent)
|
||||
);
|
||||
// Field id.
|
||||
$pattern = '[[' . $field->field->name . '#id]]';
|
||||
$result[$pattern] = $field->field->id;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all standard tags replacements.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return array of pattern => replacement
|
||||
*/
|
||||
private function get_tags_replacements(stdClass $entry, bool $canmanageentry): array {
|
||||
$result = [];
|
||||
foreach ($this->tags as $tagname) {
|
||||
$methodname = "get_tag_{$tagname}_replacement";
|
||||
if (method_exists($this, $methodname)) {
|
||||
$pattern = "##$tagname##";
|
||||
$replacement = $this->$methodname($entry, $canmanageentry);
|
||||
$result[$pattern] = $replacement;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any extra information to the parsed entry.
|
||||
*
|
||||
* @param string $result the parsed template with the entry data
|
||||
* @param stdClass $entry the entry object
|
||||
* @return string the final parsed template
|
||||
*/
|
||||
private function post_parse(string $result, stdClass $entry): string {
|
||||
if ($this->ratings) {
|
||||
$result .= data_print_ratings($this->instance, $entry, false);
|
||||
}
|
||||
if ($this->forcecomments && strpos($this->templatecontent, '##comments##') === false) {
|
||||
$result .= $this->get_tag_comments_replacement($entry, false);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##edit## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_edit_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
if (!$canmanageentry) {
|
||||
return '';
|
||||
}
|
||||
$backurl = new moodle_url($this->baseurl, [
|
||||
'rid' => $entry->id,
|
||||
'mode' => 'single',
|
||||
]);
|
||||
$url = new moodle_url('/mod/data/edit.php', $this->baseurl->params());
|
||||
$url->params([
|
||||
'rid' => $entry->id,
|
||||
'sesskey' => sesskey(),
|
||||
'backto' => urlencode($backurl->out(false))
|
||||
]);
|
||||
return html_writer::tag(
|
||||
'span',
|
||||
$OUTPUT->action_icon($url, $this->icons['edit']),
|
||||
['class' => 'edit']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##delete## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_delete_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
if (!$canmanageentry) {
|
||||
return '';
|
||||
}
|
||||
$url = new moodle_url($this->baseurl, [
|
||||
'delete' => $entry->id,
|
||||
'sesskey' => sesskey(),
|
||||
'mode' => 'single',
|
||||
]);
|
||||
|
||||
return html_writer::tag(
|
||||
'span',
|
||||
$OUTPUT->action_icon($url, $this->icons['delete']),
|
||||
['class' => 'delete']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##more## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_more_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
$url = new moodle_url($this->baseurl, [
|
||||
'rid' => $entry->id,
|
||||
'filter' => 1,
|
||||
]);
|
||||
return html_writer::tag(
|
||||
'span',
|
||||
$OUTPUT->action_icon($url, $this->icons['more']),
|
||||
['class' => 'more']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##moreurl## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_moreurl_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
$url = new moodle_url($this->baseurl, [
|
||||
'rid' => $entry->id,
|
||||
'filter' => 1,
|
||||
]);
|
||||
return $url->out(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##delcheck## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_delcheck_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
if (!$this->canmanageentries) {
|
||||
return '';
|
||||
}
|
||||
$checkbox = new checkbox_toggleall('listview-entries', false, [
|
||||
'id' => "entry_{$entry->id}",
|
||||
'name' => 'delcheck[]',
|
||||
'classes' => 'recordcheckbox',
|
||||
'value' => $entry->id,
|
||||
]);
|
||||
return $OUTPUT->render($checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##user## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_user_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
$cm = $this->manager->get_coursemodule();
|
||||
$url = new moodle_url('/user/view.php', [
|
||||
'id' => $entry->userid,
|
||||
'course' => $cm->course,
|
||||
]);
|
||||
return html_writer::tag(
|
||||
'a',
|
||||
fullname($entry),
|
||||
['href' => $url->out(false)]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##userpicture## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_userpicture_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
$cm = $this->manager->get_coursemodule();
|
||||
$user = user_picture::unalias($entry, null, 'userid');
|
||||
// If the record didn't come with user data, retrieve the user from database.
|
||||
if (!isset($user->picture)) {
|
||||
$user = core_user::get_user($entry->userid);
|
||||
}
|
||||
return $OUTPUT->user_picture($user, ['courseid' => $cm->course]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##export## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_export_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $CFG;
|
||||
if (empty($CFG->enableportfolios)) {
|
||||
return '';
|
||||
}
|
||||
// Check the user can export the entry.
|
||||
$cm = $this->manager->get_coursemodule();
|
||||
$context = $this->manager->get_context();
|
||||
$canexportall = has_capability('mod/data:exportentry', $context);
|
||||
$canexportown = has_capability('mod/data:exportownentry', $context);
|
||||
if (!$canexportall && !(data_isowner($entry->id) && $canexportown)) {
|
||||
return '';
|
||||
}
|
||||
// Add the portfolio export button.
|
||||
require_once($CFG->libdir . '/portfoliolib.php');
|
||||
$button = new portfolio_add_button();
|
||||
$button->set_callback_options(
|
||||
'data_portfolio_caller',
|
||||
['id' => $cm->id, 'recordid' => $entry->id],
|
||||
'mod_data'
|
||||
);
|
||||
$fields = $this->manager->get_fields();
|
||||
list($formats, $files) = data_portfolio_caller::formats($fields, $entry);
|
||||
$button->set_formats($formats);
|
||||
return $button->to_html(PORTFOLIO_ADD_ICON_LINK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##timeadded## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_timeadded_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
return userdate($entry->timecreated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##timemodified## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_timemodified_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
return userdate($entry->timemodified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##approve## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_approve_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
$context = $this->manager->get_context();
|
||||
if (!has_capability('mod/data:approve', $context) || !$this->instance->approval || $entry->approved) {
|
||||
return '';
|
||||
}
|
||||
$url = new moodle_url($this->baseurl, [
|
||||
'approve' => $entry->id,
|
||||
'sesskey' => sesskey(),
|
||||
]);
|
||||
return html_writer::tag(
|
||||
'span',
|
||||
$OUTPUT->action_icon($url, $this->icons['approve']),
|
||||
['class' => 'approve']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##disapprove## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_disapprove_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
$context = $this->manager->get_context();
|
||||
if (!has_capability('mod/data:approve', $context) || !$this->instance->approval || !$entry->approved) {
|
||||
return '';
|
||||
}
|
||||
$url = new moodle_url($this->baseurl, [
|
||||
'disapprove' => $entry->id,
|
||||
'sesskey' => sesskey(),
|
||||
]);
|
||||
return html_writer::tag(
|
||||
'span',
|
||||
$OUTPUT->action_icon($url, $this->icons['disapprove']),
|
||||
['class' => 'disapprove']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##approvalstatus## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_approvalstatus_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
if (!$this->instance->approval) {
|
||||
return '';
|
||||
}
|
||||
return ($entry->approved) ? get_string('approved', 'data') : get_string('notapproved', 'data');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##approvalstatusclass## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_approvalstatusclass_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
if (!$this->instance->approval) {
|
||||
return '';
|
||||
}
|
||||
return ($entry->approved) ? 'approved' : 'notapproved';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##comments## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_comments_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $CFG;
|
||||
if (empty($CFG->usecomments) || empty($this->instance->comments)) {
|
||||
return '';
|
||||
}
|
||||
$context = $this->manager->get_context();
|
||||
require_once($CFG->dirroot . '/comment/lib.php');
|
||||
list($context, $course, $cm) = get_context_info_array($context->id);
|
||||
$cmdata = (object)[
|
||||
'context' => $context,
|
||||
'course' => $course,
|
||||
'cm' => $cm,
|
||||
'area' => 'database_entry',
|
||||
'itemid' => $entry->id,
|
||||
'showcount' => true,
|
||||
'component' => 'mod_data',
|
||||
];
|
||||
$comment = new comment($cmdata);
|
||||
return $comment->output(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##tags## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_tags_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
global $OUTPUT;
|
||||
if (!core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
||||
return '';
|
||||
}
|
||||
return $OUTPUT->tag_list(
|
||||
core_tag_tag::get_item_tags('mod_data', 'data_records', $entry->id),
|
||||
'',
|
||||
'data-tags'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ##id## tag replacement for an entry.
|
||||
*
|
||||
* @param stdClass $entry the entry object
|
||||
* @param bool $canmanageentry if the current user can manage this entry
|
||||
* @return string the tag replacement
|
||||
*/
|
||||
protected function get_tag_id_replacement(stdClass $entry, bool $canmanageentry): string {
|
||||
return (string) $entry->id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +52,6 @@ Only the tags that are in the "Available tags" list may be used for the current
|
||||
$string['availabletodate'] = 'Available to';
|
||||
$string['availabletodatevalidation'] = 'The available to date cannot be before the available from date.';
|
||||
$string['blank'] = 'Blank';
|
||||
$string['buttons'] = 'Actions';
|
||||
$string['bynameondate'] = 'by {$a->name} - {$a->date}';
|
||||
$string['calendarend'] = '{$a} closes';
|
||||
$string['calendarstart'] = '{$a} opens';
|
||||
@@ -203,6 +202,7 @@ $string['headerlisttemplate'] = 'Defines browsing interface for multiple entries
|
||||
$string['headerrsstemplate'] = 'Defines appearance of entries in RSS feeds';
|
||||
$string['headersingletemplate'] = 'Defines browsing interface for a single entry';
|
||||
$string['checkbox'] = 'Checkbox';
|
||||
$string['id'] = 'Entry ID';
|
||||
$string['chooseexportfields'] = 'Choose the fields you wish to export';
|
||||
$string['chooseexportformat'] = 'Choose the format you wish to export to';
|
||||
$string['chooseorupload'] = 'Choose file';
|
||||
@@ -432,3 +432,6 @@ $string['wrongdataid'] = 'Wrong data id provided';
|
||||
|
||||
// Deprecated since Moodle 3.11.
|
||||
$string['unsupportedexport'] = '({$a->fieldtype}) cannot be exported.';
|
||||
|
||||
// Deprecated since Moodle 4.1.
|
||||
$string['buttons'] = 'Actions';
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
unsupportedexport,mod_data
|
||||
buttons,mod_data
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user