├── README.md ├── pix ├── icon.png └── icon.svg ├── amd ├── build │ └── confirm.min.js └── src │ └── confirm.js ├── db ├── events.php ├── install.xml ├── access.php ├── services.php └── upgrade.php ├── version.php ├── .travis.yml ├── classes ├── renderer.php ├── output │ └── entries_list.php ├── form.php ├── event │ ├── entry_created.php │ ├── entry_deleted.php │ └── entry_updated.php ├── external.php ├── table.php └── api.php ├── lang └── en │ └── tool_devcourse.php ├── templates └── entries_list.mustache ├── index.php ├── edit.php ├── tests ├── behat │ └── editing.feature ├── events_test.php └── api_test.php └── lib.php /README.md: -------------------------------------------------------------------------------- 1 | Plugin tool_devcourse 2 | -------------------------------------------------------------------------------- /pix/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marinaglancy/moodle-tool_devcourse/master/pix/icon.png -------------------------------------------------------------------------------- /amd/build/confirm.min.js: -------------------------------------------------------------------------------- 1 | define(["jquery","core/str","core/notification","core/ajax","core/templates"],function(a,b,c,d,e){var f=function(a,d){b.get_strings([{key:"delete"},{key:"confirmdeleteentry",component:"tool_devcourse"},{key:"yes"},{key:"no"}]).done(function(b){c.confirm(b[0],b[1],b[2],b[3],function(){g(a,d)})}).fail(c.exception)},g=function(a,b){var e=b.attr("data-courseid"),f=d.call([{methodname:"tool_devcourse_delete_entry",args:{id:a}},{methodname:"tool_devcourse_entries_list",args:{courseid:e}}]);f[1].done(function(a){h(a,b)}).fail(c.exception)},h=function(a,b){e.render("tool_devcourse/entries_list",a).done(function(a){b.replaceWith(a)})},i=function(b){a(b).on("click",function(b){b.preventDefault();var c=a(b.currentTarget).attr("data-entryid"),d=a(b.currentTarget).closest(".tool_devcourse_entries_list");f(c,d)})};return{init:function(a){i(a)}}}); -------------------------------------------------------------------------------- /db/events.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Add event handlers for the tool_devcourse 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $observers = array( 29 | 30 | array( 31 | 'eventname' => '\core\event\course_deleted', 32 | 'callback' => 'tool_devcourse_api::course_deleted_observer', 33 | ), 34 | ); 35 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Version information 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $plugin->version = 2018072500; // The current plugin version (Date: YYYYMMDDXX). 28 | $plugin->requires = 2018050800; // Requires this Moodle version. 29 | $plugin->release = 'v2.5'; // Release name. 30 | $plugin->maturity = MATURITY_STABLE; // Maturity. 31 | $plugin->component = 'tool_devcourse'; // Full name of the plugin (used for diagnostics). 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | # Workaround for fixing that Selenium server is not running and therefore javascript Behat tests are not working: 4 | # https://github.com/moodlerooms/moodle-plugin-ci/issues/70 5 | sudo: required 6 | # ORIGINAL: 7 | # sudo: false 8 | 9 | addons: 10 | firefox: "47.0.1" 11 | postgresql: "9.6" 12 | apt: 13 | packages: 14 | - oracle-java8-installer 15 | - oracle-java8-set-default 16 | 17 | cache: 18 | directories: 19 | - $HOME/.composer/cache 20 | - $HOME/.npm 21 | 22 | php: 23 | - 7.0 24 | - 7.2 25 | 26 | env: 27 | global: 28 | - MOODLE_BRANCH=master 29 | matrix: 30 | - DB=pgsql 31 | - DB=mysqli 32 | 33 | before_install: 34 | - phpenv config-rm xdebug.ini 35 | - nvm install 8.9 36 | - nvm use 8.9 37 | - cd ../../.. 38 | - composer create-project -n --no-dev --prefer-dist moodlerooms/moodle-plugin-ci ci ^2 39 | - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH" 40 | 41 | install: 42 | - moodle-plugin-ci install 43 | 44 | script: 45 | - moodle-plugin-ci phplint 46 | - moodle-plugin-ci phpcpd 47 | - moodle-plugin-ci phpmd 48 | - moodle-plugin-ci codechecker 49 | - moodle-plugin-ci validate 50 | - moodle-plugin-ci savepoints 51 | - moodle-plugin-ci mustache 52 | - moodle-plugin-ci grunt -t eslint:amd -t uglify 53 | - moodle-plugin-ci phpunit 54 | - moodle-plugin-ci behat 55 | -------------------------------------------------------------------------------- /classes/renderer.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse_renderer 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | use tool_devcourse\output\entries_list; 28 | 29 | /** 30 | * Renderer for tool_devcourse 31 | * 32 | * @package tool_devcourse 33 | * @copyright 2018 Marina Glancy 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class tool_devcourse_renderer extends plugin_renderer_base { 37 | 38 | /** 39 | * Renders an entries list. 40 | * 41 | * @param entries_list $list 42 | * @return string HTML 43 | */ 44 | protected function render_entries_list(entries_list $list) { 45 | $context = $list->export_for_template($this); 46 | return $this->render_from_template('tool_devcourse/entries_list', $context); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /db/install.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
-------------------------------------------------------------------------------- /db/access.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Capability definitions for this module. 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $capabilities = [ 28 | 29 | // Capability for viewing data. 30 | 'tool/devcourse:view' => [ 31 | 'captype' => 'read', 32 | 'contextlevel' => CONTEXT_COURSE, 33 | 'archetypes' => array( 34 | 'student' => CAP_ALLOW, 35 | 'teacher' => CAP_ALLOW, 36 | 'editingteacher' => CAP_ALLOW, 37 | 'manager' => CAP_ALLOW 38 | ) 39 | ], 40 | 41 | // Capability for editing data. 42 | 'tool/devcourse:edit' => [ 43 | 'riskbitmask' => RISK_SPAM | RISK_XSS, 44 | 'captype' => 'write', 45 | 'contextlevel' => CONTEXT_COURSE, 46 | 'archetypes' => [ 47 | 'editingteacher' => CAP_ALLOW, 48 | 'manager' => CAP_ALLOW, 49 | ] 50 | ], 51 | ]; 52 | -------------------------------------------------------------------------------- /db/services.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Web services for tool_devcourse 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | // We defined the web service functions to install. 28 | $functions = array( 29 | 'tool_devcourse_delete_entry' => array( 30 | 'classname' => 'tool_devcourse_external', 31 | 'methodname' => 'delete_entry', 32 | 'description' => 'Deletes an entry', 33 | 'type' => 'write', 34 | 'capabilities' => 'tool/devcourse:edit', 35 | 'ajax' => true, 36 | ), 37 | 'tool_devcourse_entries_list' => array( 38 | 'classname' => 'tool_devcourse_external', 39 | 'methodname' => 'entries_list', 40 | 'description' => 'Returns list of entries', 41 | 'type' => 'read', 42 | 'capabilities' => 'tool/devcourse:view', 43 | 'ajax' => true, 44 | ), 45 | ); 46 | -------------------------------------------------------------------------------- /lang/en/tool_devcourse.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Strings for component 'tool_devcourse', language 'en' 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $string['completed'] = 'Completed'; 28 | $string['confirmdeleteentry'] = 'Are you sure you want to delete this entry?'; 29 | $string['description'] = 'Description'; 30 | $string['devcourse:edit'] = 'Edit devcourse data'; 31 | $string['devcourse:view'] = 'View devcourse data'; 32 | $string['editentry'] = 'Edit entry'; 33 | $string['errornameexists'] = 'Name must be unique in this course'; 34 | $string['evententrydeleted'] = 'Entry deleted'; 35 | $string['evententrycreated'] = 'Entry created'; 36 | $string['evententryupdated'] = 'Entry updated'; 37 | $string['helloworld'] = 'Hello world!'; 38 | $string['name'] = 'Name'; 39 | $string['newentry'] = 'New entry'; 40 | $string['pluginname'] = 'Dev course example'; 41 | $string['priority'] = 'Priority'; 42 | $string['timecreated'] = 'Created'; 43 | $string['timemodified'] = 'Modified'; 44 | $string['youareviewing'] = 'You are viewing course {$a}'; 45 | -------------------------------------------------------------------------------- /templates/entries_list.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template tool_devcourse/entries_list 19 | 20 | Template for the list of etries. 21 | 22 | Classes required for JS: 23 | * tool_devcourse_entries_list 24 | 25 | Data attributes required for JS: 26 | * data-entryid 27 | * data-action 28 | * data-courseid 29 | 30 | Context variables required for this template: 31 | - 32 | 33 | Example context (json): 34 | { 35 | "courseid": 2, 36 | "coursename": "Test course", 37 | "contents": "Here goes the list of entries", 38 | "addlink": "#" 39 | } 40 | }} 41 | 42 |
43 |

{{#str}} helloworld , tool_devcourse {{/str}}

44 |

{{#str}} youareviewing , tool_devcourse , {{courseid}} {{/str}}

45 |

{{{coursename}}}

46 | 47 | {{{contents}}} 48 | 49 | {{#addlink}} 50 |
{{#str}} newentry , tool_devcourse {{/str}}
51 | {{/addlink}} 52 |
53 | 54 | {{#js}} 55 | require( 56 | [ 57 | 'tool_devcourse/confirm' 58 | ], 59 | function( 60 | c 61 | ) { 62 | c.init('[data-action=deleteentry]'); 63 | }); 64 | 65 | {{/js}} 66 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Main file 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require_once(__DIR__ . '/../../../config.php'); 26 | 27 | $courseid = required_param('id', PARAM_INT); 28 | 29 | $url = new moodle_url('/admin/tool/devcourse/index.php', ['id' => $courseid]); 30 | $PAGE->set_url($url); 31 | 32 | require_login($courseid); 33 | $context = context_course::instance($courseid); 34 | require_capability('tool/devcourse:view', $context); 35 | 36 | $PAGE->set_title(get_string('helloworld', 'tool_devcourse')); 37 | $PAGE->set_heading(get_string('pluginname', 'tool_devcourse')); 38 | 39 | // Deleting an entry if specified. 40 | if ($deleteid = optional_param('delete', null, PARAM_INT)) { 41 | require_sesskey(); 42 | $record = tool_devcourse_api::retrieve($deleteid, $courseid); 43 | require_capability('tool/devcourse:edit', $PAGE->context); 44 | tool_devcourse_api::delete($record->id); 45 | redirect(new moodle_url('/admin/tool/devcourse/index.php', ['id' => $courseid])); 46 | } 47 | 48 | $outputpage = new \tool_devcourse\output\entries_list($courseid); 49 | $output = $PAGE->get_renderer('tool_devcourse'); 50 | echo $output->header(); 51 | echo $output->render($outputpage); 52 | echo $output->footer(); 53 | -------------------------------------------------------------------------------- /edit.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Editing or creating entries 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require_once(__DIR__ . '/../../../config.php'); 26 | 27 | $id = optional_param('id', 0, PARAM_INT); 28 | if ($id) { 29 | // We are going to edit an entry. 30 | $entry = tool_devcourse_api::retrieve($id); 31 | $courseid = $entry->courseid; 32 | $urlparams = ['id' => $id]; 33 | $title = get_string('newentry', 'tool_devcourse'); 34 | } else { 35 | // We are going to add an entry. Parameter courseid is required. 36 | $courseid = required_param('courseid', PARAM_INT); 37 | $entry = (object)['courseid' => $courseid]; 38 | $urlparams = ['courseid' => $courseid]; 39 | $title = get_string('editentry', 'tool_devcourse'); 40 | } 41 | 42 | $url = new moodle_url('/admin/tool/devcourse/edit.php', $urlparams); 43 | $PAGE->set_url($url); 44 | 45 | require_login($courseid); 46 | $context = context_course::instance($courseid); 47 | require_capability('tool/devcourse:edit', $context); 48 | 49 | $PAGE->set_title($title); 50 | $PAGE->set_heading(get_string('pluginname', 'tool_devcourse')); 51 | 52 | $form = new tool_devcourse_form(); 53 | if (!empty($entry->id)) { 54 | file_prepare_standard_editor($entry, 'description', 55 | tool_devcourse_api::editor_options($courseid), 56 | $PAGE->context, 'tool_devcourse', 'entry', $entry->id); 57 | } 58 | $form->set_data($entry); 59 | 60 | $returnurl = new moodle_url('/admin/tool/devcourse/index.php', ['id' => $courseid]); 61 | if ($form->is_cancelled()) { 62 | redirect($returnurl); 63 | } else if ($data = $form->get_data()) { 64 | if ($data->id) { 65 | // Update entry. 66 | tool_devcourse_api::update($data); 67 | } else { 68 | // Add entry. 69 | tool_devcourse_api::insert($data); 70 | } 71 | redirect($returnurl); 72 | } 73 | 74 | echo $OUTPUT->header(); 75 | echo $OUTPUT->heading($title); 76 | 77 | $form->display(); 78 | 79 | echo $OUTPUT->footer(); -------------------------------------------------------------------------------- /classes/output/entries_list.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse\output\entries_list 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace tool_devcourse\output; 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | use renderer_base; 30 | use moodle_url; 31 | use tool_devcourse_table; 32 | use context_course; 33 | 34 | /** 35 | * Class tool_devcourse\output\entries_list 36 | * 37 | * @package tool_devcourse 38 | * @copyright 2018 Marina Glancy 39 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 | */ 41 | class entries_list implements \templatable, \renderable { 42 | 43 | /** @var int */ 44 | protected $courseid; 45 | 46 | /** 47 | * entries_list constructor. 48 | * @param int $courseid 49 | */ 50 | public function __construct($courseid) { 51 | $this->courseid = $courseid; 52 | } 53 | 54 | /** 55 | * Implementation of exporter from templatable interface 56 | * 57 | * @param renderer_base $output 58 | * @return array 59 | */ 60 | public function export_for_template(renderer_base $output) { 61 | $course = get_course($this->courseid); 62 | $context = context_course::instance($this->courseid); 63 | $data = [ 64 | 'courseid' => $this->courseid, 65 | 'coursename' => format_string($course->fullname, true, ['context' => $context]) 66 | ]; 67 | 68 | // Display table. 69 | ob_start(); 70 | $table = new tool_devcourse_table('tool_devcourse', $this->courseid); 71 | $table->out(20, false); 72 | $data['contents'] = ob_get_clean(); 73 | 74 | // Link to add new entry. 75 | if (has_capability('tool/devcourse:edit', $context)) { 76 | $url = new moodle_url('/admin/tool/devcourse/edit.php', ['courseid' => $this->courseid]); 77 | // Link will be escaped inside template so no need to escape it now. 78 | $data['addlink'] = $url->out(false); 79 | } 80 | return $data; 81 | } 82 | } -------------------------------------------------------------------------------- /classes/form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse_form 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->libdir.'/formslib.php'); 28 | 29 | /** 30 | * Class tool_devcourse_form for displaying an editing form 31 | * 32 | * @package tool_devcourse 33 | * @copyright 2018 Marina Glancy 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class tool_devcourse_form extends moodleform { 37 | 38 | /** 39 | * Form definition 40 | */ 41 | public function definition() { 42 | $mform = $this->_form; 43 | 44 | $mform->addElement('hidden', 'courseid'); 45 | $mform->setType('courseid', PARAM_INT); 46 | 47 | $mform->addElement('hidden', 'id'); 48 | $mform->setType('id', PARAM_INT); 49 | 50 | $mform->addElement('text', 'name', 51 | get_string('name', 'tool_devcourse')); 52 | $mform->setType('name', PARAM_NOTAGS); 53 | 54 | $mform->addElement('advcheckbox', 'completed', 55 | get_string('completed', 'tool_devcourse')); 56 | 57 | $mform->addElement('editor', 'description_editor', 58 | get_string('description', 'tool_devcourse'), 59 | null, tool_devcourse_api::editor_options()); 60 | 61 | $this->add_action_buttons(); 62 | } 63 | 64 | /** 65 | * Form validation 66 | * 67 | * @param array $data 68 | * @param array $files 69 | * @return array 70 | */ 71 | public function validation($data, $files) { 72 | global $DB; 73 | $errors = parent::validation($data, $files); 74 | 75 | // Check that name is unique for the course. 76 | if ($DB->record_exists_select('tool_devcourse', 77 | 'name = :name AND id <> :id AND courseid = :courseid', 78 | ['name' => $data['name'], 'id' => $data['id'], 'courseid' => $data['courseid']])) { 79 | $errors['name'] = get_string('errornameexists', 'tool_devcourse'); 80 | } 81 | 82 | return $errors; 83 | } 84 | } -------------------------------------------------------------------------------- /tests/behat/editing.feature: -------------------------------------------------------------------------------- 1 | @tool @tool_devcourse 2 | Feature: Creating, editing and deleting entries 3 | In order to manage entries 4 | As a teacher 5 | I need to be able to add, edit and delete entries 6 | 7 | Background: 8 | Given the following "users" exist: 9 | | username | firstname | lastname | email | 10 | | teacher1 | Teacher | 1 | teacher1@example.com | 11 | And the following "courses" exist: 12 | | fullname | shortname | format | 13 | | Course 1 | C1 | weeks | 14 | And the following "course enrolments" exist: 15 | | user | course | role | 16 | | teacher1 | C1 | editingteacher | 17 | 18 | Scenario: Add and edit an entry 19 | When I log in as "teacher1" 20 | And I follow "Course 1" 21 | And I navigate to "Dev course example" in current page administration 22 | And I follow "New entry" 23 | And I set the following fields to these values: 24 | | Name | test entry 1 | 25 | | Completed | 0 | 26 | | Description | cat | 27 | And I press "Save changes" 28 | Then the following should exist in the "tool_devcourse_overview" table: 29 | | Name | Completed | Description | 30 | | test entry 1 | No | cat | 31 | And I click on "Edit" "link" in the "test entry 1" "table_row" 32 | And I set the following fields to these values: 33 | | Completed | 1 | 34 | And I press "Save changes" 35 | And the following should exist in the "tool_devcourse_overview" table: 36 | | Name | Description | Completed | 37 | | test entry 1 | cat | Yes | 38 | And I log out 39 | 40 | Scenario: Delete an entry with javascript disabled 41 | When I log in as "teacher1" 42 | And I follow "Course 1" 43 | And I navigate to "Dev course example" in current page administration 44 | And I follow "New entry" 45 | And I set the field "Name" to "test entry 1" 46 | And I press "Save changes" 47 | And I follow "New entry" 48 | And I set the field "Name" to "test entry 2" 49 | And I press "Save changes" 50 | And I click on "Delete" "link" in the "test entry 1" "table_row" 51 | Then I should see "test entry 2" 52 | And I should not see "test entry 1" 53 | And I log out 54 | 55 | @javascript @xxx 56 | Scenario: Delete an entry with javascript enabled 57 | When I log in as "teacher1" 58 | And I am on "Course 1" course homepage 59 | And I navigate to "Dev course example" in current page administration 60 | And I follow "New entry" 61 | And I set the field "Name" to "test entry 1" 62 | And I press "Save changes" 63 | And I follow "New entry" 64 | And I set the field "Name" to "test entry 2" 65 | And I press "Save changes" 66 | And I click on "Delete" "link" in the "test entry 1" "table_row" 67 | And I press "Yes" 68 | Then I should see "test entry 2" 69 | And I should not see "test entry 1" 70 | And I log out 71 | -------------------------------------------------------------------------------- /lib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Callbacks for plugin tool_devcourse 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | /** 28 | * Adds this plugin to the course administration menu 29 | * 30 | * @param navigation_node $navigation The navigation node to extend 31 | * @param stdClass $course The course to object for the tool 32 | * @param context $context The context of the course 33 | * @return void|null return null if we don't want to display the node. 34 | */ 35 | function tool_devcourse_extend_navigation_course($navigation, $course, $context) { 36 | if (has_capability('tool/devcourse:view', $context)) { 37 | $navigation->add( 38 | get_string('pluginname', 'tool_devcourse'), 39 | new moodle_url('/admin/tool/devcourse/index.php', ['id' => $course->id]), 40 | navigation_node::TYPE_SETTING, 41 | get_string('pluginname', 'tool_devcourse'), 42 | 'devcourse', 43 | new pix_icon('icon', '', 'tool_devcourse')); 44 | } 45 | } 46 | 47 | /** 48 | * Serve the embedded files. 49 | * 50 | * @param stdClass $course the course object 51 | * @param stdClass $cm the course module object 52 | * @param context $context the context 53 | * @param string $filearea the name of the file area 54 | * @param array $args extra arguments (itemid, path) 55 | * @param bool $forcedownload whether or not force download 56 | * @param array $options additional options affecting the file serving 57 | * @return bool false if the file not found, just send the file otherwise and do not return anything 58 | */ 59 | function tool_devcourse_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) { 60 | 61 | if ($context->contextlevel != CONTEXT_COURSE) { 62 | return false; 63 | } 64 | 65 | if ($filearea !== 'entry') { 66 | return false; 67 | } 68 | 69 | require_login($course); 70 | require_capability('tool/devcourse:view', $context); 71 | 72 | $itemid = array_shift($args); 73 | 74 | $entry = tool_devcourse_api::retrieve($itemid); 75 | 76 | $filename = array_pop($args); 77 | 78 | if (!$args) { 79 | $filepath = '/'; 80 | } else { 81 | $filepath = '/'.implode('/', $args).'/'; 82 | } 83 | 84 | $fs = get_file_storage(); 85 | $file = $fs->get_file($context->id, 'tool_devcourse', $filearea, $itemid, $filepath, $filename); 86 | 87 | if (!$file) { 88 | return false; 89 | } 90 | 91 | send_stored_file($file, null, 0, $forcedownload, $options); 92 | } 93 | -------------------------------------------------------------------------------- /classes/event/entry_created.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse\event\entry_created 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace tool_devcourse\event; 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | use core\event\base; 30 | 31 | /** 32 | * Class tool_devcourse\event\entry_created 33 | * 34 | * @package tool_devcourse 35 | * @copyright 2018 Marina Glancy 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class entry_created extends base { 39 | 40 | /** 41 | * Initialise the event data. 42 | */ 43 | protected function init() { 44 | $this->data['objecttable'] = 'tool_devcourse'; 45 | $this->data['crud'] = 'c'; 46 | $this->data['edulevel'] = self::LEVEL_TEACHING; 47 | } 48 | 49 | /** 50 | * Returns localised general event name. 51 | * 52 | * @return string 53 | */ 54 | public static function get_name() { 55 | return get_string('evententrycreated', 'tool_devcourse'); 56 | } 57 | 58 | /** 59 | * Returns non-localised description of what happened. 60 | * 61 | * @return string 62 | */ 63 | public function get_description() { 64 | return "The user with id '$this->userid' created the entry with id '$this->objectid'."; 65 | } 66 | 67 | /** 68 | * Returns relevant URL. 69 | * 70 | * @return \moodle_url 71 | */ 72 | public function get_url() { 73 | return new \moodle_url('/admin/tool/devcourse/index.php', ['id' => $this->courseid]); 74 | } 75 | 76 | /** 77 | * Custom validation. 78 | * 79 | * @throws \coding_exception 80 | * @return void 81 | */ 82 | protected function validate_data() { 83 | parent::validate_data(); 84 | 85 | if ($this->contextlevel != CONTEXT_COURSE) { 86 | throw new \coding_exception('Context level must be CONTEXT_COURSE.'); 87 | } 88 | } 89 | 90 | /** 91 | * This is used when restoring course logs where it is required that we 92 | * map the objectid to it's new value in the new course. 93 | * 94 | * @return string the name of the restore mapping the objectid links to 95 | */ 96 | public static function get_objectid_mapping() { 97 | return base::NOT_MAPPED; 98 | } 99 | 100 | /** 101 | * This is used when restoring course logs where it is required that we 102 | * map the information in 'other' to it's new value in the new course. 103 | * 104 | * @return array an array of other values and their corresponding mapping 105 | */ 106 | public static function get_other_mapping() { 107 | // Nothing to map. 108 | return false; 109 | } 110 | } -------------------------------------------------------------------------------- /classes/event/entry_deleted.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse\event\entry_deleted 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace tool_devcourse\event; 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | use core\event\base; 30 | 31 | /** 32 | * Class tool_devcourse\event\entry_deleted 33 | * 34 | * @package tool_devcourse 35 | * @copyright 2018 Marina Glancy 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class entry_deleted extends base { 39 | 40 | /** 41 | * Initialise the event data. 42 | */ 43 | protected function init() { 44 | $this->data['objecttable'] = 'tool_devcourse'; 45 | $this->data['crud'] = 'd'; 46 | $this->data['edulevel'] = self::LEVEL_TEACHING; 47 | } 48 | 49 | /** 50 | * Returns localised general event name. 51 | * 52 | * @return string 53 | */ 54 | public static function get_name() { 55 | return get_string('evententrydeleted', 'tool_devcourse'); 56 | } 57 | 58 | /** 59 | * Returns non-localised description of what happened. 60 | * 61 | * @return string 62 | */ 63 | public function get_description() { 64 | return "The user with id '$this->userid' deleted the entry with id '$this->objectid'."; 65 | } 66 | 67 | /** 68 | * Returns relevant URL. 69 | * 70 | * @return \moodle_url 71 | */ 72 | public function get_url() { 73 | return new \moodle_url('/admin/tool/devcourse/index.php', ['id' => $this->courseid]); 74 | } 75 | 76 | /** 77 | * Custom validation. 78 | * 79 | * @throws \coding_exception 80 | * @return void 81 | */ 82 | protected function validate_data() { 83 | parent::validate_data(); 84 | 85 | if ($this->contextlevel != CONTEXT_COURSE) { 86 | throw new \coding_exception('Context level must be CONTEXT_COURSE.'); 87 | } 88 | } 89 | 90 | /** 91 | * This is used when restoring course logs where it is required that we 92 | * map the objectid to it's new value in the new course. 93 | * 94 | * @return string the name of the restore mapping the objectid links to 95 | */ 96 | public static function get_objectid_mapping() { 97 | return base::NOT_MAPPED; 98 | } 99 | 100 | /** 101 | * This is used when restoring course logs where it is required that we 102 | * map the information in 'other' to it's new value in the new course. 103 | * 104 | * @return array an array of other values and their corresponding mapping 105 | */ 106 | public static function get_other_mapping() { 107 | // Nothing to map. 108 | return false; 109 | } 110 | } -------------------------------------------------------------------------------- /classes/event/entry_updated.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse\event\entry_updated 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace tool_devcourse\event; 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | use core\event\base; 30 | 31 | /** 32 | * Class tool_devcourse\event\entry_updated 33 | * 34 | * @package tool_devcourse 35 | * @copyright 2018 Marina Glancy 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class entry_updated extends base { 39 | 40 | /** 41 | * Initialise the event data. 42 | */ 43 | protected function init() { 44 | $this->data['objecttable'] = 'tool_devcourse'; 45 | $this->data['crud'] = 'u'; 46 | $this->data['edulevel'] = self::LEVEL_TEACHING; 47 | } 48 | 49 | /** 50 | * Returns localised general event name. 51 | * 52 | * @return string 53 | */ 54 | public static function get_name() { 55 | return get_string('evententryupdated', 'tool_devcourse'); 56 | } 57 | 58 | /** 59 | * Returns non-localised description of what happened. 60 | * 61 | * @return string 62 | */ 63 | public function get_description() { 64 | return "The user with id '$this->userid' updated the entry with id '$this->objectid'."; 65 | } 66 | 67 | /** 68 | * Returns relevant URL. 69 | * 70 | * @return \moodle_url 71 | */ 72 | public function get_url() { 73 | return new \moodle_url('/admin/tool/devcourse/index.php', ['id' => $this->courseid]); 74 | } 75 | 76 | /** 77 | * Custom validation. 78 | * 79 | * @throws \coding_exception 80 | * @return void 81 | */ 82 | protected function validate_data() { 83 | parent::validate_data(); 84 | 85 | if ($this->contextlevel != CONTEXT_COURSE) { 86 | throw new \coding_exception('Context level must be CONTEXT_COURSE.'); 87 | } 88 | } 89 | 90 | /** 91 | * This is used when restoring course logs where it is required that we 92 | * map the objectid to it's new value in the new course. 93 | * 94 | * @return string the name of the restore mapping the objectid links to 95 | */ 96 | public static function get_objectid_mapping() { 97 | return base::NOT_MAPPED; 98 | } 99 | 100 | /** 101 | * This is used when restoring course logs where it is required that we 102 | * map the information in 'other' to it's new value in the new course. 103 | * 104 | * @return array an array of other values and their corresponding mapping 105 | */ 106 | public static function get_other_mapping() { 107 | // Nothing to map. 108 | return false; 109 | } 110 | } -------------------------------------------------------------------------------- /amd/src/confirm.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - http://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * Add confirmation 18 | * 19 | * @module tool_devcourse/confirm 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'], 25 | function($, str, notification, ajax, templates) { 26 | 27 | /** 28 | * Displays the delete confirmation and on approval redirects to href 29 | * @param {Number} id 30 | * @param {jQuery} list 31 | */ 32 | var confirmDelete = function(id, list) { 33 | str.get_strings([ 34 | {key: 'delete'}, 35 | {key: 'confirmdeleteentry', component: 'tool_devcourse'}, 36 | {key: 'yes'}, 37 | {key: 'no'} 38 | ]).done(function(s) { 39 | notification.confirm(s[0], s[1], s[2], s[3], function() { 40 | processDelete(id, list); 41 | }); 42 | } 43 | ).fail(notification.exception); 44 | }; 45 | 46 | /** 47 | * Processes deleting an entry 48 | * @param {Number} id 49 | * @param {jQuery} list 50 | */ 51 | var processDelete = function(id, list) { 52 | var courseid = list.attr('data-courseid'); 53 | // We are chaining ajax requests here. 54 | var requests = ajax.call([{ 55 | methodname: 'tool_devcourse_delete_entry', 56 | args: {id: id} 57 | }, { 58 | methodname: 'tool_devcourse_entries_list', 59 | args: {courseid: courseid} 60 | }]); 61 | requests[1].done(function(data) { 62 | reloadList(data, list); 63 | }).fail(notification.exception); 64 | }; 65 | 66 | /** 67 | * Replaces the current list with the data rendered from template 68 | * @param {Object} data 69 | * @param {jQuery} list 70 | */ 71 | var reloadList = function(data, list) { 72 | templates.render('tool_devcourse/entries_list', data).done(function(html) { 73 | list.replaceWith(html); 74 | }); 75 | }; 76 | 77 | /** 78 | * Registers the handler for click event 79 | * @param {String} selector 80 | */ 81 | var registerClickHandler = function(selector) { 82 | $(selector).on('click', function(e) { 83 | e.preventDefault(); 84 | var id = $(e.currentTarget).attr('data-entryid'), 85 | list = $(e.currentTarget).closest('.tool_devcourse_entries_list'); 86 | confirmDelete(id, list); 87 | }); 88 | }; 89 | 90 | return /** @alias module:tool_devcourse/confirm */ { 91 | /** 92 | * Initialise the confirmation for selector 93 | * 94 | * @method init 95 | * @param {String} selector 96 | */ 97 | init: function(selector) { 98 | registerClickHandler(selector); 99 | } 100 | }; 101 | }); -------------------------------------------------------------------------------- /tests/events_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This file contains the class that handles testing of course events. 19 | * 20 | * @package core 21 | * @copyright 2016 Stephen Bourget 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | /** 28 | * This file contains the class that handles testing of course events. 29 | * 30 | * @package core_course 31 | * @copyright 2016 Stephen Bourget 32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 | */ 34 | class tool_devcourse_events_testcase extends advanced_testcase { 35 | 36 | /** 37 | * Tests set up 38 | */ 39 | protected function setUp() { 40 | $this->resetAfterTest(); 41 | } 42 | 43 | /** 44 | * Test for event entry_created 45 | */ 46 | public function test_entry_created() { 47 | $course = $this->getDataGenerator()->create_course(); 48 | $sink = $this->redirectEvents(); 49 | $entryid = tool_devcourse_api::insert((object)[ 50 | 'courseid' => $course->id, 51 | 'name' => 'testname1', 52 | 'completed' => 1, 53 | 'priority' => 0, 54 | ]); 55 | $events = $sink->get_events(); 56 | $this->assertCount(1, $events); 57 | $event = array_shift($events); 58 | 59 | // Checking that the event contains the expected values. 60 | $this->assertInstanceOf('\tool_devcourse\event\entry_created', $event); 61 | $this->assertEquals($course->id, $event->courseid); 62 | $this->assertEquals($entryid, $event->objectid); 63 | } 64 | 65 | /** 66 | * Test for event entry_updated 67 | */ 68 | public function test_entry_updated() { 69 | $course = $this->getDataGenerator()->create_course(); 70 | $entryid = tool_devcourse_api::insert((object)[ 71 | 'courseid' => $course->id, 72 | 'name' => 'testname1' 73 | ]); 74 | 75 | $sink = $this->redirectEvents(); 76 | tool_devcourse_api::update((object)[ 77 | 'id' => $entryid, 78 | 'name' => 'testname2', 79 | ]); 80 | $events = $sink->get_events(); 81 | $this->assertCount(1, $events); 82 | $event = array_shift($events); 83 | 84 | // Checking that the event contains the expected values. 85 | $this->assertInstanceOf('\tool_devcourse\event\entry_updated', $event); 86 | $this->assertEquals($course->id, $event->courseid); 87 | $this->assertEquals($entryid, $event->objectid); 88 | } 89 | 90 | 91 | /** 92 | * Test for event entry_deleted 93 | */ 94 | public function test_entry_deleted() { 95 | $course = $this->getDataGenerator()->create_course(); 96 | $entryid = tool_devcourse_api::insert((object)[ 97 | 'courseid' => $course->id, 98 | 'name' => 'testname1' 99 | ]); 100 | 101 | $sink = $this->redirectEvents(); 102 | tool_devcourse_api::delete($entryid); 103 | $events = $sink->get_events(); 104 | $this->assertCount(1, $events); 105 | $event = array_shift($events); 106 | 107 | // Checking that the event contains the expected values. 108 | $this->assertInstanceOf('\tool_devcourse\event\entry_deleted', $event); 109 | $this->assertEquals($course->id, $event->courseid); 110 | $this->assertEquals($entryid, $event->objectid); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /pix/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 6 | 7 | -------------------------------------------------------------------------------- /classes/external.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse_external 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->libdir . "/externallib.php"); 28 | 29 | /** 30 | * Web services for tool_devcourse 31 | * 32 | * @package tool_devcourse 33 | * @copyright 2018 Marina Glancy 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class tool_devcourse_external extends external_api { 37 | 38 | /** 39 | * Returns description of method parameters 40 | * @return external_function_parameters 41 | */ 42 | public static function entries_list_parameters() { 43 | return new external_function_parameters( 44 | array('courseid' => new external_value(PARAM_INT, 'Course id', VALUE_REQUIRED)) 45 | ); 46 | } 47 | 48 | /** 49 | * List of entries in the course 50 | * @return array 51 | */ 52 | public static function entries_list($courseid) { 53 | global $PAGE; 54 | 55 | // Parameter validation. 56 | $params = self::validate_parameters(self::entries_list_parameters(), 57 | array('courseid' => $courseid)); 58 | $courseid = $params['courseid']; 59 | 60 | // From web services we don't call require_login(), but rather validate_context. 61 | $context = context_course::instance($courseid); 62 | self::validate_context($context); 63 | require_capability('tool/devcourse:view', $context); 64 | 65 | $outputpage = new \tool_devcourse\output\entries_list($courseid); 66 | $renderer = $PAGE->get_renderer('tool_devcourse'); 67 | return $outputpage->export_for_template($renderer); 68 | } 69 | 70 | /** 71 | * Returns description of method result value 72 | * @return external_description 73 | */ 74 | public static function entries_list_returns() { 75 | return new external_single_structure( 76 | array( 77 | 'courseid' => new external_value(PARAM_INT, 'Course id'), 78 | 'coursename' => new external_value(PARAM_NOTAGS, 'Course name'), 79 | 'contents' => new external_value(PARAM_RAW, 'Contents'), 80 | 'addlink' => new external_value(PARAM_URL, 'Link to add an entry', VALUE_OPTIONAL), 81 | ) 82 | ); 83 | } 84 | 85 | 86 | /** 87 | * Returns description of method parameters 88 | * @return external_function_parameters 89 | */ 90 | public static function delete_entry_parameters() { 91 | return new external_function_parameters( 92 | array('id' => new external_value(PARAM_INT, 'Id of an entry', VALUE_REQUIRED)) 93 | ); 94 | } 95 | 96 | /** 97 | * List of entries in the course 98 | * @param int $id 99 | * @return array 100 | */ 101 | public static function delete_entry($id) { 102 | // Parameter validation. 103 | $params = self::validate_parameters(self::delete_entry_parameters(), 104 | array('id' => $id)); 105 | $id = $params['id']; 106 | 107 | $entry = tool_devcourse_api::retrieve($id); 108 | 109 | // From web services we don't call require_login(), but rather validate_context. 110 | $context = context_course::instance($entry->courseid); 111 | self::validate_context($context); 112 | require_capability('tool/devcourse:edit', $context); 113 | 114 | tool_devcourse_api::delete($id); 115 | } 116 | 117 | /** 118 | * Returns description of method result value 119 | * @return external_description 120 | */ 121 | public static function delete_entry_returns() { 122 | return null; 123 | } 124 | } -------------------------------------------------------------------------------- /db/upgrade.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * tool_devcourse upgrade script. 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | /** 28 | * Run all upgrade steps between the current DB version and the current version on disk. 29 | * 30 | * @param int $oldversion The old version of atto equation in the DB. 31 | * @return bool 32 | */ 33 | function xmldb_tool_devcourse_upgrade($oldversion) { 34 | global $CFG, $DB; 35 | 36 | $dbman = $DB->get_manager(); 37 | 38 | if ($oldversion < 2018072005) { 39 | 40 | // Define table tool_devcourse to be created. 41 | $table = new xmldb_table('tool_devcourse'); 42 | 43 | // Adding fields to table tool_devcourse. 44 | $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 45 | $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); 46 | $table->add_field('name', XMLDB_TYPE_CHAR, '50', null, null, null, null); 47 | $table->add_field('completed', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); 48 | $table->add_field('priority', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); 49 | $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 50 | $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 51 | 52 | // Adding keys to table tool_devcourse. 53 | $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); 54 | 55 | // Conditionally launch create table for tool_devcourse. 56 | if (!$dbman->table_exists($table)) { 57 | $dbman->create_table($table); 58 | } 59 | 60 | // Devcourse savepoint reached. 61 | upgrade_plugin_savepoint(true, 2018072005, 'tool', 'devcourse'); 62 | } 63 | 64 | if ($oldversion < 2018072006) { 65 | 66 | // Define key courseid (foreign) to be added to tool_devcourse. 67 | $table = new xmldb_table('tool_devcourse'); 68 | $key = new xmldb_key('courseid', XMLDB_KEY_FOREIGN, ['courseid'], 'course', ['id']); 69 | 70 | // Launch add key courseid. 71 | $dbman->add_key($table, $key); 72 | 73 | // Devcourse savepoint reached. 74 | upgrade_plugin_savepoint(true, 2018072006, 'tool', 'devcourse'); 75 | } 76 | 77 | if ($oldversion < 2018072007) { 78 | 79 | // Define index courseidname (unique) to be added to tool_devcourse. 80 | $table = new xmldb_table('tool_devcourse'); 81 | $index = new xmldb_index('courseidname', XMLDB_INDEX_UNIQUE, ['courseid', 'name']); 82 | 83 | // Conditionally launch add index courseidname. 84 | if (!$dbman->index_exists($table, $index)) { 85 | $dbman->add_index($table, $index); 86 | } 87 | 88 | // Devcourse savepoint reached. 89 | upgrade_plugin_savepoint(true, 2018072007, 'tool', 'devcourse'); 90 | } 91 | 92 | if ($oldversion < 2018072307) { 93 | 94 | // Define field description to be added to tool_devcourse. 95 | $table = new xmldb_table('tool_devcourse'); 96 | $field = new xmldb_field('description', XMLDB_TYPE_TEXT, null, null, null, null, null, 'priority'); 97 | 98 | // Conditionally launch add field description. 99 | if (!$dbman->field_exists($table, $field)) { 100 | $dbman->add_field($table, $field); 101 | } 102 | 103 | // Define field descriptionformat to be added to tool_devcourse. 104 | $field = new xmldb_field('descriptionformat', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'description'); 105 | 106 | // Conditionally launch add field descriptionformat. 107 | if (!$dbman->field_exists($table, $field)) { 108 | $dbman->add_field($table, $field); 109 | } 110 | 111 | // Devcourse savepoint reached. 112 | upgrade_plugin_savepoint(true, 2018072307, 'tool', 'devcourse'); 113 | } 114 | 115 | return true; 116 | } 117 | -------------------------------------------------------------------------------- /tests/api_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * API tests. 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | use core\invalid_persistent_exception; 26 | use core\task\manager; 27 | use tool_dataprivacy\context_instance; 28 | use tool_dataprivacy\api; 29 | use tool_dataprivacy\data_registry; 30 | use tool_dataprivacy\expired_context; 31 | use tool_dataprivacy\data_request; 32 | use tool_dataprivacy\local\helper; 33 | use tool_dataprivacy\task\initiate_data_request_task; 34 | use tool_dataprivacy\task\process_data_request_task; 35 | 36 | defined('MOODLE_INTERNAL') || die(); 37 | global $CFG; 38 | 39 | /** 40 | * API tests. 41 | * 42 | * @package tool_devcourse 43 | * @copyright 2018 Marina Glancy 44 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 45 | */ 46 | class tool_devcourse_api_testcase extends advanced_testcase { 47 | 48 | /** 49 | * Set up for the tests. 50 | */ 51 | public function setUp() { 52 | $this->resetAfterTest(); 53 | } 54 | 55 | /** 56 | * Test for tool_devcourse_api::insert and tool_devcourse_api::retrieve 57 | */ 58 | public function test_insert() { 59 | $course = $this->getDataGenerator()->create_course(); 60 | $entryid = tool_devcourse_api::insert((object)[ 61 | 'courseid' => $course->id, 62 | 'name' => 'testname1', 63 | 'completed' => 1, 64 | 'priority' => 0, 65 | 'description' => 'description plain' 66 | ]); 67 | $entry = tool_devcourse_api::retrieve($entryid); 68 | $this->assertEquals($course->id, $entry->courseid); 69 | $this->assertEquals('testname1', $entry->name); 70 | $this->assertEquals('description plain', $entry->description); 71 | } 72 | 73 | /** 74 | * Test for tool_devcourse_api::update 75 | */ 76 | public function test_update() { 77 | $course = $this->getDataGenerator()->create_course(); 78 | $entryid = tool_devcourse_api::insert((object)[ 79 | 'courseid' => $course->id, 80 | 'name' => 'testname1' 81 | ]); 82 | tool_devcourse_api::update((object)[ 83 | 'id' => $entryid, 84 | 'name' => 'testname2', 85 | 'description' => 'description plain' 86 | ]); 87 | $entry = tool_devcourse_api::retrieve($entryid); 88 | $this->assertEquals($course->id, $entry->courseid); 89 | $this->assertEquals('testname2', $entry->name); 90 | $this->assertEquals('description plain', $entry->description); 91 | } 92 | 93 | /** 94 | * Test for tool_devcourse_api::delete 95 | */ 96 | public function test_delete() { 97 | $course = $this->getDataGenerator()->create_course(); 98 | $entryid = tool_devcourse_api::insert((object)[ 99 | 'courseid' => $course->id, 100 | 'name' => 'testname1' 101 | ]); 102 | tool_devcourse_api::delete($entryid); 103 | $entry = tool_devcourse_api::retrieve($entryid, 0, IGNORE_MISSING); 104 | $this->assertEmpty($entry); 105 | } 106 | 107 | public function test_description_editor() { 108 | $this->setAdminUser(); 109 | $course = $this->getDataGenerator()->create_course(); 110 | $entryid = tool_devcourse_api::insert((object)[ 111 | 'courseid' => $course->id, 112 | 'name' => 'testname1', 113 | 'description_editor' => [ 114 | 'text' => 'description formatted', 115 | 'format' => FORMAT_HTML, 116 | 'itemid' => file_get_unused_draft_itemid() 117 | ] 118 | ]); 119 | $entry = tool_devcourse_api::retrieve($entryid); 120 | $this->assertEquals('description formatted', $entry->description); 121 | tool_devcourse_api::update((object)[ 122 | 'id' => $entryid, 123 | 'name' => 'testname2', 124 | 'description_editor' => [ 125 | 'text' => 'description edited', 126 | 'format' => FORMAT_HTML, 127 | 'itemid' => file_get_unused_draft_itemid() 128 | ] 129 | ]); 130 | $entry = tool_devcourse_api::retrieve($entryid); 131 | $this->assertEquals($course->id, $entry->courseid); 132 | $this->assertEquals('testname2', $entry->name); 133 | $this->assertEquals('description edited', $entry->description); 134 | 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /classes/table.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse_table 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->libdir.'/tablelib.php'); 28 | 29 | /** 30 | * Class tool_devcourse_table for displaying tool_devcourse table 31 | * 32 | * @package tool_devcourse 33 | * @copyright 2018 Marina Glancy 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class tool_devcourse_table extends table_sql { 37 | 38 | /** @var context_course */ 39 | protected $context; 40 | 41 | /** 42 | * Sets up the table_log parameters. 43 | * 44 | * @param string $uniqueid unique id of form. 45 | * @param int $courseid 46 | */ 47 | public function __construct($uniqueid, $courseid) { 48 | global $PAGE; 49 | 50 | parent::__construct($uniqueid); 51 | 52 | $this->set_attribute('id', 'tool_devcourse_overview'); 53 | 54 | $columns = array('name', 'description', 'completed', 'priority', 'timecreated', 'timemodified'); 55 | $headers = array( 56 | get_string('name', 'tool_devcourse'), 57 | get_string('description', 'tool_devcourse'), 58 | get_string('completed', 'tool_devcourse'), 59 | get_string('priority', 'tool_devcourse'), 60 | get_string('timecreated', 'tool_devcourse'), 61 | get_string('timemodified', 'tool_devcourse'), 62 | ); 63 | $this->context = context_course::instance($courseid); 64 | if (has_capability('tool/devcourse:edit', $this->context)) { 65 | $columns[] = 'edit'; 66 | $headers[] = ''; 67 | } 68 | $this->define_columns($columns); 69 | $this->define_headers($headers); 70 | $this->pageable(true); 71 | $this->collapsible(false); 72 | $this->sortable(false); 73 | $this->is_downloadable(false); 74 | 75 | $this->define_baseurl($PAGE->url); 76 | 77 | $this->set_sql('id, name, completed, priority, timecreated, timemodified, '. 78 | 'description, descriptionformat', 79 | '{tool_devcourse}', 'courseid = ?', [$courseid]); 80 | } 81 | 82 | /** 83 | * Displays column completed 84 | * 85 | * @param stdClass $row 86 | * @return string 87 | */ 88 | protected function col_completed($row) { 89 | return $row->completed ? get_string('yes') : get_string('no'); 90 | } 91 | 92 | /** 93 | * Displays column priority 94 | * 95 | * @param stdClass $row 96 | * @return string 97 | */ 98 | protected function col_priority($row) { 99 | return $row->priority ? get_string('yes') : get_string('no'); 100 | } 101 | 102 | /** 103 | * Displays column name 104 | * 105 | * @param stdClass $row 106 | * @return string 107 | */ 108 | protected function col_name($row) { 109 | return format_string($row->name, true, 110 | ['context' => $this->context]); 111 | } 112 | 113 | /** 114 | * Displays column description 115 | * 116 | * @param stdClass $row 117 | * @return string 118 | */ 119 | protected function col_description($row) { 120 | $options = tool_devcourse_api::editor_options(); 121 | $description = file_rewrite_pluginfile_urls($row->description, 'pluginfile.php', 122 | $options['context']->id, 'tool_devcourse', 'entry', $row->id, $options); 123 | return format_text($description, $row->descriptionformat, $options); 124 | } 125 | 126 | /** 127 | * Displays column timecreated 128 | * 129 | * @param stdClass $row 130 | * @return string 131 | */ 132 | protected function col_timecreated($row) { 133 | return userdate($row->timecreated, get_string('strftimedatetime')); 134 | } 135 | 136 | /** 137 | * Displays column timemodified 138 | * 139 | * @param stdClass $row 140 | * @return string 141 | */ 142 | protected function col_timemodified($row) { 143 | return userdate($row->timemodified, get_string('strftimedatetime')); 144 | } 145 | 146 | protected function col_edit($row) { 147 | $url = new moodle_url('/admin/tool/devcourse/edit.php', ['id' => $row->id]); 148 | $deleteurl = new moodle_url('/admin/tool/devcourse/index.php', 149 | ['delete' => $row->id, 'id' => $this->context->instanceid, 150 | 'sesskey' => sesskey()]); 151 | return html_writer::link($url, get_string('edit')) . '
' . 152 | html_writer::link($deleteurl, get_string('delete'), 153 | ['data-action' => 'deleteentry', 'data-entryid' => $row->id]); 154 | } 155 | } -------------------------------------------------------------------------------- /classes/api.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tool_devcourse_api 19 | * 20 | * @package tool_devcourse 21 | * @copyright 2018 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->libdir.'/formslib.php'); 28 | 29 | /** 30 | * Class tool_devcourse_api for various api methods 31 | * 32 | * @package tool_devcourse 33 | * @copyright 2018 Marina Glancy 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class tool_devcourse_api { 37 | 38 | /** 39 | * Retrieve an entry 40 | * 41 | * @param int $id id of the entry 42 | * @param int $courseid optional course id for validation 43 | * @param int $strictness 44 | * @return stdClass|bool retrieved object or false if entry not found and strictness is IGNORE_MISSING 45 | */ 46 | public static function retrieve(int $id, int $courseid = 0, int $strictness = MUST_EXIST) { 47 | global $DB; 48 | $params = ['id' => $id]; 49 | if ($courseid) { 50 | $params['courseid'] = $courseid; 51 | } 52 | return $DB->get_record('tool_devcourse', $params, '*', $strictness); 53 | } 54 | 55 | /** 56 | * Update an entry 57 | * 58 | * @param stdClass $data 59 | */ 60 | public static function update(stdClass $data) { 61 | global $DB, $PAGE; 62 | if (empty($data->id)) { 63 | throw new coding_exception('Object data must contain property id'); 64 | } 65 | if (isset($data->description_editor)) { 66 | $data = file_postupdate_standard_editor($data, 'description', 67 | self::editor_options(), $PAGE->context, 'tool_devcourse', 'entry', $data->id); 68 | } 69 | // Only fields name, completed, priority, description, descriptionformat can be modified. 70 | $updatedata = array_intersect_key((array)$data, 71 | ['id' => 1, 'name' => 1, 'completed' => 1, 'priority' => 1, 72 | 'description' => 1, 'descriptionformat' => 1]); 73 | $updatedata['timemodified'] = time(); 74 | $DB->update_record('tool_devcourse', $updatedata); 75 | 76 | // Trigger event. 77 | $entry = self::retrieve($data->id); 78 | $event = \tool_devcourse\event\entry_updated::create([ 79 | 'context' => context_course::instance($entry->courseid), 80 | 'objectid' => $entry->id 81 | ]); 82 | $event->add_record_snapshot('tool_devcourse', $entry); 83 | $event->trigger(); 84 | } 85 | 86 | /** 87 | * Insert an entry 88 | * 89 | * @param stdClass $data 90 | * @return int id of the new entry 91 | */ 92 | public static function insert(stdClass $data) : int { 93 | global $DB; 94 | if (empty($data->courseid)) { 95 | throw new coding_exception('Object data must contain property courseid'); 96 | } 97 | $insertdata = array_intersect_key((array)$data, 98 | ['courseid' => 1, 'name' => 1, 'completed' => 1, 'priority' => 1, 99 | 'description' => 1, 'descriptionformat' => 1]); 100 | $insertdata['timemodified'] = $insertdata['timecreated'] = time(); 101 | $entryid = $DB->insert_record('tool_devcourse', $insertdata); 102 | 103 | // Now when we know id update the description and save the files. 104 | if (isset($data->description_editor)) { 105 | $context = context_course::instance($data->courseid); 106 | $data = file_postupdate_standard_editor($data, 'description', 107 | self::editor_options(), $context, 'tool_devcourse', 'entry', $entryid); 108 | $updatedata = ['id' => $entryid, 'description' => $data->description, 109 | 'descriptionformat' => $data->descriptionformat]; 110 | $DB->update_record('tool_devcourse', $updatedata); 111 | } 112 | 113 | // Trigger event. 114 | $event = \tool_devcourse\event\entry_created::create([ 115 | 'context' => context_course::instance($data->courseid), 116 | 'objectid' => $entryid 117 | ]); 118 | $event->trigger(); 119 | 120 | return $entryid; 121 | } 122 | 123 | /** 124 | * Delete an entry 125 | * 126 | * @param int $id 127 | */ 128 | public static function delete(int $id) { 129 | global $DB; 130 | if (!$entry = self::retrieve($id, 0, IGNORE_MISSING)) { 131 | return; 132 | } 133 | $DB->delete_records('tool_devcourse', ['id' => $id]); 134 | 135 | // Trigger event. 136 | $event = \tool_devcourse\event\entry_deleted::create([ 137 | 'context' => context_course::instance($entry->courseid), 138 | 'objectid' => $entry->id 139 | ]); 140 | $event->add_record_snapshot('tool_devcourse', $entry); 141 | $event->trigger(); 142 | } 143 | 144 | /** 145 | * Options for the description editor 146 | * @return array 147 | */ 148 | public static function editor_options() { 149 | global $PAGE; 150 | return [ 151 | 'maxfiles' => -1, 152 | 'maxbytes' => 0, 153 | 'context' => $PAGE->context, 154 | 'noclean' => true, 155 | ]; 156 | } 157 | 158 | /** 159 | * Observer for course_deleted event - deletes all associated entries 160 | * 161 | * @param \core\event\course_deleted $event 162 | */ 163 | public static function course_deleted_observer(\core\event\course_deleted $event) { 164 | global $DB; 165 | $DB->delete_records('tool_devcourse', ['courseid' => $event->objectid]); 166 | } 167 | } --------------------------------------------------------------------------------