490 lines
19 KiB
PHP
490 lines
19 KiB
PHP
<?php
|
|
// This file is part of the Studyplan plugin for Moodle
|
|
//
|
|
// 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 <https://www.gnu.org/licenses/>.
|
|
/**
|
|
* Moodle hook functions and some internally used functions
|
|
* @package local_treestudyplan
|
|
* @copyright 2023 P.M. Kuipers
|
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
require_once($CFG->dirroot.'/course/modlib.php');
|
|
|
|
use local_treestudyplan\local\helpers\webservicehelper;
|
|
use \local_treestudyplan\studyplan;
|
|
|
|
/**
|
|
* Describe editor options
|
|
* @param context $context Context for options
|
|
* @return array Editor options
|
|
*/
|
|
function local_treestudyplan_unit_get_editor_options(context $context) {
|
|
global $CFG;
|
|
return ['subdirs' => 1,
|
|
'maxbytes' => $CFG->maxbytes,
|
|
'maxfiles' => -1,
|
|
'changeformat' => 1,
|
|
'context' => $context,
|
|
'noclean' => 1,
|
|
'trusttext' => 0];
|
|
}
|
|
|
|
|
|
/**
|
|
* Create primary navigation links for studyplan if needed
|
|
*/
|
|
function local_treestudyplan_autofill_customusermenuitems() {
|
|
if (get_config("local_treestudyplan", "primary_nav_autofill")) {
|
|
$lang = current_language();
|
|
$navitems = [
|
|
"/local/treestudyplan/myreport.php" => ["included" => false, "strkey" => "link_myreport"],
|
|
"/local/treestudyplan/view-plan.php" => ["included" => false, "strkey" => "link_viewplan"],
|
|
"/local/treestudyplan/edit-plan.php" => ["included" => false, "strkey" => "link_editplan"],
|
|
];
|
|
|
|
// Load the custom menu items from config.
|
|
$custommenuitems = get_config("core", "custommenuitems");
|
|
|
|
// Scan through all the lines to see if it is a link to one of our nav items in the current language.
|
|
$lines = explode("\n", $custommenuitems);
|
|
|
|
$links = array_keys($navitems);
|
|
foreach ($lines as $line) {
|
|
$parms = explode('|', $line);
|
|
if (count($parms) > 3) {
|
|
$link = trim($parms[1]);
|
|
if (trim($parms[3]) == $lang && in_array($link, $links)) {
|
|
// Register the link as already included if it is found.
|
|
$navitems[$link]["included"] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// List through all the links to see if we need to add one or more.
|
|
foreach ($navitems as $link => $details) {
|
|
if (!$details["included"]) {
|
|
$line = implode("|", [
|
|
get_string($details["strkey"], "local_treestudyplan"), // Menu text.
|
|
$link, // Link.
|
|
'', // Tooltip.
|
|
$lang, // Language code.
|
|
" #Automatically added by studyplan plugin. See setting 'primary_nav_autofill' to disable this"
|
|
]);
|
|
$custommenuitems = trim($custommenuitems)."\n".$line;
|
|
}
|
|
}
|
|
|
|
// Store the modified custom menu items.
|
|
set_config("custommenuitems", $custommenuitems);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Hook to extend navigation
|
|
* @param global_navigation $navigation Navigation object
|
|
*/
|
|
function local_treestudyplan_extend_navigation(global_navigation $navigation) {
|
|
global $CFG, $PAGE, $COURSE, $USER;
|
|
|
|
$systemcontext = context_system::instance();
|
|
|
|
/* Moodle 4.0-4.2 do not yet support customizing the primary navigation bar (it is a planned feature though).
|
|
For now, go to theme settings and add the following into "Custom menu items".
|
|
[your name for my studyplan]|/local/treestudyplan/myreport.php.
|
|
[your name for studyplan viewing]|/local/treestudyplan/view-plan.php.
|
|
[your name for studyplan managing]|/local/treestudyplan/edit-plan.php.
|
|
For example:.
|
|
Mijn studieplan|/local/treestudyplan/myreport.php.
|
|
Studieplannen|/local/treestudyplan/view-plan.php.
|
|
Studieplannen beheren|/local/treestudyplan/edit-plan.php.
|
|
|
|
Using some javascript magic we'll hide the links that are not accessible.
|
|
(Since the Output API does not easily support inline style tags, adding one through Javascript is easier,.
|
|
and not much more complex than loading a separate stylesheet for each link we want to hide).
|
|
We will add all the hrefs that should be hidden to this variable below.
|
|
*/
|
|
|
|
/*
|
|
In addition, the function local_treestudyplan_autofill_customusermenuitems() called below will
|
|
automatically generate the required lines if they are missing...
|
|
*/
|
|
local_treestudyplan_autofill_customusermenuitems();
|
|
|
|
$hideprimaryhrefs = [];
|
|
|
|
if ($USER->id > 1) {
|
|
// Don't show if user is not logged in (id == 0) or is guest user (id == 1).
|
|
|
|
$userstudyplans = studyplan::find_for_user($USER->id);
|
|
if (!empty($userstudyplans)) {
|
|
|
|
// Create studyplan node.
|
|
$node = navigation_node::create(
|
|
get_string("link_myreport", "local_treestudyplan"),
|
|
new moodle_url($CFG->wwwroot . "/local/treestudyplan/myreport.php", array()),
|
|
global_navigation::TYPE_SYSTEM,
|
|
null,
|
|
"local_treestudyplan_myreport",
|
|
new pix_icon("myreport", '', 'local_treestudyplan')
|
|
);
|
|
$node->showinflatnavigation = true;
|
|
$node->showinsecondarynavigation = true;
|
|
|
|
// Create invitenode node.
|
|
$invitenode = navigation_node::create(
|
|
get_string("manage_invites", "local_treestudyplan"),
|
|
new moodle_url($CFG->wwwroot . "/local/treestudyplan/invitations.php", array()),
|
|
global_navigation::TYPE_CUSTOM ,
|
|
null,
|
|
"local_treestudyplan_invitemgmt",
|
|
new pix_icon("invitemgmt", '', 'local_treestudyplan')
|
|
);
|
|
$invitenode->showinflatnavigation = false;
|
|
$node->add_node($invitenode);
|
|
|
|
$navigation->add_node($node, 'mycourses');
|
|
} else {
|
|
$hideprimaryhrefs[] = "/local/treestudyplan/myreport.php";
|
|
}
|
|
if ( has_capability('local/treestudyplan:viewuserreports', context_system::instance())
|
|
|| webservicehelper::has_capability_in_any_category('local/treestudyplan:viewuserreports')) {
|
|
$node = navigation_node::create(
|
|
get_string("link_viewplan", "local_treestudyplan"),
|
|
new moodle_url($CFG->wwwroot . "/local/treestudyplan/view-plan.php", array()),
|
|
global_navigation::TYPE_SYSTEM ,
|
|
null,
|
|
"local_treestudyplan_viewplan",
|
|
new pix_icon("viewplans", '', 'local_treestudyplan')
|
|
);
|
|
$node->showinflatnavigation = true;
|
|
$node->showinsecondarynavigation = true;
|
|
$navigation->add_node($node, 'mycourses');
|
|
} else {
|
|
$hideprimaryhrefs[] = "/local/treestudyplan/view-plan.php";
|
|
}
|
|
if ( has_capability('local/treestudyplan:editstudyplan', context_system::instance())
|
|
|| webservicehelper::has_capability_in_any_category('local/treestudyplan:editstudyplan')
|
|
) {
|
|
$node = navigation_node::create(
|
|
get_string("link_editplan", "local_treestudyplan"),
|
|
new moodle_url($CFG->wwwroot . "/local/treestudyplan/edit-plan.php", array()),
|
|
global_navigation::TYPE_SYSTEM ,
|
|
null,
|
|
"local_treestudyplan_editplan",
|
|
new pix_icon("viewplans", '', 'local_treestudyplan')
|
|
);
|
|
$node->showinflatnavigation = true;
|
|
$node->showinsecondarynavigation = true;
|
|
$navigation->add_node($node, 'mycourses');
|
|
} else {
|
|
$hideprimaryhrefs[] = "/local/treestudyplan/edit-plan.php";
|
|
}
|
|
} else {
|
|
$hideprimaryhrefs[] = "/local/treestudyplan/myreport.php";
|
|
$hideprimaryhrefs[] = "/local/treestudyplan/edit-plan.php";
|
|
$hideprimaryhrefs[] = "/local/treestudyplan/view-plan.php";
|
|
}
|
|
// Create invitenode node.
|
|
$invitenode = navigation_node::create(
|
|
get_string("nav_invited", "local_treestudyplan"),
|
|
new moodle_url($CFG->wwwroot . "/local/treestudyplan/invited.php", array()),
|
|
global_navigation::TYPE_USER ,
|
|
null,
|
|
"local_treestudyplan_invitemgmt",
|
|
new pix_icon("nav_invited", '', 'local_treestudyplan')
|
|
);
|
|
$invitenode->showinflatnavigation = false;
|
|
$navigation->add_node($invitenode, 'mycourses');
|
|
|
|
// Now using some javascript magic, we'll hide the links that are not accessible.
|
|
$PAGE->requires->js_call_amd('local_treestudyplan/primary-nav-tools', 'hide_primary', [$hideprimaryhrefs]);
|
|
|
|
}
|
|
|
|
/**
|
|
* Hook to extend navigation in category view
|
|
* @param mixed $navigation
|
|
* @param context_coursecat $coursecategorycontext
|
|
*/
|
|
function local_treestudyplan_extend_navigation_category_settings($navigation, context_coursecat $coursecategorycontext) {
|
|
global $CFG, $PAGE;
|
|
$categoryid = $coursecategorycontext->instanceid;
|
|
if (has_capability('local/treestudyplan:editstudyplan', $coursecategorycontext)) {
|
|
$node = $navigation->add(
|
|
get_string('treestudyplan:editstudyplan', "local_treestudyplan"),
|
|
new moodle_url($CFG->wwwroot . "/local/treestudyplan/edit-plan.php", ["categoryid" => $categoryid]),
|
|
global_navigation::TYPE_CATEGORY,
|
|
null,
|
|
"local_treestudyplan_editplan",
|
|
new pix_icon("editplans", '', 'local_treestudyplan')
|
|
);
|
|
|
|
}
|
|
if (has_capability('local/treestudyplan:viewuserreports', $coursecategorycontext)) {
|
|
$node = $navigation->add(
|
|
get_string('link_viewplan', "local_treestudyplan"),
|
|
new moodle_url($CFG->wwwroot . "/local/treestudyplan/view-plan.php", ["categoryid" => $categoryid]),
|
|
global_navigation::TYPE_CATEGORY,
|
|
null,
|
|
"local_treestudyplan_viewplan",
|
|
new pix_icon("viewplans", '', 'local_treestudyplan')
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Map fontawesome icons for use in flat navigation
|
|
* @return array Icon mapping
|
|
*
|
|
*/
|
|
function local_treestudyplan_get_fontawesome_icon_map() {
|
|
|
|
// Create the icon map with the icons which are used in any case.
|
|
$iconmapping = [
|
|
'local_treestudyplan:myreport' => 'fa-vcard',
|
|
'local_treestudyplan:editplans' => 'fa-share-alt',
|
|
'local_treestudyplan:viewplans' => 'fa-share-alt',
|
|
];
|
|
return $iconmapping;
|
|
}
|
|
|
|
/**
|
|
* Helper function to reset the icon system used as updatecallback function when saving some of the plugin's settings.
|
|
*/
|
|
function local_treestudyplan_reset_fontawesome_icon_map() {
|
|
// Reset the icon system cache.
|
|
// There is the function \core\output\icon_system::reset_caches() which does seem to be only usable in unit tests.
|
|
// Thus, we clear the icon system cache brutally.
|
|
$cache = \cache::make('core', 'fontawesomeiconmapping');
|
|
$cache->delete('mapping');
|
|
// And rebuild it brutally.
|
|
$instance = \core\output\icon_system::instance(\core\output\icon_system::FONTAWESOME);
|
|
$instance->get_icon_name_map();
|
|
}
|
|
|
|
/**
|
|
* Send invitation to invited person
|
|
* @param mixed $inviteid Database id of the invitation
|
|
*
|
|
*/
|
|
function local_treestudyplan_send_invite($inviteid) {
|
|
global $DB, $USER, $CFG;
|
|
$invite = $DB->get_record("local_treestudyplan_invit", array('id' => $inviteid));
|
|
|
|
$noreply = 'noreply@' . get_host_from_url($CFG->wwwroot);
|
|
$mailer = get_mailer();
|
|
$mailer->setFrom($noreply, "{$USER->firstname} {$USER->lastname}");
|
|
$mailer->addAddress($invite->email, $invite->name);
|
|
$mailer->addReplyTo($USER->email, "{$USER->firstname} {$USER->lastname}");
|
|
|
|
$invitehref = $CFG->wwwroot."/local/treestudyplan/invited.php?key={$invite->invitekey}";
|
|
|
|
$data = [ 'permissions' => '',
|
|
'invitee' => $invite->name,
|
|
'sender' => "{$USER->firstname} {$USER->lastname}",
|
|
'link' => $invitehref];
|
|
|
|
if ($invite->allow_details || $invite->allow_calendar || $invite->allow_badges) {
|
|
$data['permissions'] = get_string('invite_mail_permissions', 'local_treestudyplan');
|
|
$data['permissions'] .= "<ul>\n";
|
|
if ($invite->allow_details ) {
|
|
$data['permissions'] .= "<li>".get_string('invite_allow_details', 'local_treestudyplan')."</li>\n";
|
|
}
|
|
if ($invite->allow_calendar) {
|
|
$data['permissions'] .= "<li>".get_string('invite_allow_calendar', 'local_treestudyplan')."</li>\n";
|
|
}
|
|
if ($invite->allow_badges) {
|
|
$data['permissions'] .= "<li>".get_string('invite_allow_badges', 'local_treestudyplan')."</li>\n";
|
|
}
|
|
|
|
$data['permissions'] .= "</ul></p>\n";
|
|
}
|
|
|
|
$body = get_string('invite_mail_text', 'local_treestudyplan', $data);
|
|
$subject = get_string('invite_mail_subject', 'local_treestudyplan', $data);
|
|
|
|
$html = "
|
|
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>.
|
|
<html xmlns='http://www.w3.org/1999/xhtml'>.
|
|
<head>
|
|
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
|
|
<title>{$subject}</title>
|
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'/>
|
|
</head>
|
|
<body>
|
|
{$body}
|
|
</body>
|
|
</html>";
|
|
|
|
$mailer->isHTML(true);
|
|
$mailer->Subject = $subject;
|
|
$mailer->Body = $html;
|
|
$mailer->AltBody = strip_tags($body);
|
|
|
|
$mailer->send();
|
|
|
|
}
|
|
|
|
/**
|
|
* Hook to display fragment of activity/mod settings editor. Used in feature to edit name and description of activity
|
|
* @param mixed $args
|
|
* @return string Rendered form output HTML
|
|
*/
|
|
function local_treestudyplan_output_fragment_mod_edit_form($args) {
|
|
global $CFG;
|
|
global $DB;
|
|
$args = (object)$args;
|
|
$context = $args->context;
|
|
|
|
if (empty($args->cmid)) {
|
|
return "RANDOM!";
|
|
}
|
|
|
|
// Check the course module exists.
|
|
$cm = \get_coursemodule_from_id('', $args->cmid, 0, false, MUST_EXIST);
|
|
|
|
// Check the course exists.
|
|
$course = \get_course($cm->course);
|
|
|
|
// Require_login.
|
|
require_login($course, false, $cm); // Needed to setup proper $COURSE.
|
|
|
|
list($cm, $context, $module, $data, $cw) = \get_moduleinfo_data($cm, $course);
|
|
|
|
$modmoodleform = "$CFG->dirroot/mod/$module->name/mod_form.php";
|
|
if (file_exists($modmoodleform)) {
|
|
require_once($modmoodleform);
|
|
} else {
|
|
throw new \moodle_exception('noformdesc', 'local_treestudyplan');;
|
|
}
|
|
|
|
$mformclassname = 'mod_'.$module->name.'_mod_form';
|
|
$mform = new $mformclassname($data, $cw->section, $cm, $course);
|
|
$mform->set_data($data);
|
|
|
|
return $mform->render();
|
|
|
|
}
|
|
|
|
/**
|
|
* Serve the files from the myplugin file areas.
|
|
*
|
|
* @param stdClass $course the course object
|
|
* @param stdClass $cm the course module object
|
|
* @param stdClass $context the context
|
|
* @param string $filearea the name of the file area
|
|
* @param array $args extra arguments (itemid, path)
|
|
* @param bool $forcedownload whether or not force download
|
|
* @param array $options additional options affecting the file serving
|
|
* @return bool false if the file not found, just send the file otherwise and do not return anything
|
|
*/
|
|
function local_treestudyplan_pluginfile(
|
|
$course,
|
|
$cm,
|
|
$context,
|
|
string $filearea,
|
|
array $args,
|
|
bool $forcedownload,
|
|
array $options = []
|
|
): bool {
|
|
global $DB,$USER;
|
|
|
|
$fp = fopen("/tmp/debug","a+");
|
|
fputs($fp, print_r([
|
|
'context' => $context,
|
|
'filearea' => $filearea,
|
|
'args' => $args,
|
|
'forcedownload' => $forcedownload,
|
|
'options' => $options,
|
|
],true)."\n\n");
|
|
fclose($fp);
|
|
|
|
$studyplan_filecaps = ["local/treestudyplan:editstudyplan","local/treestudyplan:viewuserreports"];
|
|
|
|
// Check the contextlevel is as expected - the studyplan plugin only uses system context for storing files.
|
|
// This avoids headaches when moving studyplans between contexts, while the security impact is minimal...
|
|
if ($context->contextlevel != CONTEXT_SYSTEM) {
|
|
return false;
|
|
}
|
|
|
|
|
|
// Make sure the filearea is one of those used by the plugin.
|
|
if (in_array($filearea,["studyplan","icon"])) {
|
|
// The args is an array containing [itemid, path].
|
|
// Fetch the itemid from the path.
|
|
$itemid = array_shift($args);
|
|
|
|
$plan = studyplan::find_by_id($itemid);
|
|
$planctx = $plan->context();
|
|
|
|
// Check if the current user has access to this studyplan
|
|
if ( webservicehelper::has_capabilities($studyplan_filecaps,$planctx) || $plan->has_linked_user($USER)) {
|
|
// Extract the filename / filepath from the $args array
|
|
$filename = array_pop($args); // The last item in the $args array.
|
|
if (empty($args)) {
|
|
// $args is empty => the path is '/'.
|
|
$filepath = '/';
|
|
} else {
|
|
// $args contains the remaining elements of the filepath.
|
|
$filepath = '/' . implode('/', $args) . '/';
|
|
}
|
|
|
|
// Retrieve the file from the Files API.
|
|
$fs = get_file_storage();
|
|
$file = $fs->get_file(\context_system::instance()->id, 'local_treestudyplan', $filearea, $itemid, $filepath, $filename);
|
|
if (!$file) {
|
|
// The file does not exist.
|
|
return false;
|
|
}
|
|
// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
|
|
send_stored_file($file, 24*60*60, 0, $forcedownload, $options);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
} else if (in_array($filearea,['defaulticon'])) {
|
|
// The args is an array containing [itemid, path].
|
|
// Fetch the itemid from the path.
|
|
$itemid = array_shift($args);
|
|
|
|
// Extract the filename / filepath from the $args array
|
|
$filename = array_pop($args); // The last item in the $args array.
|
|
if (empty($args)) {
|
|
// $args is empty => the path is '/'.
|
|
$filepath = '/';
|
|
} else {
|
|
// $args contains the remaining elements of the filepath.
|
|
$filepath = '/' . implode('/', $args) . '/';
|
|
}
|
|
|
|
// Retrieve the file from the Files API.
|
|
$fs = get_file_storage();
|
|
$file = $fs->get_file(\context_system::instance()->id, 'local_treestudyplan', $filearea, $itemid, $filepath, $filename);
|
|
if (!$file) {
|
|
// The file does not exist.
|
|
return false;
|
|
}
|
|
// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
|
|
send_stored_file($file, 24*60*60, 0, $forcedownload, $options);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
} |