--- /dev/null
+<?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/>.
+
+/**
+ * Displays the top level category or all courses
+ * In editing mode, allows the admin to edit a category,
+ * and rearrange courses
+ *
+ * @package core
+ * @subpackage course
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once("../config.php");
+require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->libdir.'/textlib.class.php');
+
+$id = required_param('id', PARAM_INT); // Category id
+$page = optional_param('page', 0, PARAM_INT); // which page to show
+$categoryedit = optional_param('categoryedit', -1, PARAM_BOOL);
+$hide = optional_param('hide', 0, PARAM_INT);
+$show = optional_param('show', 0, PARAM_INT);
+$moveup = optional_param('moveup', 0, PARAM_INT);
+$movedown = optional_param('movedown', 0, PARAM_INT);
+$moveto = optional_param('moveto', 0, PARAM_INT);
+$resort = optional_param('resort', 0, PARAM_BOOL);
+$sesskey = optional_param('sesskey', '', PARAM_RAW);
+
+// MDL-27824 - This is a temporary fix until we have the proper
+// way to check/initialize $CFG value.
+// @todo MDL-35138 remove this temporary solution
+if (!empty($CFG->coursesperpage)) {
+ $defaultperpage = $CFG->coursesperpage;
+} else {
+ $defaultperpage = 20;
+}
+$perpage = optional_param('perpage', $defaultperpage, PARAM_INT); // how many per page
+
+if (empty($id)) {
+ print_error("unknowcategory");
+}
+
+$PAGE->set_category_by_id($id);
+$PAGE->set_url(new moodle_url('/course/category.php', array('id' => $id)));
+// This is sure to be the category context
+$context = $PAGE->context;
+// And the object has been loaded for us no need for another DB call
+$category = $PAGE->category;
+
+$canedit = can_edit_in_category($category->id);
+if ($canedit) {
+ if ($categoryedit !== -1) {
+ $USER->editing = $categoryedit;
+ }
+ require_login();
+ $editingon = $PAGE->user_is_editing();
+} else {
+ if ($CFG->forcelogin) {
+ require_login();
+ }
+ $editingon = false;
+}
+
+if (!$category->visible) {
+ require_capability('moodle/category:viewhiddencategories', $context);
+}
+
+$canmanage = has_capability('moodle/category:manage', $context);
+$sesskeyprovided = !empty($sesskey) && confirm_sesskey($sesskey);
+
+// Process any category actions.
+if ($canmanage && $resort && $sesskeyprovided) {
+ // Resort the category if requested
+ if ($courses = get_courses($category->id, '', 'c.id,c.fullname,c.sortorder')) {
+ collatorlib::asort_objects_by_property($courses, 'fullname', collatorlib::SORT_NATURAL);
+ $i = 1;
+ foreach ($courses as $course) {
+ $DB->set_field('course', 'sortorder', $category->sortorder+$i, array('id'=>$course->id));
+ $i++;
+ }
+ fix_course_sortorder(); // should not be needed
+ }
+}
+
+// Process any course actions.
+if ($editingon && $sesskeyprovided) {
+
+ // Move a specified course to a new category
+ if (!empty($moveto) and $data = data_submitted()) {
+ // Some courses are being moved
+ // user must have category update in both cats to perform this
+ require_capability('moodle/category:manage', $context);
+ require_capability('moodle/category:manage', context_coursecat::instance($moveto));
+
+ if (!$destcategory = $DB->get_record('course_categories', array('id' => $data->moveto))) {
+ print_error('cannotfindcategory', '', '', $data->moveto);
+ }
+
+ $courses = array();
+ foreach ($data as $key => $value) {
+ if (preg_match('/^c\d+$/', $key)) {
+ $courseid = substr($key, 1);
+ array_push($courses, $courseid);
+
+ // check this course's category
+ if ($movingcourse = $DB->get_record('course', array('id'=>$courseid))) {
+ if ($movingcourse->category != $id ) {
+ print_error('coursedoesnotbelongtocategory');
+ }
+ } else {
+ print_error('cannotfindcourse');
+ }
+ }
+ }
+ move_courses($courses, $data->moveto);
+ }
+
+ // Hide or show a course
+ if (!empty($hide) or !empty($show)) {
+ if (!empty($hide)) {
+ $course = $DB->get_record('course', array('id' => $hide));
+ $visible = 0;
+ } else {
+ $course = $DB->get_record('course', array('id' => $show));
+ $visible = 1;
+ }
+
+ if ($course) {
+ $coursecontext = context_course::instance($course->id);
+ require_capability('moodle/course:visibility', $coursecontext);
+ // Set the visibility of the course. we set the old flag when user manually changes visibility of course.
+ $DB->update_record('course', array('id' => $course->id, 'visible' => $visible, 'visibleold' => $visible, 'timemodified' => time()));
+ add_to_log($course->id, "course", ($visible ? 'show' : 'hide'), "edit.php?id=$course->id", $course->id);
+ }
+ }
+
+
+ // Move a course up or down
+ if (!empty($moveup) or !empty($movedown)) {
+ require_capability('moodle/category:manage', $context);
+
+ // Ensure the course order has continuous ordering
+ fix_course_sortorder();
+ $swapcourse = NULL;
+
+ if (!empty($moveup)) {
+ if ($movecourse = $DB->get_record('course', array('id' => $moveup))) {
+ $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder - 1));
+ }
+ } else {
+ if ($movecourse = $DB->get_record('course', array('id' => $movedown))) {
+ $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder + 1));
+ }
+ }
+ if ($swapcourse and $movecourse) {
+ // check course's category
+ if ($movecourse->category != $id) {
+ print_error('coursedoesnotbelongtocategory');
+ }
+ $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id' => $movecourse->id));
+ $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id' => $swapcourse->id));
+ add_to_log($movecourse->id, "course", "move", "edit.php?id=$movecourse->id", $movecourse->id);
+ }
+ }
+
+} // End of editing stuff
+
+// Prepare the standard URL params for this page. We'll need them later.
+$urlparams = array('id' => $id);
+if ($page) {
+ $urlparams['page'] = $page;
+}
+if ($perpage) {
+ $urlparams['perpage'] = $perpage;
+}
+
+// Begin output
+if ($editingon && can_edit_in_category()) {
+ // Integrate into the admin tree only if the user can edit categories at the top level,
+ // otherwise the admin block does not appear to this user, and you get an error.
+ require_once($CFG->libdir . '/adminlib.php');
+ navigation_node::override_active_url(new moodle_url('/course/category.php', array('id' => $id)));
+ admin_externalpage_setup('coursemgmt', '', $urlparams, $CFG->wwwroot . '/course/category.php');
+ $PAGE->set_context($context); // Ensure that we are actually showing blocks etc for the cat context
+
+ $settingsnode = $PAGE->settingsnav->find_active_node();
+ if ($settingsnode) {
+ $settingsnode->make_inactive();
+ $settingsnode->force_open();
+ $PAGE->navbar->add($settingsnode->text, $settingsnode->action);
+ }
+ echo $OUTPUT->header();
+} else {
+ $site = get_site();
+ $PAGE->set_title("$site->shortname: $category->name");
+ $PAGE->set_heading($site->fullname);
+ $PAGE->set_button(print_course_search('', true, 'navbar'));
+ $PAGE->set_pagelayout('coursecategory');
+ echo $OUTPUT->header();
+}
+
+/// Print the category selector
+$displaylist = array();
+$notused = array();
+make_categories_list($displaylist, $notused);
+
+echo '<div class="categorypicker">';
+$select = new single_select(new moodle_url('/course/category.php'), 'id', $displaylist, $category->id, null, 'switchcategory');
+$select->set_label(get_string('categories').':');
+echo $OUTPUT->render($select);
+echo '</div>';
+
+/// Print current category description
+if (!$editingon && $category->description) {
+ echo $OUTPUT->box_start();
+ $options = new stdClass;
+ $options->noclean = true;
+ $options->para = false;
+ $options->overflowdiv = true;
+ if (!isset($category->descriptionformat)) {
+ $category->descriptionformat = FORMAT_MOODLE;
+ }
+ $text = file_rewrite_pluginfile_urls($category->description, 'pluginfile.php', $context->id, 'coursecat', 'description', null);
+ echo format_text($text, $category->descriptionformat, $options);
+ echo $OUTPUT->box_end();
+}
+
+if ($editingon && $canmanage) {
+ echo $OUTPUT->container_start('buttons');
+
+ // Print button to update this category
+ $url = new moodle_url('/course/editcategory.php', array('id' => $category->id));
+ echo $OUTPUT->single_button($url, get_string('editcategorythis'), 'get');
+
+ // Print button for creating new categories
+ $url = new moodle_url('/course/editcategory.php', array('parent' => $category->id));
+ echo $OUTPUT->single_button($url, get_string('addsubcategory'), 'get');
+
+ echo $OUTPUT->container_end();
+}
+
+// Print out all the sub-categories
+// In order to view hidden subcategories the user must have the viewhiddencategories
+// capability in the current category.
+if (has_capability('moodle/category:viewhiddencategories', $context)) {
+ $categorywhere = '';
+} else {
+ $categorywhere = 'AND cc.visible = 1';
+}
+// We're going to preload the context for the subcategory as we know that we
+// need it later on for formatting.
+
+$ctxselect = context_helper::get_preload_record_columns_sql('ctx');
+$sql = "SELECT cc.*, $ctxselect
+ FROM {course_categories} cc
+ JOIN {context} ctx ON cc.id = ctx.instanceid
+ WHERE cc.parent = :parentid AND
+ ctx.contextlevel = :contextlevel
+ $categorywhere
+ ORDER BY cc.sortorder ASC";
+$subcategories = $DB->get_recordset_sql($sql, array('parentid' => $category->id, 'contextlevel' => CONTEXT_COURSECAT));
+// Prepare a table to display the sub categories.
+$table = new html_table;
+$table->attributes = array('border' => '0', 'cellspacing' => '2', 'cellpadding' => '4', 'class' => 'generalbox boxaligncenter category_subcategories');
+$table->head = array(new lang_string('subcategories'));
+$table->data = array();
+$baseurl = new moodle_url('/course/category.php');
+foreach ($subcategories as $subcategory) {
+ // Preload the context we will need it to format the category name shortly.
+ context_helper::preload_from_record($subcategory);
+ $context = context_coursecat::instance($subcategory->id);
+ // Prepare the things we need to create a link to the subcategory
+ $attributes = $subcategory->visible ? array() : array('class' => 'dimmed');
+ $text = format_string($subcategory->name, true, array('context' => $context));
+ // Add the subcategory to the table
+ $baseurl->param('id', $subcategory->id);
+ $table->data[] = array(html_writer::link($baseurl, $text, $attributes));
+}
+
+$subcategorieswereshown = (count($table->data) > 0);
+if ($subcategorieswereshown) {
+ echo html_writer::table($table);
+}
+
+// Print out all the courses.
+$courses = get_courses_page($category->id, 'c.sortorder ASC',
+ 'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible',
+ $totalcount, $page*$perpage, $perpage);
+$numcourses = count($courses);
+
+// We can consider that we are using pagination when the total count of courses is different than the one returned.
+$pagingmode = $totalcount != $numcourses;
+
+if (!$courses) {
+ // There is no course to display.
+ if (empty($subcategorieswereshown)) {
+ echo $OUTPUT->heading(get_string("nocoursesyet"));
+ }
+} else if ($numcourses <= $CFG->courseswithsummarieslimit and !$pagingmode and !$editingon) {
+ // We display courses with their summaries as we have not reached the limit, also we are not
+ // in paging mode and not allowed to edit either.
+ echo $OUTPUT->box_start('courseboxes');
+ print_courses($category);
+ echo $OUTPUT->box_end();
+} else {
+ // The conditions above have failed, we display a basic list of courses with paging/editing options.
+ echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "/course/category.php?id=$category->id&perpage=$perpage");
+
+ echo '<form id="movecourses" action="category.php" method="post"><div>';
+ echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
+ echo '<table border="0" cellspacing="2" cellpadding="4" class="generalbox boxaligncenter"><tr>';
+ echo '<th class="header" scope="col">'.get_string('courses').'</th>';
+ if ($editingon) {
+ echo '<th class="header" scope="col">'.get_string('edit').'</th>';
+ echo '<th class="header" scope="col">'.get_string('select').'</th>';
+ } else {
+ echo '<th class="header" scope="col"> </th>';
+ }
+ echo '</tr>';
+
+ $count = 0;
+ $abletomovecourses = false; // for now
+
+ // Checking if we are at the first or at the last page, to allow courses to
+ // be moved up and down beyond the paging border
+ if ($totalcount > $perpage) {
+ $atfirstpage = ($page == 0);
+ if ($perpage > 0) {
+ $atlastpage = (($page + 1) == ceil($totalcount / $perpage));
+ } else {
+ $atlastpage = true;
+ }
+ } else {
+ $atfirstpage = true;
+ $atlastpage = true;
+ }
+
+ $baseurl = new moodle_url('/course/category.php', $urlparams + array('sesskey' => sesskey()));
+ foreach ($courses as $acourse) {
+ $coursecontext = context_course::instance($acourse->id);
+
+ $count++;
+ $up = ($count > 1 || !$atfirstpage);
+ $down = ($count < $numcourses || !$atlastpage);
+
+ $linkcss = $acourse->visible ? '' : ' class="dimmed" ';
+ echo '<tr>';
+ $coursename = get_course_display_name_for_list($acourse);
+ echo '<td><a '.$linkcss.' href="view.php?id='.$acourse->id.'">'. format_string($coursename) .'</a></td>';
+ if ($editingon) {
+ echo '<td>';
+ if (has_capability('moodle/course:update', $coursecontext)) {
+ $url = new moodle_url('/course/edit.php', array('id' => $acourse->id, 'category' => $id, 'returnto' => 'category'));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/edit', get_string('settings')));
+ }
+
+ // role assignment link
+ if (has_capability('moodle/course:enrolreview', $coursecontext)) {
+ $url = new moodle_url('/enrol/users.php', array('id' => $acourse->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/enrolusers', get_string('enrolledusers', 'enrol')));
+ }
+
+ if (can_delete_course($acourse->id)) {
+ $url = new moodle_url('/course/delete.php', array('id' => $acourse->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/delete', get_string('delete')));
+ }
+
+ // MDL-8885, users with no capability to view hidden courses, should not be able to lock themselves out
+ if (has_capability('moodle/course:visibility', $coursecontext) && has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+ if (!empty($acourse->visible)) {
+ $url = new moodle_url($baseurl, array('hide' => $acourse->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/hide', get_string('hide')));
+ } else {
+ $url = new moodle_url($baseurl, array('show' => $acourse->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/show', get_string('show')));
+ }
+ }
+
+ if (has_capability('moodle/backup:backupcourse', $coursecontext)) {
+ $url = new moodle_url('/backup/backup.php', array('id' => $acourse->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/backup', get_string('backup')));
+ }
+
+ if (has_capability('moodle/restore:restorecourse', $coursecontext)) {
+ $url = new moodle_url('/backup/restorefile.php', array('contextid' => $coursecontext->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/restore', get_string('restore')));
+ }
+
+ if ($canmanage) {
+ if ($up) {
+ $url = new moodle_url($baseurl, array('moveup' => $acourse->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/up', get_string('moveup')));
+ }
+
+ if ($down) {
+ $url = new moodle_url($baseurl, array('movedown' => $acourse->id));
+ echo $OUTPUT->action_icon($url, new pix_icon('t/down', get_string('movedown')));
+ }
+ $abletomovecourses = true;
+ }
+
+ echo '</td>';
+ echo '<td align="center">';
+ echo '<input type="checkbox" name="c'.$acourse->id.'" />';
+ echo '</td>';
+ } else {
+ echo '<td align="right">';
+ // print enrol info
+ if ($icons = enrol_get_course_info_icons($acourse)) {
+ foreach ($icons as $pix_icon) {
+ echo $OUTPUT->render($pix_icon);
+ }
+ }
+ if (!empty($acourse->summary)) {
+ $url = new moodle_url("/course/info.php?id=$acourse->id");
+ echo $OUTPUT->action_link($url, '<img alt="'.get_string('info').'" class="icon" src="'.$OUTPUT->pix_url('i/info') . '" />',
+ new popup_action('click', $url, 'courseinfo'), array('title'=>get_string('summary')));
+ }
+ echo "</td>";
+ }
+ echo "</tr>";
+ }
+
+ if ($abletomovecourses) {
+ $movetocategories = array();
+ $notused = array();
+ make_categories_list($movetocategories, $notused, 'moodle/category:manage');
+ $movetocategories[$category->id] = get_string('moveselectedcoursesto');
+ echo '<tr><td colspan="3" align="right">';
+ echo html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
+ echo html_writer::select($movetocategories, 'moveto', $category->id, null, array('id'=>'movetoid', 'class' => 'autosubmit'));
+ $PAGE->requires->yui_module('moodle-core-formautosubmit',
+ 'M.core.init_formautosubmit',
+ array(array('selectid' => 'movetoid', 'nothing' => $category->id))
+ );
+ echo '<input type="hidden" name="id" value="'.$category->id.'" />';
+ echo '</td></tr>';
+ }
+
+ echo '</table>';
+ echo '</div></form>';
+ echo '<br />';
+}
+
+echo '<div class="buttons">';
+if ($canmanage and $numcourses > 1) {
+ // Print button to re-sort courses by name
+ $url = new moodle_url('/course/category.php', array('id' => $category->id, 'resort' => 'name', 'sesskey' => sesskey()));
+ echo $OUTPUT->single_button($url, get_string('resortcoursesbyname'), 'get');
+}
+
+if (has_capability('moodle/course:create', $context)) {
+ // Print button to create a new course
+ $url = new moodle_url('/course/edit.php', array('category' => $category->id, 'returnto' => 'category'));
+ echo $OUTPUT->single_button($url, get_string('addnewcourse'), 'get');
+}
+
+if (!empty($CFG->enablecourserequests) && $category->id == $CFG->defaultrequestcategory) {
+ print_course_request_buttons(context_system::instance());
+}
+echo '</div>';
+
+print_course_search();
+
+echo $OUTPUT->footer();
--- /dev/null
+<?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/>.
+
+/**
+ * This script allows the number of sections in a course to be increased
+ * or decreased, redirecting to the course page.
+ *
+ * @package core_course
+ * @copyright 2012 Dan Poltawski
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.3
+ */
+
+require_once(dirname(__FILE__).'/../config.php');
+require_once($CFG->dirroot.'/course/lib.php');
+
+$courseid = required_param('courseid', PARAM_INT);
+$increase = optional_param('increase', true, PARAM_BOOL);
+$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
+$courseformatoptions = course_get_format($course)->get_format_options();
+
+$PAGE->set_url('/course/changenumsections.php', array('courseid' => $courseid));
+
+// Authorisation checks.
+require_login($course);
+require_capability('moodle/course:update', context_course::instance($course->id));
+require_sesskey();
+
+if (isset($courseformatoptions['numsections'])) {
+ if ($increase) {
+ // Add an additional section.
+ $courseformatoptions['numsections']++;
+ } else {
+ // Remove a section.
+ $courseformatoptions['numsections']--;
+ }
+
+ // Don't go less than 0, intentionally redirect silently (for the case of
+ // double clicks).
+ if ($courseformatoptions['numsections'] >= 0) {
+ course_get_format($course)->update_course_format_options(
+ array('numsections' => $courseformatoptions['numsections']));
+ }
+}
+
+$url = course_get_url($course);
+$url->set_anchor('changenumsections');
+// Redirect to where we were..
+redirect($url);
--- /dev/null
+
+M.core_completion = {};
+
+M.core_completion.init = function(Y) {
+ // Check the reload-forcing
+ var changeDetector = Y.one('#completion_dynamic_change');
+ if (changeDetector.get('value') > 0) {
+ changeDetector.set('value', 0);
+ window.location.reload();
+ return;
+ }
+
+ var handle_success = function(id, o, args) {
+ Y.one('#completion_dynamic_change').set('value', 1);
+
+ if (o.responseText != 'OK') {
+ alert('An error occurred when attempting to save your tick mark.\n\n('+o.responseText+'.)'); //TODO: localize
+
+ } else {
+ var current = args.state.get('value');
+ var modulename = args.modulename.get('value');
+ if (current == 1) {
+ var altstr = M.str.completion['completion-alt-manual-y'].replace('{$a}', modulename);
+ var titlestr = M.str.completion['completion-title-manual-y'].replace('{$a}', modulename);
+ args.state.set('value', 0);
+ args.image.set('src', M.util.image_url('i/completion-manual-y', 'moodle'));
+ args.image.set('alt', altstr);
+ args.image.set('title', titlestr);
+ } else {
+ var altstr = M.str.completion['completion-alt-manual-n'].replace('{$a}', modulename);
+ var titlestr = M.str.completion['completion-title-manual-n'].replace('{$a}', modulename);
+ args.state.set('value', 1);
+ args.image.set('src', M.util.image_url('i/completion-manual-n', 'moodle'));
+ args.image.set('alt', altstr);
+ args.image.set('title', titlestr);
+ }
+ }
+
+ args.ajax.remove();
+ };
+
+ var handle_failure = function(id, o, args) {
+ alert('An error occurred when attempting to save your tick mark.\n\n('+o.responseText+'.)'); //TODO: localize
+ args.ajax.remove();
+ };
+
+ var toggle = function(e) {
+ e.preventDefault();
+
+ var form = e.target;
+ var cmid = 0;
+ var completionstate = 0;
+ var state = null;
+ var image = null;
+ var modulename = null;
+
+ var inputs = Y.Node.getDOMNode(form).getElementsByTagName('input');
+ for (var i=0; i<inputs.length; i++) {
+ switch (inputs[i].name) {
+ case 'id':
+ cmid = inputs[i].value;
+ break;
+ case 'completionstate':
+ completionstate = inputs[i].value;
+ state = Y.one(inputs[i]);
+ break;
+ case 'modulename':
+ modulename = Y.one(inputs[i]);
+ break;
+ }
+ if (inputs[i].type == 'image') {
+ image = Y.one(inputs[i]);
+ }
+ }
+
+ // start spinning the ajax indicator
+ var ajax = Y.Node.create('<div class="ajaxworking" />');
+ form.append(ajax);
+
+ var cfg = {
+ method: "POST",
+ data: 'id='+cmid+'&completionstate='+completionstate+'&fromajax=1&sesskey='+M.cfg.sesskey,
+ on: {
+ success: handle_success,
+ failure: handle_failure
+ },
+ arguments: {state: state, image: image, ajax: ajax, modulename: modulename}
+ };
+
+ Y.use('io-base', function(Y) {
+ Y.io(M.cfg.wwwroot+'/course/togglecompletion.php', cfg);
+ });
+ };
+
+ // register submit handlers on manual tick completion forms
+ Y.all('form.togglecompletion').each(function(form) {
+ if (!form.hasClass('preventjs')) {
+ Y.on('submit', toggle, form);
+ }
+ });
+
+ // hide the help if there are no completion toggles or icons
+ var help = Y.one('#completionprogressid');
+ if (help && !(Y.one('form.togglecompletion') || Y.one('.autocompletion'))) {
+ help.setStyle('display', 'none');
+ }
+};
+
+
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+// Edit course completion settings
+
+require_once('../config.php');
+require_once('lib.php');
+require_once($CFG->libdir.'/completionlib.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_date.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_unenrol.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_activity.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_duration.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_grade.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_role.php');
+require_once($CFG->dirroot.'/completion/criteria/completion_criteria_course.php');
+require_once $CFG->libdir.'/gradelib.php';
+require_once('completion_form.php');
+
+$id = required_param('id', PARAM_INT); // course id
+
+/// basic access control checks
+if ($id) { // editing course
+
+ if($id == SITEID){
+ // don't allow editing of 'site course' using this from
+ print_error('cannoteditsiteform');
+ }
+
+ if (!$course = $DB->get_record('course', array('id'=>$id))) {
+ print_error('invalidcourseid');
+ }
+ require_login($course);
+ require_capability('moodle/course:update', context_course::instance($course->id));
+
+} else {
+ require_login();
+ print_error('needcourseid');
+}
+
+/// Set up the page
+$streditcompletionsettings = get_string("editcoursecompletionsettings", 'completion');
+$PAGE->set_course($course);
+$PAGE->set_url('/course/completion.php', array('id' => $course->id));
+//$PAGE->navbar->add($streditcompletionsettings);
+$PAGE->set_title($course->shortname);
+$PAGE->set_heading($course->fullname);
+$PAGE->set_pagelayout('standard');
+
+/// first create the form
+$form = new course_completion_form('completion.php?id='.$id, compact('course'));
+
+// now override defaults if course already exists
+if ($form->is_cancelled()){
+ redirect($CFG->wwwroot.'/course/view.php?id='.$course->id);
+
+} else if ($data = $form->get_data()) {
+
+ $completion = new completion_info($course);
+
+/// process criteria unlocking if requested
+ if (!empty($data->settingsunlock)) {
+
+ $completion->delete_course_completion_data();
+
+ // Return to form (now unlocked)
+ redirect($CFG->wwwroot."/course/completion.php?id=$course->id");
+ }
+
+/// process data if submitted
+ // Delete old criteria
+ $completion->clear_criteria();
+
+ // Loop through each criteria type and run update_config
+ global $COMPLETION_CRITERIA_TYPES;
+ foreach ($COMPLETION_CRITERIA_TYPES as $type) {
+ $class = 'completion_criteria_'.$type;
+ $criterion = new $class();
+ $criterion->update_config($data);
+ }
+
+ // Handle aggregation methods
+ // Overall aggregation
+ $aggdata = array(
+ 'course' => $data->id,
+ 'criteriatype' => null
+ );
+ $aggregation = new completion_aggregation($aggdata);
+ $aggregation->setMethod($data->overall_aggregation);
+ $aggregation->save();
+
+ // Activity aggregation
+ if (empty($data->activity_aggregation)) {
+ $data->activity_aggregation = 0;
+ }
+
+ $aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ACTIVITY;
+ $aggregation = new completion_aggregation($aggdata);
+ $aggregation->setMethod($data->activity_aggregation);
+ $aggregation->save();
+
+ // Course aggregation
+ if (empty($data->course_aggregation)) {
+ $data->course_aggregation = 0;
+ }
+
+ $aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_COURSE;
+ $aggregation = new completion_aggregation($aggdata);
+ $aggregation->setMethod($data->course_aggregation);
+ $aggregation->save();
+
+ // Role aggregation
+ if (empty($data->role_aggregation)) {
+ $data->role_aggregation = 0;
+ }
+
+ $aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ROLE;
+ $aggregation = new completion_aggregation($aggdata);
+ $aggregation->setMethod($data->role_aggregation);
+ $aggregation->save();
+
+ add_to_log($course->id, 'course', 'completion updated', 'completion.php?id='.$course->id);
+
+ $url = new moodle_url('/course/view.php', array('id' => $course->id));
+ redirect($url);
+}
+
+
+/// Print the form
+
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($streditcompletionsettings);
+
+$form->display();
+
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.com //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+
+require_once($CFG->libdir.'/formslib.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+class course_completion_form extends moodleform {
+
+ function definition() {
+ global $USER, $CFG, $DB, $js_enabled;
+
+ $courseconfig = get_config('moodlecourse');
+ $mform =& $this->_form;
+
+ $course = $this->_customdata['course'];
+ $completion = new completion_info($course);
+
+ $params = array(
+ 'course' => $course->id
+ );
+
+
+/// form definition
+//--------------------------------------------------------------------------------
+
+ // Check if there is existing criteria completions
+ if ($completion->is_course_locked()) {
+ $mform->addElement('header', '', get_string('completionsettingslocked', 'completion'));
+ $mform->addElement('static', '', '', get_string('err_settingslocked', 'completion'));
+ $mform->addElement('submit', 'settingsunlock', get_string('unlockcompletiondelete', 'completion'));
+ }
+
+ // Get array of all available aggregation methods
+ $aggregation_methods = $completion->get_aggregation_methods();
+
+ // Overall criteria aggregation
+ $mform->addElement('header', 'overallcriteria', get_string('overallcriteriaaggregation', 'completion'));
+ $mform->addElement('select', 'overall_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('overall_aggregation', $completion->get_aggregation_method());
+
+ // Course prerequisite completion criteria
+ $mform->addElement('header', 'courseprerequisites', get_string('completiondependencies', 'completion'));
+
+ // Get applicable courses
+ $courses = $DB->get_records_sql(
+ "
+ SELECT DISTINCT
+ c.id,
+ c.category,
+ c.fullname,
+ cc.id AS selected
+ FROM
+ {course} c
+ LEFT JOIN
+ {course_completion_criteria} cc
+ ON cc.courseinstance = c.id
+ AND cc.course = {$course->id}
+ INNER JOIN
+ {course_completion_criteria} ccc
+ ON ccc.course = c.id
+ WHERE
+ c.enablecompletion = ".COMPLETION_ENABLED."
+ AND c.id <> {$course->id}
+ "
+ );
+
+ if (!empty($courses)) {
+ if (count($courses) > 1) {
+ $mform->addElement('select', 'course_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('course_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_COURSE));
+ }
+
+ // Get category list
+ $list = array();
+ $parents = array();
+ make_categories_list($list, $parents);
+
+ // Get course list for select box
+ $selectbox = array();
+ $selected = array();
+ foreach ($courses as $c) {
+ $selectbox[$c->id] = $list[$c->category] . ' / ' . format_string($c->fullname, true, array('context' => context_course::instance($c->id)));
+
+ // If already selected
+ if ($c->selected) {
+ $selected[] = $c->id;
+ }
+ }
+
+ // Show multiselect box
+ $mform->addElement('select', 'criteria_course', get_string('coursesavailable', 'completion'), $selectbox, array('multiple' => 'multiple', 'size' => 6));
+
+ // Select current criteria
+ $mform->setDefault('criteria_course', $selected);
+
+ // Explain list
+ $mform->addElement('static', 'criteria_courses_explaination', '', get_string('coursesavailableexplaination', 'completion'));
+
+ } else {
+ $mform->addElement('static', 'nocourses', '', get_string('err_nocourses', 'completion'));
+ }
+
+ // Manual self completion
+ $mform->addElement('header', 'manualselfcompletion', get_string('manualselfcompletion', 'completion'));
+ $criteria = new completion_criteria_self($params);
+ $criteria->config_form_display($mform);
+
+ // Role completion criteria
+ $mform->addElement('header', 'roles', get_string('manualcompletionby', 'completion'));
+
+ $roles = get_roles_with_capability('moodle/course:markcomplete', CAP_ALLOW, context_course::instance($course->id, IGNORE_MISSING));
+
+ if (!empty($roles)) {
+ $mform->addElement('select', 'role_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('role_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ROLE));
+
+ foreach ($roles as $role) {
+ $params_a = array('role' => $role->id);
+ $criteria = new completion_criteria_role(array_merge($params, $params_a));
+ $criteria->config_form_display($mform, $role);
+ }
+ } else {
+ $mform->addElement('static', 'noroles', '', get_string('err_noroles', 'completion'));
+ }
+
+ // Activity completion criteria
+ $mform->addElement('header', 'activitiescompleted', get_string('activitiescompleted', 'completion'));
+
+ $activities = $completion->get_activities();
+ if (!empty($activities)) {
+ if (count($activities) > 1) {
+ $mform->addElement('select', 'activity_aggregation', get_string('aggregationmethod', 'completion'), $aggregation_methods);
+ $mform->setDefault('activity_aggregation', $completion->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ACTIVITY));
+ }
+
+ foreach ($activities as $activity) {
+ $params_a = array('moduleinstance' => $activity->id);
+ $criteria = new completion_criteria_activity(array_merge($params, $params_a));
+ $criteria->config_form_display($mform, $activity);
+ }
+ } else {
+ $mform->addElement('static', 'noactivities', '', get_string('err_noactivities', 'completion'));
+ }
+
+ // Completion on date
+ $mform->addElement('header', 'date', get_string('date'));
+ $criteria = new completion_criteria_date($params);
+ $criteria->config_form_display($mform);
+
+ // Completion after enrolment duration
+ $mform->addElement('header', 'duration', get_string('durationafterenrolment', 'completion'));
+ $criteria = new completion_criteria_duration($params);
+ $criteria->config_form_display($mform);
+
+ // Completion on course grade
+ $mform->addElement('header', 'grade', get_string('coursegrade', 'completion'));
+
+ // Grade enable and passing grade
+ $course_grade = $DB->get_field('grade_items', 'gradepass', array('courseid' => $course->id, 'itemtype' => 'course'));
+ if (!$course_grade) {
+ $course_grade = '0.00000';
+ }
+ $criteria = new completion_criteria_grade($params);
+ $criteria->config_form_display($mform, $course_grade);
+
+ // Completion on unenrolment
+ $mform->addElement('header', 'unenrolment', get_string('unenrolment', 'completion'));
+ $criteria = new completion_criteria_unenrol($params);
+ $criteria->config_form_display($mform);
+
+
+//--------------------------------------------------------------------------------
+ $this->add_action_buttons();
+//--------------------------------------------------------------------------------
+ $mform->addElement('hidden', 'id', $course->id);
+ $mform->setType('id', PARAM_INT);
+
+ // If the criteria are locked, freeze values and submit button
+ if ($completion->is_course_locked()) {
+ $except = array('settingsunlock');
+ $mform->hardFreezeAllVisibleExcept($except);
+ $mform->addElement('cancel');
+ }
+ }
+
+
+/// perform some extra moodle validation
+ function validation($data, $files) {
+ global $DB, $CFG;
+
+ $errors = parent::validation($data, $files);
+
+ return $errors;
+ }
+}
+?>
--- /dev/null
+<?php
+ // Admin-only code to delete a course utterly
+
+ require_once(dirname(__FILE__) . '/../config.php');
+ require_once($CFG->dirroot . '/course/lib.php');
+
+ $id = required_param('id', PARAM_INT); // course id
+ $delete = optional_param('delete', '', PARAM_ALPHANUM); // delete confirmation hash
+
+ $PAGE->set_url('/course/delete.php', array('id' => $id));
+ $PAGE->set_context(context_system::instance());
+ require_login();
+
+ $site = get_site();
+
+ $strdeletecourse = get_string("deletecourse");
+ $stradministration = get_string("administration");
+ $strcategories = get_string("categories");
+
+ if (! $course = $DB->get_record("course", array("id"=>$id))) {
+ print_error("invalidcourseid");
+ }
+ if ($site->id == $course->id) {
+ // can not delete frontpage!
+ print_error("invalidcourseid");
+ }
+
+ $coursecontext = context_course::instance($course->id);
+
+ if (!can_delete_course($id)) {
+ print_error('cannotdeletecourse');
+ }
+
+ $category = $DB->get_record("course_categories", array("id"=>$course->category));
+ $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
+ $categoryname = format_string($category->name, true, array('context' => context_coursecat::instance($category->id)));
+
+ $PAGE->navbar->add($stradministration, new moodle_url('/admin/index.php/'));
+ $PAGE->navbar->add($strcategories, new moodle_url('/course/index.php'));
+ $PAGE->navbar->add($categoryname, new moodle_url('/course/category.php', array('id'=>$course->category)));
+ if (! $delete) {
+ $strdeletecheck = get_string("deletecheck", "", $courseshortname);
+ $strdeletecoursecheck = get_string("deletecoursecheck");
+
+ $PAGE->navbar->add($strdeletecheck);
+ $PAGE->set_title("$site->shortname: $strdeletecheck");
+ $PAGE->set_heading($site->fullname);
+ echo $OUTPUT->header();
+
+ $message = "$strdeletecoursecheck<br /><br />" . format_string($course->fullname, true, array('context' => $coursecontext)) . " (" . $courseshortname . ")";
+
+ echo $OUTPUT->confirm($message, "delete.php?id=$course->id&delete=".md5($course->timemodified), "category.php?id=$course->category");
+
+ echo $OUTPUT->footer();
+ exit;
+ }
+
+ if ($delete != md5($course->timemodified)) {
+ print_error("invalidmd5");
+ }
+
+ if (!confirm_sesskey()) {
+ print_error('confirmsesskeybad', 'error');
+ }
+
+ // OK checks done, delete the course now.
+
+ add_to_log(SITEID, "course", "delete", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
+
+ $strdeletingcourse = get_string("deletingcourse", "", $courseshortname);
+
+ $PAGE->navbar->add($strdeletingcourse);
+ $PAGE->set_title("$site->shortname: $strdeletingcourse");
+ $PAGE->set_heading($site->fullname);
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading($strdeletingcourse);
+
+ delete_course($course);
+ fix_course_sortorder(); //update course count in catagories
+
+ echo $OUTPUT->heading( get_string("deletedcourse", "", $courseshortname) );
+
+ echo $OUTPUT->continue_button("category.php?id=$course->category");
+
+ echo $OUTPUT->footer();
+
+
--- /dev/null
+<?php
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+
+require_once($CFG->libdir.'/formslib.php');
+require_once($CFG->libdir.'/questionlib.php');
+
+class delete_category_form extends moodleform {
+
+ var $_category;
+
+ function definition() {
+ global $CFG, $DB;
+
+ $mform =& $this->_form;
+ $category = $this->_customdata;
+ $categorycontext = context_coursecat::instance($category->id);
+ $this->_category = $category;
+
+ /// Check permissions, to see if it OK to give the option to delete
+ /// the contents, rather than move elsewhere.
+ /// Are there any subcategories of this one, can they be deleted?
+ $candeletecontent = true;
+ $tocheck = get_child_categories($category->id);
+ $containscategories = !empty($tocheck);
+ $categoryids = array($category->id);
+ while (!empty($tocheck)) {
+ $checkcat = array_pop($tocheck);
+ $childcategoryids[] = $checkcat->id;
+ $tocheck = $tocheck + get_child_categories($checkcat->id);
+ $chcontext = context_coursecat::instance($checkcat->id);
+ if ($candeletecontent && !has_capability('moodle/category:manage', $chcontext)) {
+ $candeletecontent = false;
+ }
+ }
+
+ /// Are there any courses in here, can they be deleted?
+ list($test, $params) = $DB->get_in_or_equal($categoryids);
+ $containedcourses = $DB->get_records_sql(
+ "SELECT id,1 FROM {course} c WHERE c.category $test", $params);
+ $containscourses = false;
+ if ($containedcourses) {
+ $containscourses = true;
+ foreach ($containedcourses as $courseid => $notused) {
+ if ($candeletecontent && !can_delete_course($courseid)) {
+ $candeletecontent = false;
+ break;
+ }
+ }
+ }
+
+ /// Are there any questions in the question bank here?
+ $containsquestions = question_context_has_any_questions($categorycontext);
+
+ /// Get the list of categories we might be able to move to.
+ $testcaps = array();
+ if ($containscourses) {
+ $testcaps[] = 'moodle/course:create';
+ }
+ if ($containscategories || $containsquestions) {
+ $testcaps[] = 'moodle/category:manage';
+ }
+ $displaylist = array();
+ $notused = array();
+ if (!empty($testcaps)) {
+ make_categories_list($displaylist, $notused, $testcaps, $category->id);
+ }
+
+ /// Now build the options.
+ $options = array();
+ if ($displaylist) {
+ $options[0] = get_string('movecontentstoanothercategory');
+ }
+ if ($candeletecontent) {
+ $options[1] = get_string('deleteallcannotundo');
+ }
+
+ /// Now build the form.
+ $mform->addElement('header','general', get_string('categorycurrentcontents', '', format_string($category->name, true, array('context' => $categorycontext))));
+
+ if ($containscourses || $containscategories || $containsquestions) {
+ if (empty($options)) {
+ print_error('youcannotdeletecategory', 'error', 'index.php', format_string($category->name, true, array('context' => $categorycontext)));
+ }
+
+ /// Describe the contents of this category.
+ $contents = '<ul>';
+ if ($containscategories) {
+ $contents .= '<li>' . get_string('subcategories') . '</li>';
+ }
+ if ($containscourses) {
+ $contents .= '<li>' . get_string('courses') . '</li>';
+ }
+ if ($containsquestions) {
+ $contents .= '<li>' . get_string('questionsinthequestionbank') . '</li>';
+ }
+ $contents .= '</ul>';
+ $mform->addElement('static', 'emptymessage', get_string('thiscategorycontains'), $contents);
+
+ /// Give the options for what to do.
+ $mform->addElement('select', 'fulldelete', get_string('whattodo'), $options);
+ if (count($options) == 1) {
+ $optionkeys = array_keys($options);
+ $option = reset($optionkeys);
+ $mform->hardFreeze('fulldelete');
+ $mform->setConstant('fulldelete', $option);
+ }
+
+ if ($displaylist) {
+ $mform->addElement('select', 'newparent', get_string('movecategorycontentto'), $displaylist);
+ if (in_array($category->parent, $displaylist)) {
+ $mform->setDefault('newparent', $category->parent);
+ }
+ $mform->disabledIf('newparent', 'fulldelete', 'eq', '1');
+ }
+ } else {
+ $mform->addElement('hidden', 'fulldelete', 1);
+ $mform->setType('fulldelete', PARAM_INT);
+ $mform->addElement('static', 'emptymessage', '', get_string('deletecategoryempty'));
+ }
+
+ $mform->addElement('hidden', 'delete');
+ $mform->setType('delete', PARAM_ALPHANUM);
+ $mform->addElement('hidden', 'sure');
+ $mform->setType('sure', PARAM_ALPHANUM);
+ $mform->setDefault('sure', md5(serialize($category)));
+
+//--------------------------------------------------------------------------------
+ $this->add_action_buttons(true, get_string('delete'));
+
+ }
+
+/// perform some extra moodle validation
+ function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+
+ if (empty($data['fulldelete']) && empty($data['newparent'])) {
+ /// When they have chosen the move option, they must specify a destination.
+ $errors['newparent'] = get_string('required');
+ }
+
+ if ($data['sure'] != md5(serialize($this->_category))) {
+ $errors['categorylabel'] = get_string('categorymodifiedcancel');
+ }
+
+ return $errors;
+ }
+}
+
--- /dev/null
+// 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/>.
+
+/**
+ * Javascript library for enableing a drag and drop upload to courses
+ *
+ * @package core
+ * @subpackage course
+ * @copyright 2012 Davo Smith
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.course_dndupload = {
+ // YUI object.
+ Y: null,
+ // URL for upload requests
+ url: M.cfg.wwwroot + '/course/dndupload.php',
+ // maximum size of files allowed in this form
+ maxbytes: 0,
+ // ID of the course we are on
+ courseid: null,
+ // Data about the different file/data handlers that are available
+ handlers: null,
+ // Nasty hack to distinguish between dragenter(first entry),
+ // dragenter+dragleave(moving between child elements) and dragleave (leaving element)
+ entercount: 0,
+ // Used to keep track of the section we are dragging across - to make
+ // spotting movement between sections more reliable
+ currentsection: null,
+ // Used to store the pending uploads whilst the user is being asked for further input
+ uploadqueue: null,
+ // True if the there is currently a dialog being shown (asking for a name, or giving a
+ // choice of file handlers)
+ uploaddialog: false,
+ // An array containing the last selected file handler for each file type
+ lastselected: null,
+
+ // The following are used to identify specific parts of the course page
+
+ // The type of HTML element that is a course section
+ sectiontypename: 'li',
+ // The classes that an element must have to be identified as a course section
+ sectionclasses: ['section', 'main'],
+ // The ID of the main content area of the page (for adding the 'status' div)
+ pagecontentid: 'page',
+ // The selector identifying the list of modules within a section (note changing this may require
+ // changes to the get_mods_element function)
+ modslistselector: 'ul.section',
+
+ /**
+ * Initalise the drag and drop upload interface
+ * Note: one and only one of options.filemanager and options.formcallback must be defined
+ *
+ * @param Y the YUI object
+ * @param object options {
+ * courseid: ID of the course we are on
+ * maxbytes: maximum size of files allowed in this form
+ * handlers: Data about the different file/data handlers that are available
+ * }
+ */
+ init: function(Y, options) {
+ this.Y = Y;
+
+ if (!this.browser_supported()) {
+ return; // Browser does not support the required functionality
+ }
+
+ this.maxbytes = options.maxbytes;
+ this.courseid = options.courseid;
+ this.handlers = options.handlers;
+ this.uploadqueue = new Array();
+ this.lastselected = new Array();
+
+ var sectionselector = this.sectiontypename + '.' + this.sectionclasses.join('.');
+ var sections = this.Y.all(sectionselector);
+ if (sections.isEmpty()) {
+ return; // No sections - incompatible course format or front page.
+ }
+ sections.each( function(el) {
+ this.add_preview_element(el);
+ this.init_events(el);
+ }, this);
+
+ if (options.showstatus) {
+ this.add_status_div();
+ }
+ },
+
+ /**
+ * Add a div element to tell the user that drag and drop upload
+ * is available (or to explain why it is not available)
+ */
+ add_status_div: function() {
+ var coursecontents = document.getElementById(this.pagecontentid);
+ if (!coursecontents) {
+ return;
+ }
+
+ var div = document.createElement('div');
+ div.id = 'dndupload-status';
+ div.style.opacity = 0.0;
+ coursecontents.insertBefore(div, coursecontents.firstChild);
+
+ var Y = this.Y;
+ div = Y.one(div);
+ var handlefile = (this.handlers.filehandlers.length > 0);
+ var handletext = false;
+ var handlelink = false;
+ var i;
+ for (i=0; i<this.handlers.types.length; i++) {
+ switch (this.handlers.types[i].identifier) {
+ case 'text':
+ case 'text/html':
+ handletext = true;
+ break;
+ case 'url':
+ handlelink = true;
+ break;
+ }
+ }
+ $msgident = 'dndworking';
+ if (handlefile) {
+ $msgident += 'file';
+ }
+ if (handletext) {
+ $msgident += 'text';
+ }
+ if (handlelink) {
+ $msgident += 'link';
+ }
+ div.setContent(M.util.get_string($msgident, 'moodle'));
+
+ var fadeanim = new Y.Anim({
+ node: '#dndupload-status',
+ from: {
+ opacity: 0.0,
+ top: '-30px'
+ },
+
+ to: {
+ opacity: 1.0,
+ top: '0px'
+ },
+ duration: 0.5
+ });
+ fadeanim.once('end', function(e) {
+ this.set('reverse', 1);
+ Y.later(3000, this, 'run', null, false);
+ });
+ fadeanim.run();
+ },
+
+ /**
+ * Check the browser has the required functionality
+ * @return true if browser supports drag/drop upload
+ */
+ browser_supported: function() {
+ if (typeof FileReader == 'undefined') {
+ return false;
+ }
+ if (typeof FormData == 'undefined') {
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Initialise drag events on node container, all events need
+ * to be processed for drag and drop to work
+ * @param el the element to add events to
+ */
+ init_events: function(el) {
+ this.Y.on('dragenter', this.drag_enter, el, this);
+ this.Y.on('dragleave', this.drag_leave, el, this);
+ this.Y.on('dragover', this.drag_over, el, this);
+ this.Y.on('drop', this.drop, el, this);
+ },
+
+ /**
+ * Work out which course section a given element is in
+ * @param el the child DOM element within the section
+ * @return the DOM element representing the section
+ */
+ get_section: function(el) {
+ var sectionclasses = this.sectionclasses;
+ return el.ancestor( function(test) {
+ var i;
+ for (i=0; i<sectionclasses.length; i++) {
+ if (!test.hasClass(sectionclasses[i])) {
+ return false;
+ }
+ return true;
+ }
+ }, true);
+ },
+
+ /**
+ * Work out the number of the section we have been dropped on to, from the section element
+ * @param DOMElement section the selected section
+ * @return int the section number
+ */
+ get_section_number: function(section) {
+ var sectionid = section.get('id').split('-');
+ if (sectionid.length < 2 || sectionid[0] != 'section') {
+ return false;
+ }
+ return parseInt(sectionid[1]);
+ },
+
+ /**
+ * Check if the event includes data of the given type
+ * @param e the event details
+ * @param type the data type to check for
+ * @return true if the data type is found in the event data
+ */
+ types_includes: function(e, type) {
+ var i;
+ var types = e._event.dataTransfer.types;
+ for (i=0; i<types.length; i++) {
+ if (types[i] == type) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Look through the event data, checking it against the registered data types
+ * (in order of priority) and return details of the first matching data type
+ * @param e the event details
+ * @return mixed false if not found or an object {
+ * realtype: the type as given by the browser
+ * addmessage: the message to show to the user during dragging
+ * namemessage: the message for requesting a name for the resource from the user
+ * type: the identifier of the type (may match several 'realtype's)
+ * }
+ */
+ drag_type: function(e) {
+ // Check there is some data attached.
+ if (e._event.dataTransfer === null) {
+ return false;
+ }
+ if (e._event.dataTransfer.types === null) {
+ return false;
+ }
+ if (e._event.dataTransfer.types.length == 0) {
+ return false;
+ }
+
+ // Check for files first.
+ if (this.types_includes(e, 'Files')) {
+ if (e.type != 'drop' || e._event.dataTransfer.files.length != 0) {
+ if (this.handlers.filehandlers.length == 0) {
+ return false; // No available file handlers - ignore this drag.
+ }
+ return {
+ realtype: 'Files',
+ addmessage: M.util.get_string('addfilehere', 'moodle'),
+ namemessage: null, // Should not be asked for anyway
+ type: 'Files'
+ };
+ }
+ }
+
+ // Check each of the registered types.
+ var types = this.handlers.types;
+ for (var i=0; i<types.length; i++) {
+ // Check each of the different identifiers for this type
+ var dttypes = types[i].datatransfertypes;
+ for (var j=0; j<dttypes.length; j++) {
+ if (this.types_includes(e, dttypes[j])) {
+ return {
+ realtype: dttypes[j],
+ addmessage: types[i].addmessage,
+ namemessage: types[i].namemessage,
+ type: types[i].identifier,
+ handlers: types[i].handlers
+ };
+ }
+ }
+ }
+ return false; // No types we can handle
+ },
+
+ /**
+ * Check the content of the drag/drop includes a type we can handle, then, if
+ * it is, notify the browser that we want to handle it
+ * @param event e
+ * @return string type of the event or false
+ */
+ check_drag: function(e) {
+ var type = this.drag_type(e);
+ if (type) {
+ // Notify browser that we will handle this drag/drop
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ return type;
+ },
+
+ /**
+ * Handle a dragenter event: add a suitable 'add here' message
+ * when a drag event occurs, containing a registered data type
+ * @param e event data
+ * @return false to prevent the event from continuing to be processed
+ */
+ drag_enter: function(e) {
+ if (!(type = this.check_drag(e))) {
+ return false;
+ }
+
+ var section = this.get_section(e.currentTarget);
+ if (!section) {
+ return false;
+ }
+
+ if (this.currentsection && this.currentsection != section) {
+ this.currentsection = section;
+ this.entercount = 1;
+ } else {
+ this.entercount++;
+ if (this.entercount > 2) {
+ this.entercount = 2;
+ return false;
+ }
+ }
+
+ this.show_preview_element(section, type);
+
+ return false;
+ },
+
+ /**
+ * Handle a dragleave event: remove the 'add here' message (if present)
+ * @param e event data
+ * @return false to prevent the event from continuing to be processed
+ */
+ drag_leave: function(e) {
+ if (!this.check_drag(e)) {
+ return false;
+ }
+
+ this.entercount--;
+ if (this.entercount == 1) {
+ return false;
+ }
+ this.entercount = 0;
+ this.currentsection = null;
+
+ this.hide_preview_element();
+ return false;
+ },
+
+ /**
+ * Handle a dragover event: just prevent the browser default (necessary
+ * to allow drag and drop handling to work)
+ * @param e event data
+ * @return false to prevent the event from continuing to be processed
+ */
+ drag_over: function(e) {
+ this.check_drag(e);
+ return false;
+ },
+
+ /**
+ * Handle a drop event: hide the 'add here' message, check the attached
+ * data type and start the upload process
+ * @param e event data
+ * @return false to prevent the event from continuing to be processed
+ */
+ drop: function(e) {
+ if (!(type = this.check_drag(e))) {
+ return false;
+ }
+
+ this.hide_preview_element();
+
+ // Work out the number of the section we are on (from its id)
+ var section = this.get_section(e.currentTarget);
+ var sectionnumber = this.get_section_number(section);
+
+ // Process the file or the included data
+ if (type.type == 'Files') {
+ var files = e._event.dataTransfer.files;
+ for (var i=0, f; f=files[i]; i++) {
+ this.handle_file(f, section, sectionnumber);
+ }
+ } else {
+ var contents = e._event.dataTransfer.getData(type.realtype);
+ if (contents) {
+ this.handle_item(type, contents, section, sectionnumber);
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Find or create the 'ul' element that contains all of the module
+ * instances in this section
+ * @param section the DOM element representing the section
+ * @return false to prevent the event from continuing to be processed
+ */
+ get_mods_element: function(section) {
+ // Find the 'ul' containing the list of mods
+ var modsel = section.one(this.modslistselector);
+ if (!modsel) {
+ // Create the above 'ul' if it doesn't exist
+ var modsel = document.createElement('ul');
+ modsel.className = 'section img-text';
+ var contentel = section.get('children').pop();
+ var brel = contentel.get('children').pop();
+ contentel.insertBefore(modsel, brel);
+ modsel = this.Y.one(modsel);
+ }
+
+ return modsel;
+ },
+
+ /**
+ * Add a new dummy item to the list of mods, to be replaced by a real
+ * item & link once the AJAX upload call has completed
+ * @param name the label to show in the element
+ * @param section the DOM element reperesenting the course section
+ * @return DOM element containing the new item
+ */
+ add_resource_element: function(name, section) {
+ var modsel = this.get_mods_element(section);
+
+ var resel = {
+ parent: modsel,
+ li: document.createElement('li'),
+ div: document.createElement('div'),
+ indentdiv: document.createElement('div'),
+ a: document.createElement('a'),
+ icon: document.createElement('img'),
+ namespan: document.createElement('span'),
+ groupingspan: document.createElement('span'),
+ progressouter: document.createElement('span'),
+ progress: document.createElement('span')
+ };
+
+ resel.li.className = 'activity resource modtype_resource';
+
+ resel.indentdiv.className = 'mod-indent';
+ resel.li.appendChild(resel.indentdiv);
+
+ resel.div.className = 'activityinstance';
+ resel.indentdiv.appendChild(resel.div);
+
+ resel.a.href = '#';
+ resel.div.appendChild(resel.a);
+
+ resel.icon.src = M.util.image_url('i/ajaxloader');
+ resel.icon.className = 'activityicon iconlarge';
+ resel.a.appendChild(resel.icon);
+
+ resel.namespan.className = 'instancename';
+ resel.namespan.innerHTML = name;
+ resel.a.appendChild(resel.namespan);
+
+ resel.groupingspan.className = 'groupinglabel';
+ resel.div.appendChild(resel.groupingspan);
+
+ resel.progressouter.className = 'dndupload-progress-outer';
+ resel.progress.className = 'dndupload-progress-inner';
+ resel.progress.innerHTML = ' ';
+ resel.progressouter.appendChild(resel.progress);
+ resel.div.appendChild(resel.progressouter);
+
+ modsel.insertBefore(resel.li, modsel.get('children').pop()); // Leave the 'preview element' at the bottom
+
+ return resel;
+ },
+
+ /**
+ * Hide any visible dndupload-preview elements on the page
+ */
+ hide_preview_element: function() {
+ this.Y.all('li.dndupload-preview').addClass('dndupload-hidden');
+ },
+
+ /**
+ * Unhide the preview element for the given section and set it to display
+ * the correct message
+ * @param section the YUI node representing the selected course section
+ * @param type the details of the data type detected in the drag (including the message to display)
+ */
+ show_preview_element: function(section, type) {
+ this.hide_preview_element();
+ var preview = section.one('li.dndupload-preview').removeClass('dndupload-hidden');
+ preview.one('span').setContent(type.addmessage);
+ },
+
+ /**
+ * Add the preview element to a course section. Note: this needs to be done before 'addEventListener'
+ * is called, otherwise Firefox will ignore events generated when the mouse is over the preview
+ * element (instead of passing them up to the parent element)
+ * @param section the YUI node representing the selected course section
+ */
+ add_preview_element: function(section) {
+ var modsel = this.get_mods_element(section);
+ var preview = {
+ li: document.createElement('li'),
+ div: document.createElement('div'),
+ icon: document.createElement('img'),
+ namespan: document.createElement('span')
+ };
+
+ preview.li.className = 'dndupload-preview dndupload-hidden';
+
+ preview.div.className = 'mod-indent';
+ preview.li.appendChild(preview.div);
+
+ preview.icon.src = M.util.image_url('t/addfile');
+ preview.icon.className = 'icon';
+ preview.div.appendChild(preview.icon);
+
+ preview.div.appendChild(document.createTextNode(' '));
+
+ preview.namespan.className = 'instancename';
+ preview.namespan.innerHTML = M.util.get_string('addfilehere', 'moodle');
+ preview.div.appendChild(preview.namespan);
+
+ modsel.appendChild(preview.li);
+ },
+
+ /**
+ * Find the registered handler for the given file type. If there is more than one, ask the
+ * user which one to use. Then upload the file to the server
+ * @param file the details of the file, taken from the FileList in the drop event
+ * @param section the DOM element representing the selected course section
+ * @param sectionnumber the number of the selected course section
+ */
+ handle_file: function(file, section, sectionnumber) {
+ var handlers = new Array();
+ var filehandlers = this.handlers.filehandlers;
+ var extension = '';
+ var dotpos = file.name.lastIndexOf('.');
+ if (dotpos != -1) {
+ extension = file.name.substr(dotpos+1, file.name.length);
+ }
+
+ for (var i=0; i<filehandlers.length; i++) {
+ if (filehandlers[i].extension == '*' || filehandlers[i].extension == extension) {
+ handlers.push(filehandlers[i]);
+ }
+ }
+
+ if (handlers.length == 0) {
+ // No handlers at all (not even 'resource'?)
+ return;
+ }
+
+ if (handlers.length == 1) {
+ this.upload_file(file, section, sectionnumber, handlers[0].module);
+ return;
+ }
+
+ this.file_handler_dialog(handlers, extension, file, section, sectionnumber);
+ },
+
+ /**
+ * Show a dialog box, allowing the user to choose what to do with the file they are uploading
+ * @param handlers the available handlers to choose between
+ * @param extension the extension of the file being uploaded
+ * @param file the File object being uploaded
+ * @param section the DOM element of the section being uploaded to
+ * @param sectionnumber the number of the selected course section
+ */
+ file_handler_dialog: function(handlers, extension, file, section, sectionnumber) {
+ if (this.uploaddialog) {
+ var details = new Object();
+ details.isfile = true;
+ details.handlers = handlers;
+ details.extension = extension;
+ details.file = file;
+ details.section = section;
+ details.sectionnumber = sectionnumber;
+ this.uploadqueue.push(details);
+ return;
+ }
+ this.uploaddialog = true;
+
+ var timestamp = new Date().getTime();
+ var uploadid = Math.round(Math.random()*100000)+'-'+timestamp;
+ var content = '';
+ var sel;
+ if (extension in this.lastselected) {
+ sel = this.lastselected[extension];
+ } else {
+ sel = handlers[0].module;
+ }
+ content += '<p>'+M.util.get_string('actionchoice', 'moodle', file.name)+'</p>';
+ content += '<div id="dndupload_handlers'+uploadid+'">';
+ for (var i=0; i<handlers.length; i++) {
+ var id = 'dndupload_handler'+uploadid+handlers[i].module;
+ var checked = (handlers[i].module == sel) ? 'checked="checked" ' : '';
+ content += '<input type="radio" name="handler" value="'+handlers[i].module+'" id="'+id+'" '+checked+'/>';
+ content += ' <label for="'+id+'">';
+ content += handlers[i].message;
+ content += '</label><br/>';
+ }
+ content += '</div>';
+
+ var Y = this.Y;
+ var self = this;
+ var panel = new Y.Panel({
+ bodyContent: content,
+ width: 350,
+ zIndex: 5,
+ centered: true,
+ modal: true,
+ visible: true,
+ render: true,
+ buttons: [{
+ value: M.util.get_string('upload', 'moodle'),
+ action: function(e) {
+ e.preventDefault();
+ // Find out which module was selected
+ var module = false;
+ var div = Y.one('#dndupload_handlers'+uploadid);
+ div.all('input').each(function(input) {
+ if (input.get('checked')) {
+ module = input.get('value');
+ }
+ });
+ if (!module) {
+ return;
+ }
+ panel.hide();
+ // Remember this selection for next time
+ self.lastselected[extension] = module;
+ // Do the upload
+ self.upload_file(file, section, sectionnumber, module);
+ },
+ section: Y.WidgetStdMod.FOOTER
+ },{
+ value: M.util.get_string('cancel', 'moodle'),
+ action: function(e) {
+ e.preventDefault();
+ panel.hide();
+ },
+ section: Y.WidgetStdMod.FOOTER
+ }]
+ });
+ // When the panel is hidden - destroy it and then check for other pending uploads
+ panel.after("visibleChange", function(e) {
+ if (!panel.get('visible')) {
+ panel.destroy(true);
+ self.check_upload_queue();
+ }
+ });
+ },
+
+ /**
+ * Check to see if there are any other dialog boxes to show, now that the current one has
+ * been dealt with
+ */
+ check_upload_queue: function() {
+ this.uploaddialog = false;
+ if (this.uploadqueue.length == 0) {
+ return;
+ }
+
+ var details = this.uploadqueue.shift();
+ if (details.isfile) {
+ this.file_handler_dialog(details.handlers, details.extension, details.file, details.section, details.sectionnumber);
+ } else {
+ this.handle_item(details.type, details.contents, details.section, details.sectionnumber);
+ }
+ },
+
+ /**
+ * Do the file upload: show the dummy element, use an AJAX call to send the data
+ * to the server, update the progress bar for the file, then replace the dummy
+ * element with the real information once the AJAX call completes
+ * @param file the details of the file, taken from the FileList in the drop event
+ * @param section the DOM element representing the selected course section
+ * @param sectionnumber the number of the selected course section
+ */
+ upload_file: function(file, section, sectionnumber, module) {
+
+ // This would be an ideal place to use the Y.io function
+ // however, this does not support data encoded using the
+ // FormData object, which is needed to transfer data from
+ // the DataTransfer object into an XMLHTTPRequest
+ // This can be converted when the YUI issue has been integrated:
+ // http://yuilibrary.com/projects/yui3/ticket/2531274
+ var xhr = new XMLHttpRequest();
+ var self = this;
+
+ if (file.size > this.maxbytes) {
+ alert("'"+file.name+"' "+M.util.get_string('filetoolarge', 'moodle'));
+ return;
+ }
+
+ // Add the file to the display
+ var resel = this.add_resource_element(file.name, section);
+
+ // Update the progress bar as the file is uploaded
+ xhr.upload.addEventListener('progress', function(e) {
+ if (e.lengthComputable) {
+ var percentage = Math.round((e.loaded * 100) / e.total);
+ resel.progress.style.width = percentage + '%';
+ }
+ }, false);
+
+ // Wait for the AJAX call to complete, then update the
+ // dummy element with the returned details
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ var result = JSON.parse(xhr.responseText);
+ if (result) {
+ if (result.error == 0) {
+ // All OK - update the dummy element
+ resel.icon.src = result.icon;
+ resel.a.href = result.link;
+ resel.namespan.innerHTML = result.name;
+ if (!parseInt(result.visible, 10)) {
+ resel.a.className = 'dimmed';
+ }
+
+ if (result.groupingname) {
+ resel.groupingspan.innerHTML = '(' + result.groupingname + ')';
+ } else {
+ resel.div.removeChild(resel.groupingspan);
+ }
+
+ resel.div.removeChild(resel.progressouter);
+ resel.li.id = result.elementid;
+ resel.indentdiv.innerHTML += result.commands;
+ if (result.onclick) {
+ resel.a.onclick = result.onclick;
+ }
+ if (self.Y.UA.gecko > 0) {
+ // Fix a Firefox bug which makes sites with a '~' in their wwwroot
+ // log the user out when clicking on the link (before refreshing the page).
+ resel.div.innerHTML = unescape(resel.div.innerHTML);
+ }
+ self.add_editing(result.elementid);
+ } else {
+ // Error - remove the dummy element
+ resel.parent.removeChild(resel.li);
+ alert(result.error);
+ }
+ }
+ } else {
+ alert(M.util.get_string('servererror', 'moodle'));
+ }
+ }
+ };
+
+ // Prepare the data to send
+ var formData = new FormData();
+ formData.append('repo_upload_file', file);
+ formData.append('sesskey', M.cfg.sesskey);
+ formData.append('course', this.courseid);
+ formData.append('section', sectionnumber);
+ formData.append('module', module);
+ formData.append('type', 'Files');
+
+ // Send the AJAX call
+ xhr.open("POST", this.url, true);
+ xhr.send(formData);
+ },
+
+ /**
+ * Show a dialog box to gather the name of the resource / activity to be created
+ * from the uploaded content
+ * @param type the details of the type of content
+ * @param contents the contents to be uploaded
+ * @section the DOM element for the section being uploaded to
+ * @sectionnumber the number of the section being uploaded to
+ */
+ handle_item: function(type, contents, section, sectionnumber) {
+ if (type.handlers.length == 0) {
+ // Nothing to handle this - should not have got here
+ return;
+ }
+
+ if (this.uploaddialog) {
+ var details = new Object();
+ details.isfile = false;
+ details.type = type;
+ details.contents = contents;
+ details.section = section;
+ details.setcionnumber = sectionnumber;
+ this.uploadqueue.push(details);
+ return;
+ }
+ this.uploaddialog = true;
+
+ var timestamp = new Date().getTime();
+ var uploadid = Math.round(Math.random()*100000)+'-'+timestamp;
+ var nameid = 'dndupload_handler_name'+uploadid;
+ var content = '';
+ content += '<label for="'+nameid+'">'+type.namemessage+'</label>';
+ content += ' <input type="text" id="'+nameid+'" value="" />';
+ if (type.handlers.length > 1) {
+ content += '<div id="dndupload_handlers'+uploadid+'">';
+ var sel = type.handlers[0].module;
+ for (var i=0; i<type.handlers.length; i++) {
+ var id = 'dndupload_handler'+uploadid;
+ var checked = (type.handlers[i].module == sel) ? 'checked="checked" ' : '';
+ content += '<input type="radio" name="handler" value="'+type.handlers[i].module+'" id="'+id+'" '+checked+'/>';
+ content += ' <label for="'+id+'">';
+ content += type.handlers[i].message;
+ content += '</label><br/>';
+ }
+ content += '</div>';
+ }
+
+ var Y = this.Y;
+ var self = this;
+ var panel = new Y.Panel({
+ bodyContent: content,
+ width: 350,
+ zIndex: 5,
+ centered: true,
+ modal: true,
+ visible: true,
+ render: true,
+ buttons: [{
+ value: M.util.get_string('upload', 'moodle'),
+ action: function(e) {
+ e.preventDefault();
+ var name = Y.one('#dndupload_handler_name'+uploadid).get('value');
+ name = name.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // Trim
+ if (name == '') {
+ return;
+ }
+ var module = false;
+ if (type.handlers.length > 1) {
+ // Find out which module was selected
+ var div = Y.one('#dndupload_handlers'+uploadid);
+ div.all('input').each(function(input) {
+ if (input.get('checked')) {
+ module = input.get('value');
+ }
+ });
+ if (!module) {
+ return;
+ }
+ } else {
+ module = type.handlers[0].module;
+ }
+ panel.hide();
+ // Do the upload
+ self.upload_item(name, type.type, contents, section, sectionnumber, module);
+ },
+ section: Y.WidgetStdMod.FOOTER
+ },{
+ value: M.util.get_string('cancel', 'moodle'),
+ action: function(e) {
+ e.preventDefault();
+ panel.hide();
+ },
+ section: Y.WidgetStdMod.FOOTER
+ }]
+ });
+ // When the panel is hidden - destroy it and then check for other pending uploads
+ panel.after("visibleChange", function(e) {
+ if (!panel.get('visible')) {
+ panel.destroy(true);
+ self.check_upload_queue();
+ }
+ });
+ // Focus on the 'name' box
+ Y.one('#'+nameid).focus();
+ },
+
+ /**
+ * Upload any data types that are not files: display a dummy resource element, send
+ * the data to the server, update the progress bar for the file, then replace the
+ * dummy element with the real information once the AJAX call completes
+ * @param name the display name for the resource / activity to create
+ * @param type the details of the data type found in the drop event
+ * @param contents the actual data that was dropped
+ * @param section the DOM element representing the selected course section
+ * @param sectionnumber the number of the selected course section
+ * @param module the module chosen to handle this upload
+ */
+ upload_item: function(name, type, contents, section, sectionnumber, module) {
+
+ // This would be an ideal place to use the Y.io function
+ // however, this does not support data encoded using the
+ // FormData object, which is needed to transfer data from
+ // the DataTransfer object into an XMLHTTPRequest
+ // This can be converted when the YUI issue has been integrated:
+ // http://yuilibrary.com/projects/yui3/ticket/2531274
+ var xhr = new XMLHttpRequest();
+ var self = this;
+
+ // Add the item to the display
+ var resel = this.add_resource_element(name, section);
+
+ // Wait for the AJAX call to complete, then update the
+ // dummy element with the returned details
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ var result = JSON.parse(xhr.responseText);
+ if (result) {
+ if (result.error == 0) {
+ // All OK - update the dummy element
+ resel.icon.src = result.icon;
+ resel.a.href = result.link;
+ resel.namespan.innerHTML = result.name;
+ if (!parseInt(result.visible, 10)) {
+ resel.a.className = 'dimmed';
+ }
+
+ if (result.groupingname) {
+ resel.groupingspan.innerHTML = '(' + result.groupingname + ')';
+ } else {
+ resel.div.removeChild(resel.groupingspan);
+ }
+
+ resel.div.removeChild(resel.progressouter);
+ resel.li.id = result.elementid;
+ resel.div.innerHTML += result.commands;
+ if (result.onclick) {
+ resel.a.onclick = result.onclick;
+ }
+ if (self.Y.UA.gecko > 0) {
+ // Fix a Firefox bug which makes sites with a '~' in their wwwroot
+ // log the user out when clicking on the link (before refreshing the page).
+ resel.div.innerHTML = unescape(resel.div.innerHTML);
+ }
+ self.add_editing(result.elementid, sectionnumber);
+ } else {
+ // Error - remove the dummy element
+ resel.parent.removeChild(resel.li);
+ alert(result.error);
+ }
+ }
+ } else {
+ alert(M.util.get_string('servererror', 'moodle'));
+ }
+ }
+ };
+
+ // Prepare the data to send
+ var formData = new FormData();
+ formData.append('contents', contents);
+ formData.append('displayname', name);
+ formData.append('sesskey', M.cfg.sesskey);
+ formData.append('course', this.courseid);
+ formData.append('section', sectionnumber);
+ formData.append('type', type);
+ formData.append('module', module);
+
+ // Send the data
+ xhr.open("POST", this.url, true);
+ xhr.send(formData);
+ },
+
+ /**
+ * Call the AJAX course editing initialisation to add the editing tools
+ * to the newly-created resource link
+ * @param elementid the id of the DOM element containing the new resource link
+ * @param sectionnumber the number of the selected course section
+ */
+ add_editing: function(elementid) {
+ YUI().use('moodle-course-coursebase', function(Y) {
+ M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);
+ });
+ }
+};
--- /dev/null
+<?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/>.
+
+/**
+ * Starting point for drag and drop course uploads
+ *
+ * @package core
+ * @subpackage lib
+ * @copyright 2012 Davo smith
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('AJAX_SCRIPT', true);
+
+require_once(dirname(dirname(__FILE__)).'/config.php');
+require_once($CFG->dirroot.'/course/dnduploadlib.php');
+
+$courseid = required_param('course', PARAM_INT);
+$section = required_param('section', PARAM_INT);
+$type = required_param('type', PARAM_TEXT);
+$modulename = required_param('module', PARAM_PLUGIN);
+$displayname = optional_param('displayname', null, PARAM_TEXT);
+$contents = optional_param('contents', null, PARAM_RAW); // It will be up to each plugin to clean this data, before saving it.
+
+$dndproc = new dndupload_ajax_processor($courseid, $section, $type, $modulename);
+$dndproc->process($displayname, $contents);
--- /dev/null
+<?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/>.
+
+/**
+ * Library to handle drag and drop course uploads
+ *
+ * @package core
+ * @subpackage lib
+ * @copyright 2012 Davo smith
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot.'/repository/lib.php');
+require_once($CFG->dirroot.'/repository/upload/lib.php');
+require_once($CFG->dirroot.'/course/lib.php');
+
+/**
+ * Add the Javascript to enable drag and drop upload to a course page
+ *
+ * @param object $course The currently displayed course
+ * @param array $modnames The list of enabled (visible) modules on this site
+ * @return void
+ */
+function dndupload_add_to_course($course, $modnames) {
+ global $CFG, $PAGE;
+
+ $showstatus = optional_param('notifyeditingon', false, PARAM_BOOL);
+
+ // Get all handlers.
+ $handler = new dndupload_handler($course, $modnames);
+ $jsdata = $handler->get_js_data();
+ if (empty($jsdata->types) && empty($jsdata->filehandlers)) {
+ return; // No valid handlers - don't enable drag and drop.
+ }
+
+ // Add the javascript to the page.
+ $jsmodule = array(
+ 'name' => 'coursedndupload',
+ 'fullpath' => new moodle_url('/course/dndupload.js'),
+ 'strings' => array(
+ array('addfilehere', 'moodle'),
+ array('dndworkingfiletextlink', 'moodle'),
+ array('dndworkingfilelink', 'moodle'),
+ array('dndworkingfiletext', 'moodle'),
+ array('dndworkingfile', 'moodle'),
+ array('dndworkingtextlink', 'moodle'),
+ array('dndworkingtext', 'moodle'),
+ array('dndworkinglink', 'moodle'),
+ array('filetoolarge', 'moodle'),
+ array('actionchoice', 'moodle'),
+ array('servererror', 'moodle'),
+ array('upload', 'moodle'),
+ array('cancel', 'moodle')
+ ),
+ 'requires' => array('node', 'event', 'panel', 'json', 'anim')
+ );
+ $vars = array(
+ array('courseid' => $course->id,
+ 'maxbytes' => get_max_upload_file_size($CFG->maxbytes, $course->maxbytes),
+ 'handlers' => $handler->get_js_data(),
+ 'showstatus' => $showstatus)
+ );
+
+ $PAGE->requires->js_init_call('M.course_dndupload.init', $vars, true, $jsmodule);
+}
+
+
+/**
+ * Stores all the information about the available dndupload handlers
+ *
+ * @package core
+ * @copyright 2012 Davo Smith
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class dndupload_handler {
+
+ /**
+ * @var array A list of all registered mime types that can be dropped onto a course
+ * along with the modules that will handle them.
+ */
+ protected $types = array();
+
+ /**
+ * @var array A list of the different file types (extensions) that different modules
+ * will handle.
+ */
+ protected $filehandlers = array();
+
+ /**
+ * Gather a list of dndupload handlers from the different mods
+ *
+ * @param object $course The course this is being added to (to check course_allowed_module() )
+ */
+ public function __construct($course, $modnames = null) {
+ global $CFG;
+
+ // Add some default types to handle.
+ // Note: 'Files' type is hard-coded into the Javascript as this needs to be ...
+ // ... treated a little differently.
+ $this->add_type('url', array('url', 'text/uri-list', 'text/x-moz-url'), get_string('addlinkhere', 'moodle'),
+ get_string('nameforlink', 'moodle'), 10);
+ $this->add_type('text/html', array('text/html'), get_string('addpagehere', 'moodle'),
+ get_string('nameforpage', 'moodle'), 20);
+ $this->add_type('text', array('text', 'text/plain'), get_string('addpagehere', 'moodle'),
+ get_string('nameforpage', 'moodle'), 30);
+
+ // Loop through all modules to find handlers.
+ $mods = get_plugin_list_with_function('mod', 'dndupload_register');
+ foreach ($mods as $component => $funcname) {
+ list($modtype, $modname) = normalize_component($component);
+ if ($modnames && !array_key_exists($modname, $modnames)) {
+ continue; // Module is deactivated (hidden) at the site level.
+ }
+ if (!course_allowed_module($course, $modname)) {
+ continue; // User does not have permission to add this module to the course.
+ }
+ $resp = $funcname();
+ if (!$resp) {
+ continue;
+ }
+ if (isset($resp['files'])) {
+ foreach ($resp['files'] as $file) {
+ $this->add_file_handler($file['extension'], $modname, $file['message']);
+ }
+ }
+ if (isset($resp['addtypes'])) {
+ foreach ($resp['addtypes'] as $type) {
+ if (isset($type['priority'])) {
+ $priority = $type['priority'];
+ } else {
+ $priority = 100;
+ }
+ $this->add_type($type['identifier'], $type['datatransfertypes'],
+ $type['addmessage'], $type['namemessage'], $priority);
+ }
+ }
+ if (isset($resp['types'])) {
+ foreach ($resp['types'] as $type) {
+ $this->add_type_handler($type['identifier'], $modname, $type['message']);
+ }
+ }
+ }
+ }
+
+ /**
+ * Used to add a new mime type that can be drag and dropped onto a
+ * course displayed in a browser window
+ *
+ * @param string $identifier The name that this type will be known as
+ * @param array $datatransfertypes An array of the different types in the browser
+ * 'dataTransfer.types' object that will map to this type
+ * @param string $addmessage The message to display in the browser when this type is being
+ * dragged onto the page
+ * @param string $namemessage The message to pop up when asking for the name to give the
+ * course module instance when it is created
+ * @param int $priority Controls the order in which types are checked by the browser (mainly
+ * needed to check for 'text' last as that is usually given as fallback)
+ */
+ public function add_type($identifier, $datatransfertypes, $addmessage, $namemessage, $priority=100) {
+ if ($this->is_known_type($identifier)) {
+ throw new coding_exception("Type $identifier is already registered");
+ }
+
+ $add = new stdClass;
+ $add->identifier = $identifier;
+ $add->datatransfertypes = $datatransfertypes;
+ $add->addmessage = $addmessage;
+ $add->namemessage = $namemessage;
+ $add->priority = $priority;
+ $add->handlers = array();
+
+ $this->types[$identifier] = $add;
+ }
+
+ /**
+ * Used to declare that a particular module will handle a particular type
+ * of dropped data
+ *
+ * @param string $type The name of the type (as declared in add_type)
+ * @param string $module The name of the module to handle this type
+ * @param string $message The message to show the user if more than one handler is registered
+ * for a type and the user needs to make a choice between them
+ */
+ public function add_type_handler($type, $module, $message) {
+ if (!$this->is_known_type($type)) {
+ throw new coding_exception("Trying to add handler for unknown type $type");
+ }
+
+ $add = new stdClass;
+ $add->type = $type;
+ $add->module = $module;
+ $add->message = $message;
+
+ $this->types[$type]->handlers[] = $add;
+ }
+
+ /**
+ * Used to declare that a particular module will handle a particular type
+ * of dropped file
+ *
+ * @param string $extension The file extension to handle ('*' for all types)
+ * @param string $module The name of the module to handle this type
+ * @param string $message The message to show the user if more than one handler is registered
+ * for a type and the user needs to make a choice between them
+ */
+ public function add_file_handler($extension, $module, $message) {
+ $extension = strtolower($extension);
+
+ $add = new stdClass;
+ $add->extension = $extension;
+ $add->module = $module;
+ $add->message = $message;
+
+ $this->filehandlers[] = $add;
+ }
+
+ /**
+ * Check to see if the type has been registered
+ *
+ * @param string $type The identifier of the type you are interested in
+ * @return bool True if the type is registered
+ */
+ public function is_known_type($type) {
+ return array_key_exists($type, $this->types);
+ }
+
+ /**
+ * Check to see if the module in question has registered to handle the
+ * type given
+ *
+ * @param string $module The name of the module
+ * @param string $type The identifier of the type
+ * @return bool True if the module has registered to handle that type
+ */
+ public function has_type_handler($module, $type) {
+ if (!$this->is_known_type($type)) {
+ throw new coding_exception("Checking for handler for unknown type $type");
+ }
+ foreach ($this->types[$type]->handlers as $handler) {
+ if ($handler->module == $module) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check to see if the module in question has registered to handle files
+ * with the given extension (or to handle all file types)
+ *
+ * @param string $module The name of the module
+ * @param string $extension The extension of the uploaded file
+ * @return bool True if the module has registered to handle files with
+ * that extension (or to handle all file types)
+ */
+ public function has_file_handler($module, $extension) {
+ foreach ($this->filehandlers as $handler) {
+ if ($handler->module == $module) {
+ if ($handler->extension == '*' || $handler->extension == $extension) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a list of the file types that are handled by a particular module
+ *
+ * @param string $module The name of the module to check
+ * @return array of file extensions or string '*'
+ */
+ public function get_handled_file_types($module) {
+ $types = array();
+ foreach ($this->filehandlers as $handler) {
+ if ($handler->module == $module) {
+ if ($handler->extension == '*') {
+ return '*';
+ } else {
+ // Prepending '.' as otherwise mimeinfo fails.
+ $types[] = '.'.$handler->extension;
+ }
+ }
+ }
+ return $types;
+ }
+
+ /**
+ * Returns an object to pass onto the javascript code with data about all the
+ * registered file / type handlers
+ *
+ * @return object Data to pass on to Javascript code
+ */
+ public function get_js_data() {
+ global $CFG;
+
+ $ret = new stdClass;
+
+ // Sort the types by priority.
+ uasort($this->types, array($this, 'type_compare'));
+
+ $ret->types = array();
+ if (!empty($CFG->dndallowtextandlinks)) {
+ foreach ($this->types as $type) {
+ if (empty($type->handlers)) {
+ continue; // Skip any types without registered handlers.
+ }
+ $ret->types[] = $type;
+ }
+ }
+
+ $ret->filehandlers = $this->filehandlers;
+ $uploadrepo = repository::get_instances(array('type' => 'upload'));
+ if (empty($uploadrepo)) {
+ $ret->filehandlers = array(); // No upload repo => no file handlers.
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Comparison function used when sorting types by priority
+ * @param object $type1 first type to compare
+ * @param object $type2 second type to compare
+ * @return integer -1 for $type1 < $type2; 1 for $type1 > $type2; 0 for equal
+ */
+ protected function type_compare($type1, $type2) {
+ if ($type1->priority < $type2->priority) {
+ return -1;
+ }
+ if ($type1->priority > $type2->priority) {
+ return 1;
+ }
+ return 0;
+ }
+
+}
+
+/**
+ * Processes the upload, creating the course module and returning the result
+ *
+ * @package core
+ * @copyright 2012 Davo Smith
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class dndupload_ajax_processor {
+
+ /** Returned when no error has occurred */
+ const ERROR_OK = 0;
+
+ /** @var object The course that we are uploading to */
+ protected $course = null;
+
+ /** @var context_course The course context for capability checking */
+ protected $context = null;
+
+ /** @var int The section number we are uploading to */
+ protected $section = null;
+
+ /** @var string The type of upload (e.g. 'Files', 'text/plain') */
+ protected $type = null;
+
+ /** @var object The details of the module type that will be created */
+ protected $module= null;
+
+ /** @var object The course module that has been created */
+ protected $cm = null;
+
+ /** @var dndupload_handler used to check the allowed file types */
+ protected $dnduploadhandler = null;
+
+ /** @var string The name to give the new activity instance */
+ protected $displayname = null;
+
+ /**
+ * Set up some basic information needed to handle the upload
+ *
+ * @param int $courseid The ID of the course we are uploading to
+ * @param int $section The section number we are uploading to
+ * @param string $type The type of upload (as reported by the browser)
+ * @param string $modulename The name of the module requested to handle this upload
+ */
+ public function __construct($courseid, $section, $type, $modulename) {
+ global $DB;
+
+ if (!defined('AJAX_SCRIPT')) {
+ throw new coding_exception('dndupload_ajax_processor should only be used within AJAX requests');
+ }
+
+ $this->course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
+
+ require_login($this->course, false);
+ $this->context = context_course::instance($this->course->id);
+
+ if (!is_number($section) || $section < 0) {
+ throw new coding_exception("Invalid section number $section");
+ }
+ $this->section = $section;
+ $this->type = $type;
+
+ if (!$this->module = $DB->get_record('modules', array('name' => $modulename))) {
+ throw new coding_exception("Module $modulename does not exist");
+ }
+
+ $this->dnduploadhandler = new dndupload_handler($this->course);
+ }
+
+ /**
+ * Check if this upload is a 'file' upload
+ *
+ * @return bool true if it is a 'file' upload, false otherwise
+ */
+ protected function is_file_upload() {
+ return ($this->type == 'Files');
+ }
+
+ /**
+ * Process the upload - creating the module in the course and returning the result to the browser
+ *
+ * @param string $displayname optional the name (from the browser) to give the course module instance
+ * @param string $content optional the content of the upload (for non-file uploads)
+ */
+ public function process($displayname = null, $content = null) {
+ require_capability('moodle/course:manageactivities', $this->context);
+
+ if ($this->is_file_upload()) {
+ require_capability('moodle/course:managefiles', $this->context);
+ if ($content != null) {
+ throw new moodle_exception('fileuploadwithcontent', 'moodle');
+ }
+ } else {
+ if (empty($content)) {
+ throw new moodle_exception('dnduploadwithoutcontent', 'moodle');
+ }
+ }
+
+ require_sesskey();
+
+ $this->displayname = $displayname;
+
+ if ($this->is_file_upload()) {
+ $this->handle_file_upload();
+ } else {
+ $this->handle_other_upload($content);
+ }
+ }
+
+ /**
+ * Handle uploads containing files - create the course module, ask the upload repository
+ * to process the file, ask the mod to set itself up, then return the result to the browser
+ */
+ protected function handle_file_upload() {
+ global $CFG;
+
+ // Add the file to a draft file area.
+ $draftitemid = file_get_unused_draft_itemid();
+ $maxbytes = get_max_upload_file_size($CFG->maxbytes, $this->course->maxbytes);
+ $types = $this->dnduploadhandler->get_handled_file_types($this->module->name);
+ $repo = repository::get_instances(array('type' => 'upload'));
+ if (empty($repo)) {
+ throw new moodle_exception('errornouploadrepo', 'moodle');
+ }
+ $repo = reset($repo); // Get the first (and only) upload repo.
+ $details = $repo->process_upload(null, $maxbytes, $types, '/', $draftitemid);
+ if (empty($this->displayname)) {
+ $this->displayname = $this->display_name_from_file($details['file']);
+ }
+
+ // Create a course module to hold the new instance.
+ $this->create_course_module();
+
+ // Ask the module to set itself up.
+ $moduledata = $this->prepare_module_data($draftitemid);
+ $instanceid = plugin_callback('mod', $this->module->name, 'dndupload', 'handle', array($moduledata), 'invalidfunction');
+ if ($instanceid === 'invalidfunction') {
+ throw new coding_exception("{$this->module->name} does not support drag and drop upload (missing {$this->module->name}_dndupload_handle function");
+ }
+
+ // Finish setting up the course module.
+ $this->finish_setup_course_module($instanceid);
+ }
+
+ /**
+ * Handle uploads not containing file - create the course module, ask the mod to
+ * set itself up, then return the result to the browser
+ *
+ * @param string $content the content uploaded to the browser
+ */
+ protected function handle_other_upload($content) {
+ // Check this plugin is registered to handle this type of upload
+ if (!$this->dnduploadhandler->has_type_handler($this->module->name, $this->type)) {
+ $info = (object)array('modname' => $this->module->name, 'type' => $this->type);
+ throw new moodle_exception('moddoesnotsupporttype', 'moodle', $info);
+ }
+
+ // Create a course module to hold the new instance.
+ $this->create_course_module();
+
+ // Ask the module to set itself up.
+ $moduledata = $this->prepare_module_data(null, $content);
+ $instanceid = plugin_callback('mod', $this->module->name, 'dndupload', 'handle', array($moduledata), 'invalidfunction');
+ if ($instanceid === 'invalidfunction') {
+ throw new coding_exception("{$this->module->name} does not support drag and drop upload (missing {$this->module->name}_dndupload_handle function");
+ }
+
+ // Finish setting up the course module.
+ $this->finish_setup_course_module($instanceid);
+ }
+
+ /**
+ * Generate the name of the mod instance from the name of the file
+ * (remove the extension and convert underscore => space
+ *
+ * @param string $filename the filename of the uploaded file
+ * @return string the display name to use
+ */
+ protected function display_name_from_file($filename) {
+ $pos = textlib::strrpos($filename, '.');
+ if ($pos) { // Want to skip if $pos === 0 OR $pos === false.
+ $filename = textlib::substr($filename, 0, $pos);
+ }
+ return str_replace('_', ' ', $filename);
+ }
+
+ /**
+ * Create the coursemodule to hold the file/content that has been uploaded
+ */
+ protected function create_course_module() {
+ if (!course_allowed_module($this->course, $this->module->name)) {
+ throw new coding_exception("The module {$this->module->name} is not allowed to be added to this course");
+ }
+
+ $this->cm = new stdClass();
+ $this->cm->course = $this->course->id;
+ $this->cm->section = $this->section;
+ $this->cm->module = $this->module->id;
+ $this->cm->modulename = $this->module->name;
+ $this->cm->instance = 0; // This will be filled in after we create the instance.
+ $this->cm->visible = 1;
+ $this->cm->groupmode = $this->course->groupmode;
+ $this->cm->groupingid = $this->course->defaultgroupingid;
+
+ // Set the correct default for completion tracking.
+ $this->cm->completion = COMPLETION_TRACKING_NONE;
+ $completion = new completion_info($this->course);
+ if ($completion->is_enabled()) {
+ if (plugin_supports('mod', $this->cm->modulename, FEATURE_MODEDIT_DEFAULT_COMPLETION, true)) {
+ $this->cm->completion = COMPLETION_TRACKING_MANUAL;
+ }
+ }
+
+ if (!$this->cm->id = add_course_module($this->cm)) {
+ throw new coding_exception("Unable to create the course module");
+ }
+ // The following are used inside some few core functions, so may as well set them here.
+ $this->cm->coursemodule = $this->cm->id;
+ $groupbuttons = ($this->course->groupmode or (!$this->course->groupmodeforce));
+ if ($groupbuttons and plugin_supports('mod', $this->module->name, FEATURE_GROUPS, 0)) {
+ $this->cm->groupmodelink = (!$this->course->groupmodeforce);
+ } else {
+ $this->cm->groupmodelink = false;
+ $this->cm->groupmode = false;
+ }
+ }
+
+ /**
+ * Gather together all the details to pass on to the mod, so that it can initialise it's
+ * own database tables
+ *
+ * @param int $draftitemid optional the id of the draft area containing the file (for file uploads)
+ * @param string $content optional the content dropped onto the course (for non-file uploads)
+ * @return object data to pass on to the mod, containing:
+ * string $type the 'type' as registered with dndupload_handler (or 'Files')
+ * object $course the course the upload was for
+ * int $draftitemid optional the id of the draft area containing the files
+ * int $coursemodule id of the course module that has already been created
+ * string $displayname the name to use for this activity (can be overriden by the mod)
+ */
+ protected function prepare_module_data($draftitemid = null, $content = null) {
+ $data = new stdClass();
+ $data->type = $this->type;
+ $data->course = $this->course;
+ if ($draftitemid) {
+ $data->draftitemid = $draftitemid;
+ } else if ($content) {
+ $data->content = $content;
+ }
+ $data->coursemodule = $this->cm->id;
+ $data->displayname = $this->displayname;
+ return $data;
+ }
+
+ /**
+ * Called after the mod has set itself up, to finish off any course module settings
+ * (set instance id, add to correct section, set visibility, etc.) and send the response
+ *
+ * @param int $instanceid id returned by the mod when it was created
+ */
+ protected function finish_setup_course_module($instanceid) {
+ global $DB, $USER;
+
+ if (!$instanceid) {
+ // Something has gone wrong - undo everything we can.
+ delete_course_module($this->cm->id);
+ throw new moodle_exception('errorcreatingactivity', 'moodle', '', $this->module->name);
+ }
+
+ // Note the section visibility
+ $visible = get_fast_modinfo($this->course)->get_section_info($this->section)->visible;
+
+ $DB->set_field('course_modules', 'instance', $instanceid, array('id' => $this->cm->id));
+ // Rebuild the course cache after update action
+ rebuild_course_cache($this->course->id, true);
+ $this->course->modinfo = null; // Otherwise we will just get the old version back again.
+
+ $sectionid = course_add_cm_to_section($this->course, $this->cm->id, $this->section);
+
+ set_coursemodule_visible($this->cm->id, $visible);
+ if (!$visible) {
+ $DB->set_field('course_modules', 'visibleold', 1, array('id' => $this->cm->id));
+ }
+
+ // retrieve the final info about this module.
+ $info = get_fast_modinfo($this->course);
+ if (!isset($info->cms[$this->cm->id])) {
+ // The course module has not been properly created in the course - undo everything.
+ delete_course_module($this->cm->id);
+ throw new moodle_exception('errorcreatingactivity', 'moodle', '', $this->module->name);
+ }
+ $mod = $info->get_cm($this->cm->id);
+ $mod->groupmodelink = $this->cm->groupmodelink;
+ $mod->groupmode = $this->cm->groupmode;
+
+ // Trigger mod_created event with information about this module.
+ $eventdata = new stdClass();
+ $eventdata->modulename = $mod->modname;
+ $eventdata->name = $mod->name;
+ $eventdata->cmid = $mod->id;
+ $eventdata->courseid = $this->course->id;
+ $eventdata->userid = $USER->id;
+ events_trigger('mod_created', $eventdata);
+
+ add_to_log($this->course->id, "course", "add mod",
+ "../mod/{$mod->modname}/view.php?id=$mod->id",
+ "{$mod->modname} $instanceid");
+ add_to_log($this->course->id, $mod->modname, "add",
+ "view.php?id=$mod->id",
+ "$instanceid", $mod->id);
+
+ $this->send_response($mod);
+ }
+
+ /**
+ * Send the details of the newly created activity back to the client browser
+ *
+ * @param cm_info $mod details of the mod just created
+ */
+ protected function send_response($mod) {
+ global $OUTPUT;
+
+ $resp = new stdClass();
+ $resp->error = self::ERROR_OK;
+ $resp->icon = $mod->get_icon_url()->out();
+ $resp->name = $mod->name;
+ $resp->link = $mod->get_url()->out();
+ $resp->elementid = 'module-'.$mod->id;
+ $resp->commands = make_editing_buttons($mod, true, true, 0, $mod->sectionnum);
+ $resp->onclick = $mod->get_on_click();
+ $resp->visible = $mod->visible;
+
+ // if using groupings, then display grouping name
+ if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', $this->context)) {
+ $groupings = groups_get_all_groupings($this->course->id);
+ $resp->groupingname = format_string($groupings[$mod->groupingid]->name);
+ }
+
+ echo $OUTPUT->header();
+ echo json_encode($resp);
+ die();
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Edit course settings
+ *
+ * @package moodlecore
+ * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once('lib.php');
+require_once('edit_form.php');
+
+$id = optional_param('id', 0, PARAM_INT); // course id
+$categoryid = optional_param('category', 0, PARAM_INT); // course category - can be changed in edit form
+$returnto = optional_param('returnto', 0, PARAM_ALPHANUM); // generic navigation return page switch
+
+$PAGE->set_pagelayout('admin');
+$pageparams = array('id'=>$id);
+if (empty($id)) {
+ $pageparams = array('category'=>$categoryid);
+}
+$PAGE->set_url('/course/edit.php', $pageparams);
+
+// basic access control checks
+if ($id) { // editing course
+ if ($id == SITEID){
+ // don't allow editing of 'site course' using this from
+ print_error('cannoteditsiteform');
+ }
+
+ $course = course_get_format($id)->get_course();
+ require_login($course);
+ $category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
+ $coursecontext = context_course::instance($course->id);
+ require_capability('moodle/course:update', $coursecontext);
+
+} else if ($categoryid) { // creating new course in this category
+ $course = null;
+ require_login();
+ $category = $DB->get_record('course_categories', array('id'=>$categoryid), '*', MUST_EXIST);
+ $catcontext = context_coursecat::instance($category->id);
+ require_capability('moodle/course:create', $catcontext);
+ $PAGE->set_context($catcontext);
+
+} else {
+ require_login();
+ print_error('needcoursecategroyid');
+}
+
+// Prepare course and the editor
+$editoroptions = array('maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
+if (!empty($course)) {
+ //add context for editor
+ $editoroptions['context'] = $coursecontext;
+ $course = file_prepare_standard_editor($course, 'summary', $editoroptions, $coursecontext, 'course', 'summary', 0);
+
+ // Inject current aliases
+ $aliases = $DB->get_records('role_names', array('contextid'=>$coursecontext->id));
+ foreach($aliases as $alias) {
+ $course->{'role_'.$alias->roleid} = $alias->name;
+ }
+
+} else {
+ //editor should respect category context if course context is not set.
+ $editoroptions['context'] = $catcontext;
+ $course = file_prepare_standard_editor($course, 'summary', $editoroptions, null, 'course', 'summary', null);
+}
+
+// first create the form
+$editform = new course_edit_form(NULL, array('course'=>$course, 'category'=>$category, 'editoroptions'=>$editoroptions, 'returnto'=>$returnto));
+if ($editform->is_cancelled()) {
+ switch ($returnto) {
+ case 'category':
+ $url = new moodle_url($CFG->wwwroot.'/course/category.php', array('id'=>$categoryid));
+ break;
+ case 'topcat':
+ $url = new moodle_url($CFG->wwwroot.'/course/');
+ break;
+ default:
+ if (!empty($course->id)) {
+ $url = new moodle_url($CFG->wwwroot.'/course/view.php', array('id'=>$course->id));
+ } else {
+ $url = new moodle_url($CFG->wwwroot.'/course/');
+ }
+ break;
+ }
+ redirect($url);
+
+} else if ($data = $editform->get_data()) {
+ // process data if submitted
+
+ if (empty($course->id)) {
+ // In creating the course
+ $course = create_course($data, $editoroptions);
+
+ // Get the context of the newly created course
+ $context = context_course::instance($course->id, MUST_EXIST);
+
+ if (!empty($CFG->creatornewroleid) and !is_viewing($context, NULL, 'moodle/role:assign') and !is_enrolled($context, NULL, 'moodle/role:assign')) {
+ // deal with course creators - enrol them internally with default role
+ enrol_try_internal_enrol($course->id, $USER->id, $CFG->creatornewroleid);
+
+ }
+ if (!is_enrolled($context)) {
+ // Redirect to manual enrolment page if possible
+ $instances = enrol_get_instances($course->id, true);
+ foreach($instances as $instance) {
+ if ($plugin = enrol_get_plugin($instance->enrol)) {
+ if ($plugin->get_manual_enrol_link($instance)) {
+ // we know that the ajax enrol UI will have an option to enrol
+ redirect(new moodle_url('/enrol/users.php', array('id'=>$course->id)));
+ }
+ }
+ }
+ }
+ } else {
+ // Save any changes to the files used in the editor
+ update_course($data, $editoroptions);
+ }
+
+ // Redirect user to newly created/updated course.
+ redirect(new moodle_url('/course/view.php', array('id' => $course->id)));
+}
+
+
+// Print the form
+
+$site = get_site();
+
+$streditcoursesettings = get_string("editcoursesettings");
+$straddnewcourse = get_string("addnewcourse");
+$stradministration = get_string("administration");
+$strcategories = get_string("categories");
+
+if (!empty($course->id)) {
+ $PAGE->navbar->add($streditcoursesettings);
+ $title = $streditcoursesettings;
+ $fullname = $course->fullname;
+} else {
+ $PAGE->navbar->add($stradministration, new moodle_url('/admin/index.php'));
+ $PAGE->navbar->add($strcategories, new moodle_url('/course/index.php'));
+ $PAGE->navbar->add($straddnewcourse);
+ $title = "$site->shortname: $straddnewcourse";
+ $fullname = $site->fullname;
+}
+
+$PAGE->set_title($title);
+$PAGE->set_heading($fullname);
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($streditcoursesettings);
+
+$editform->display();
+
+echo $OUTPUT->footer();
+
--- /dev/null
+<?php
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir.'/formslib.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+class course_edit_form extends moodleform {
+ protected $course;
+ protected $context;
+
+ function definition() {
+ global $USER, $CFG, $DB, $PAGE;
+
+ $mform = $this->_form;
+ $PAGE->requires->yui_module('moodle-course-formatchooser', 'M.course.init_formatchooser',
+ array(array('formid' => $mform->getAttribute('id'))));
+
+ $course = $this->_customdata['course']; // this contains the data of this form
+ $category = $this->_customdata['category'];
+ $editoroptions = $this->_customdata['editoroptions'];
+ $returnto = $this->_customdata['returnto'];
+
+ $systemcontext = context_system::instance();
+ $categorycontext = context_coursecat::instance($category->id);
+
+ if (!empty($course->id)) {
+ $coursecontext = context_course::instance($course->id);
+ $context = $coursecontext;
+ } else {
+ $coursecontext = null;
+ $context = $categorycontext;
+ }
+
+ $courseconfig = get_config('moodlecourse');
+
+ $this->course = $course;
+ $this->context = $context;
+
+/// form definition with new course defaults
+//--------------------------------------------------------------------------------
+ $mform->addElement('header','general', get_string('general', 'form'));
+
+ $mform->addElement('hidden', 'returnto', null);
+ $mform->setType('returnto', PARAM_ALPHANUM);
+ $mform->setConstant('returnto', $returnto);
+
+ // verify permissions to change course category or keep current
+ if (empty($course->id)) {
+ if (has_capability('moodle/course:create', $categorycontext)) {
+ $displaylist = array();
+ $parentlist = array();
+ make_categories_list($displaylist, $parentlist, 'moodle/course:create');
+ $mform->addElement('select', 'category', get_string('category'), $displaylist);
+ $mform->addHelpButton('category', 'category');
+ $mform->setDefault('category', $category->id);
+ } else {
+ $mform->addElement('hidden', 'category', null);
+ $mform->setType('category', PARAM_INT);
+ $mform->setConstant('category', $category->id);
+ }
+ } else {
+ if (has_capability('moodle/course:changecategory', $coursecontext)) {
+ $displaylist = array();
+ $parentlist = array();
+ make_categories_list($displaylist, $parentlist, 'moodle/course:create');
+ if (!isset($displaylist[$course->category])) {
+ //always keep current
+ $displaylist[$course->category] = format_string($DB->get_field('course_categories', 'name', array('id'=>$course->category)));
+ }
+ $mform->addElement('select', 'category', get_string('category'), $displaylist);
+ $mform->addHelpButton('category', 'category');
+ } else {
+ //keep current
+ $mform->addElement('hidden', 'category', null);
+ $mform->setType('category', PARAM_INT);
+ $mform->setConstant('category', $course->category);
+ }
+ }
+
+ $mform->addElement('text','fullname', get_string('fullnamecourse'),'maxlength="254" size="50"');
+ $mform->addHelpButton('fullname', 'fullnamecourse');
+ $mform->addRule('fullname', get_string('missingfullname'), 'required', null, 'client');
+ $mform->setType('fullname', PARAM_TEXT);
+ if (!empty($course->id) and !has_capability('moodle/course:changefullname', $coursecontext)) {
+ $mform->hardFreeze('fullname');
+ $mform->setConstant('fullname', $course->fullname);
+ }
+
+ $mform->addElement('text', 'shortname', get_string('shortnamecourse'), 'maxlength="100" size="20"');
+ $mform->addHelpButton('shortname', 'shortnamecourse');
+ $mform->addRule('shortname', get_string('missingshortname'), 'required', null, 'client');
+ $mform->setType('shortname', PARAM_TEXT);
+ if (!empty($course->id) and !has_capability('moodle/course:changeshortname', $coursecontext)) {
+ $mform->hardFreeze('shortname');
+ $mform->setConstant('shortname', $course->shortname);
+ }
+
+ $mform->addElement('text','idnumber', get_string('idnumbercourse'),'maxlength="100" size="10"');
+ $mform->addHelpButton('idnumber', 'idnumbercourse');
+ $mform->setType('idnumber', PARAM_RAW);
+ if (!empty($course->id) and !has_capability('moodle/course:changeidnumber', $coursecontext)) {
+ $mform->hardFreeze('idnumber');
+ $mform->setConstants('idnumber', $course->idnumber);
+ }
+
+
+ $mform->addElement('editor','summary_editor', get_string('coursesummary'), null, $editoroptions);
+ $mform->addHelpButton('summary_editor', 'coursesummary');
+ $mform->setType('summary_editor', PARAM_RAW);
+
+ if (!empty($course->id) and !has_capability('moodle/course:changesummary', $coursecontext)) {
+ $mform->hardFreeze('summary_editor');
+ }
+
+ $courseformats = get_sorted_course_formats(true);
+ $formcourseformats = array();
+ foreach ($courseformats as $courseformat) {
+ $formcourseformats[$courseformat] = get_string('pluginname', "format_$courseformat");
+ }
+ if (isset($course->format)) {
+ $course->format = course_get_format($course)->get_format(); // replace with default if not found
+ if (!in_array($course->format, $courseformats)) {
+ // this format is disabled. Still display it in the dropdown
+ $formcourseformats[$course->format] = get_string('withdisablednote', 'moodle',
+ get_string('pluginname', 'format_'.$course->format));
+ }
+ }
+
+ $mform->addElement('select', 'format', get_string('format'), $formcourseformats);
+ $mform->addHelpButton('format', 'format');
+ $mform->setDefault('format', $courseconfig->format);
+
+ // button to update format-specific options on format change (will be hidden by JavaScript)
+ $mform->registerNoSubmitButton('updatecourseformat');
+ $mform->addElement('submit', 'updatecourseformat', get_string('courseformatudpate'));
+
+ $mform->addElement('date_selector', 'startdate', get_string('startdate'));
+ $mform->addHelpButton('startdate', 'startdate');
+ $mform->setDefault('startdate', time() + 3600 * 24);
+
+ $options = range(0, 10);
+ $mform->addElement('select', 'newsitems', get_string('newsitemsnumber'), $options);
+ $mform->addHelpButton('newsitems', 'newsitemsnumber');
+ $mform->setDefault('newsitems', $courseconfig->newsitems);
+
+ $mform->addElement('selectyesno', 'showgrades', get_string('showgrades'));
+ $mform->addHelpButton('showgrades', 'showgrades');
+ $mform->setDefault('showgrades', $courseconfig->showgrades);
+
+ $mform->addElement('selectyesno', 'showreports', get_string('showreports'));
+ $mform->addHelpButton('showreports', 'showreports');
+ $mform->setDefault('showreports', $courseconfig->showreports);
+
+ // Handle non-existing $course->maxbytes on course creation.
+ $coursemaxbytes = !isset($course->maxbytes) ? null : $course->maxbytes;
+
+ // Let's prepare the maxbytes popup.
+ $choices = get_max_upload_sizes($CFG->maxbytes, 0, 0, $coursemaxbytes);
+ $mform->addElement('select', 'maxbytes', get_string('maximumupload'), $choices);
+ $mform->addHelpButton('maxbytes', 'maximumupload');
+ $mform->setDefault('maxbytes', $courseconfig->maxbytes);
+
+ if (!empty($course->legacyfiles) or !empty($CFG->legacyfilesinnewcourses)) {
+ if (empty($course->legacyfiles)) {
+ //0 or missing means no legacy files ever used in this course - new course or nobody turned on legacy files yet
+ $choices = array('0'=>get_string('no'), '2'=>get_string('yes'));
+ } else {
+ $choices = array('1'=>get_string('no'), '2'=>get_string('yes'));
+ }
+ $mform->addElement('select', 'legacyfiles', get_string('courselegacyfiles'), $choices);
+ $mform->addHelpButton('legacyfiles', 'courselegacyfiles');
+ if (!isset($courseconfig->legacyfiles)) {
+ // in case this was not initialised properly due to switching of $CFG->legacyfilesinnewcourses
+ $courseconfig->legacyfiles = 0;
+ }
+ $mform->setDefault('legacyfiles', $courseconfig->legacyfiles);
+ }
+
+ if (!empty($CFG->allowcoursethemes)) {
+ $themeobjects = get_list_of_themes();
+ $themes=array();
+ $themes[''] = get_string('forceno');
+ foreach ($themeobjects as $key=>$theme) {
+ if (empty($theme->hidefromselector)) {
+ $themes[$key] = get_string('pluginname', 'theme_'.$theme->name);
+ }
+ }
+ $mform->addElement('select', 'theme', get_string('forcetheme'), $themes);
+ }
+
+//--------------------------------------------------------------------------------
+ $mform->addElement('hidden', 'addcourseformatoptionshere');
+
+//--------------------------------------------------------------------------------
+ enrol_course_edit_form($mform, $course, $context);
+
+//--------------------------------------------------------------------------------
+ $mform->addElement('header','', get_string('groups', 'group'));
+
+ $choices = array();
+ $choices[NOGROUPS] = get_string('groupsnone', 'group');
+ $choices[SEPARATEGROUPS] = get_string('groupsseparate', 'group');
+ $choices[VISIBLEGROUPS] = get_string('groupsvisible', 'group');
+ $mform->addElement('select', 'groupmode', get_string('groupmode', 'group'), $choices);
+ $mform->addHelpButton('groupmode', 'groupmode', 'group');
+ $mform->setDefault('groupmode', $courseconfig->groupmode);
+
+ $choices = array();
+ $choices['0'] = get_string('no');
+ $choices['1'] = get_string('yes');
+ $mform->addElement('select', 'groupmodeforce', get_string('groupmodeforce', 'group'), $choices);
+ $mform->addHelpButton('groupmodeforce', 'groupmodeforce', 'group');
+ $mform->setDefault('groupmodeforce', $courseconfig->groupmodeforce);
+
+ //default groupings selector
+ $options = array();
+ $options[0] = get_string('none');
+ $mform->addElement('select', 'defaultgroupingid', get_string('defaultgrouping', 'group'), $options);
+
+//--------------------------------------------------------------------------------
+ $mform->addElement('header','', get_string('availability'));
+
+ $choices = array();
+ $choices['0'] = get_string('courseavailablenot');
+ $choices['1'] = get_string('courseavailable');
+ $mform->addElement('select', 'visible', get_string('availability'), $choices);
+ $mform->addHelpButton('visible', 'availability');
+ $mform->setDefault('visible', $courseconfig->visible);
+ if (!has_capability('moodle/course:visibility', $context)) {
+ $mform->hardFreeze('visible');
+ if (!empty($course->id)) {
+ $mform->setConstant('visible', $course->visible);
+ } else {
+ $mform->setConstant('visible', $courseconfig->visible);
+ }
+ }
+
+//--------------------------------------------------------------------------------
+ $mform->addElement('header','', get_string('language'));
+
+ $languages=array();
+ $languages[''] = get_string('forceno');
+ $languages += get_string_manager()->get_list_of_translations();
+ $mform->addElement('select', 'lang', get_string('forcelanguage'), $languages);
+ $mform->setDefault('lang', $courseconfig->lang);
+
+//--------------------------------------------------------------------------------
+ if (completion_info::is_enabled_for_site()) {
+ $mform->addElement('header','', get_string('progress','completion'));
+ $mform->addElement('select', 'enablecompletion', get_string('completion','completion'),
+ array(0=>get_string('completiondisabled','completion'), 1=>get_string('completionenabled','completion')));
+ $mform->setDefault('enablecompletion', $courseconfig->enablecompletion);
+
+ $mform->addElement('advcheckbox', 'completionstartonenrol', get_string('completionstartonenrol', 'completion'));
+ $mform->setDefault('completionstartonenrol', $courseconfig->completionstartonenrol);
+ $mform->disabledIf('completionstartonenrol', 'enablecompletion', 'eq', 0);
+ } else {
+ $mform->addElement('hidden', 'enablecompletion');
+ $mform->setType('enablecompletion', PARAM_INT);
+ $mform->setDefault('enablecompletion',0);
+
+ $mform->addElement('hidden', 'completionstartonenrol');
+ $mform->setType('completionstartonenrol', PARAM_INT);
+ $mform->setDefault('completionstartonenrol',0);
+ }
+
+/// customizable role names in this course
+//--------------------------------------------------------------------------------
+ $mform->addElement('header','rolerenaming', get_string('rolerenaming'));
+ $mform->addHelpButton('rolerenaming', 'rolerenaming');
+
+ if ($roles = get_all_roles()) {
+ $roles = role_fix_names($roles, null, ROLENAME_ORIGINAL);
+ $assignableroles = get_roles_for_contextlevels(CONTEXT_COURSE);
+ foreach ($roles as $role) {
+ $mform->addElement('text', 'role_'.$role->id, get_string('yourwordforx', '', $role->localname));
+ $mform->setType('role_'.$role->id, PARAM_TEXT);
+ if (!in_array($role->id, $assignableroles)) {
+ $mform->setAdvanced('role_'.$role->id);
+ }
+ }
+ }
+
+//--------------------------------------------------------------------------------
+ $this->add_action_buttons();
+//--------------------------------------------------------------------------------
+ $mform->addElement('hidden', 'id', null);
+ $mform->setType('id', PARAM_INT);
+
+/// finally set the current form data
+//--------------------------------------------------------------------------------
+ $this->set_data($course);
+ }
+
+ function definition_after_data() {
+ global $DB;
+
+ $mform = $this->_form;
+
+ // add available groupings
+ if ($courseid = $mform->getElementValue('id') and $mform->elementExists('defaultgroupingid')) {
+ $options = array();
+ if ($groupings = $DB->get_records('groupings', array('courseid'=>$courseid))) {
+ foreach ($groupings as $grouping) {
+ $options[$grouping->id] = format_string($grouping->name);
+ }
+ }
+ $gr_el =& $mform->getElement('defaultgroupingid');
+ $gr_el->load($options);
+ }
+
+ // add course format options
+ $formatvalue = $mform->getElementValue('format');
+ if (is_array($formatvalue) && !empty($formatvalue)) {
+ $courseformat = course_get_format((object)array('format' => $formatvalue[0]));
+ $newel = $mform->createElement('header', '', get_string('courseformatoptions', 'moodle',
+ $courseformat->get_format_name()));
+ $mform->insertElementBefore($newel, 'addcourseformatoptionshere');
+
+ $elements = $courseformat->create_edit_form_elements($mform);
+ for ($i = 0; $i < count($elements); $i++) {
+ $mform->insertElementBefore($mform->removeElement($elements[$i]->getName(), false),
+ 'addcourseformatoptionshere');
+ }
+ }
+ }
+
+/// perform some extra moodle validation
+ function validation($data, $files) {
+ global $DB, $CFG;
+
+ $errors = parent::validation($data, $files);
+ if ($foundcourses = $DB->get_records('course', array('shortname'=>$data['shortname']))) {
+ if (!empty($data['id'])) {
+ unset($foundcourses[$data['id']]);
+ }
+ if (!empty($foundcourses)) {
+ foreach ($foundcourses as $foundcourse) {
+ $foundcoursenames[] = $foundcourse->fullname;
+ }
+ $foundcoursenamestring = implode(',', $foundcoursenames);
+ $errors['shortname']= get_string('shortnametaken', '', $foundcoursenamestring);
+ }
+ }
+
+ $errors = array_merge($errors, enrol_course_edit_validation($data, $this->context));
+
+ $courseformat = course_get_format((object)array('format' => $data['format']));
+ $formaterrors = $courseformat->edit_form_validation($data, $files, $errors);
+ if (!empty($formaterrors) && is_array($formaterrors)) {
+ $errors = array_merge($errors, $formaterrors);
+ }
+
+ return $errors;
+ }
+}
+
--- /dev/null
+<?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/>.
+
+/**
+ * Page for creating or editing course category name/parent/description.
+ * When called with an id parameter, edits the category with that id.
+ * Otherwise it creates a new category with default parent from the parent
+ * parameter, which may be 0.
+ *
+ * @package core
+ * @subpackage course
+ * @copyright 2007 Nicolas Connault
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once('lib.php');
+require_once('editcategory_form.php');
+
+require_login();
+
+$id = optional_param('id', 0, PARAM_INT);
+$itemid = 0; //initalise itemid, as all files in category description has item id 0
+
+if ($id) {
+ if (!$category = $DB->get_record('course_categories', array('id' => $id))) {
+ print_error('unknowcategory');
+ }
+ $PAGE->set_url('/course/editcategory.php', array('id' => $id));
+ $categorycontext = context_coursecat::instance($id);
+ $PAGE->set_context($categorycontext);
+ require_capability('moodle/category:manage', $categorycontext);
+ $strtitle = get_string('editcategorysettings');
+ $editorcontext = $categorycontext;
+ $title = $strtitle;
+ $fullname = $category->name;
+} else {
+ $parent = required_param('parent', PARAM_INT);
+ $PAGE->set_url('/course/editcategory.php', array('parent' => $parent));
+ if ($parent) {
+ if (!$DB->record_exists('course_categories', array('id' => $parent))) {
+ print_error('unknowcategory');
+ }
+ $context = context_coursecat::instance($parent);
+ } else {
+ $context = get_system_context();
+ }
+ $PAGE->set_context($context);
+ $category = new stdClass();
+ $category->id = 0;
+ $category->parent = $parent;
+ require_capability('moodle/category:manage', $context);
+ $strtitle = get_string("addnewcategory");
+ $editorcontext = $context;
+ $itemid = null; //set this explicitly, so files for parent category should not get loaded in draft area.
+ $title = "$SITE->shortname: ".get_string('addnewcategory');
+ $fullname = $SITE->fullname;
+}
+
+$PAGE->set_pagelayout('admin');
+
+$editoroptions = array(
+ 'maxfiles' => EDITOR_UNLIMITED_FILES,
+ 'maxbytes' => $CFG->maxbytes,
+ 'trusttext' => true,
+ 'context' => $editorcontext
+);
+$category = file_prepare_standard_editor($category, 'description', $editoroptions, $editorcontext, 'coursecat', 'description', $itemid);
+
+$mform = new editcategory_form('editcategory.php', compact('category', 'editoroptions'));
+$mform->set_data($category);
+
+if ($mform->is_cancelled()) {
+ if ($id) {
+ redirect($CFG->wwwroot . '/course/category.php?id=' . $id . '&categoryedit=on');
+ } else if ($parent) {
+ redirect($CFG->wwwroot .'/course/category.php?id=' . $parent . '&categoryedit=on');
+ } else {
+ redirect($CFG->wwwroot .'/course/index.php?categoryedit=on');
+ }
+} else if ($data = $mform->get_data()) {
+ $newcategory = new stdClass();
+ $newcategory->name = $data->name;
+ $newcategory->idnumber = $data->idnumber;
+ $newcategory->description_editor = $data->description_editor;
+ $newcategory->parent = $data->parent; // if $data->parent = 0, the new category will be a top-level category
+
+ if (isset($data->theme) && !empty($CFG->allowcategorythemes)) {
+ $newcategory->theme = $data->theme;
+ }
+
+ $logaction = 'update';
+ if ($id) {
+ // Update an existing category.
+ $newcategory->id = $category->id;
+ if ($newcategory->parent != $category->parent) {
+ // check category manage capability if parent changed
+ require_capability('moodle/category:manage', get_category_or_system_context((int)$newcategory->parent));
+ $parent_cat = $DB->get_record('course_categories', array('id' => $newcategory->parent));
+ move_category($newcategory, $parent_cat);
+ }
+ } else {
+ // Create a new category.
+ $newcategory->description = $data->description_editor['text'];
+
+ // Don't overwrite the $newcategory object as it'll be processed by file_postupdate_standard_editor in a moment
+ $category = create_course_category($newcategory);
+ $newcategory->id = $category->id;
+ $categorycontext = $category->context;
+ $logaction = 'add';
+ }
+
+ $newcategory = file_postupdate_standard_editor($newcategory, 'description', $editoroptions, $categorycontext, 'coursecat', 'description', 0);
+ $DB->update_record('course_categories', $newcategory);
+ add_to_log(SITEID, "category", $logaction, "editcategory.php?id=$newcategory->id", $newcategory->id);
+ fix_course_sortorder();
+
+ redirect('category.php?id='.$newcategory->id.'&categoryedit=on');
+}
+
+// Unfortunately the navigation never generates correctly for this page because technically this page doesn't actually
+// exist on the navigation; you get here through the course management page.
+// First up we'll try to make the course management page active seeing as that is where the user thinks they are.
+// The big prolem here is that the course management page is a common page for both editing users and common users and
+// is only added to the admin tree if the user has permission to edit at the system level.
+$node = $PAGE->settingsnav->get('root');
+if ($node) {
+ $node = $node->get('courses');
+ if ($node) {
+ $node = $node->get('coursemgmt');
+ }
+}
+if ($node) {
+ // The course management page exists so make that active.
+ $node->make_active();
+} else {
+ // Failing that we'll override the URL, not as accurate and chances are things
+ // won't be 100% correct all the time but should work most times.
+ // A common reason to arrive here is having the management capability within only a particular category (not at system level).
+ navigation_node::override_active_url(new moodle_url('/course/index.php', array('categoryedit' => 'on')));
+}
+
+$PAGE->set_title($title);
+$PAGE->set_heading($fullname);
+echo $OUTPUT->header();
+echo $OUTPUT->heading($strtitle);
+$mform->display();
+echo $OUTPUT->footer();
+
--- /dev/null
+<?php
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+
+require_once ($CFG->dirroot.'/course/moodleform_mod.php');
+class editcategory_form extends moodleform {
+
+ // form definition
+ function definition() {
+ global $CFG, $DB;
+ $mform =& $this->_form;
+ $category = $this->_customdata['category'];
+ $editoroptions = $this->_customdata['editoroptions'];
+
+ // get list of categories to use as parents, with site as the first one
+ $options = array();
+ if (has_capability('moodle/category:manage', get_system_context()) || $category->parent == 0) {
+ $options[0] = get_string('top');
+ }
+ $parents = array();
+ if ($category->id) {
+ // Editing an existing category.
+ make_categories_list($options, $parents, 'moodle/category:manage', $category->id);
+ if (empty($options[$category->parent])) {
+ $options[$category->parent] = $DB->get_field('course_categories', 'name', array('id'=>$category->parent));
+ }
+ $strsubmit = get_string('savechanges');
+ } else {
+ // Making a new category
+ make_categories_list($options, $parents, 'moodle/category:manage');
+ $strsubmit = get_string('createcategory');
+ }
+
+ $mform->addElement('select', 'parent', get_string('parentcategory'), $options);
+ $mform->addElement('text', 'name', get_string('categoryname'), array('size'=>'30'));
+ $mform->addRule('name', get_string('required'), 'required', null);
+ $mform->addElement('text', 'idnumber', get_string('idnumbercoursecategory'),'maxlength="100" size="10"');
+ $mform->addHelpButton('idnumber', 'idnumbercoursecategory');
+ $mform->addElement('editor', 'description_editor', get_string('description'), null, $editoroptions);
+ $mform->setType('description_editor', PARAM_RAW);
+ if (!empty($CFG->allowcategorythemes)) {
+ $themes = array(''=>get_string('forceno'));
+ $allthemes = get_list_of_themes();
+ foreach ($allthemes as $key=>$theme) {
+ if (empty($theme->hidefromselector)) {
+ $themes[$key] = get_string('pluginname', 'theme_'.$theme->name);
+ }
+ }
+ $mform->addElement('select', 'theme', get_string('forcetheme'), $themes);
+ }
+
+ $mform->addElement('hidden', 'id', 0);
+ $mform->setType('id', PARAM_INT);
+ $mform->setDefault('id', $category->id);
+
+ $this->add_action_buttons(true, $strsubmit);
+ }
+
+ function validation($data, $files) {
+ global $DB;
+ $errors = parent::validation($data, $files);
+ if (!empty($data['idnumber'])) {
+ if ($existing = $DB->get_record('course_categories', array('idnumber' => $data['idnumber']))) {
+ if (!$data['id'] || $existing->id != $data['id']) {
+ $errors['idnumber']= get_string('idnumbertaken');
+ }
+ }
+ }
+
+ return $errors;
+ }
+}
+
--- /dev/null
+<?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/>.
+
+/**
+ * Edit the section basic information and availability
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require_once("../config.php");
+require_once("lib.php");
+require_once($CFG->libdir . '/conditionlib.php');
+
+$id = required_param('id', PARAM_INT); // course_sections.id
+$sectionreturn = optional_param('sr', 0, PARAM_INT);
+
+$PAGE->set_url('/course/editsection.php', array('id'=>$id, 'sr'=> $sectionreturn));
+
+$section = $DB->get_record('course_sections', array('id' => $id), '*', MUST_EXIST);
+$course = $DB->get_record('course', array('id' => $section->course), '*', MUST_EXIST);
+$sectionnum = $section->section;
+
+require_login($course);
+$context = context_course::instance($course->id);
+require_capability('moodle/course:update', $context);
+
+// get section_info object with all availability options
+$sectioninfo = get_fast_modinfo($course)->get_section_info($sectionnum);
+
+$editoroptions = array('context'=>$context ,'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
+$mform = course_get_format($course->id)->editsection_form($PAGE->url,
+ array('cs' => $sectioninfo, 'editoroptions' => $editoroptions));
+// set current value, make an editable copy of section_info object
+// this will retrieve all format-specific options as well
+$mform->set_data(convert_to_array($sectioninfo));
+
+if ($mform->is_cancelled()){
+ // form cancelled, return to course
+ redirect(course_get_url($course, $section, array('sr' => $sectionreturn)));
+} else if ($data = $mform->get_data()) {
+ // data submitted and validated, update and return to course
+ $DB->update_record('course_sections', $data);
+ rebuild_course_cache($course->id, true);
+ if (isset($data->section)) {
+ // usually edit form does not change relative section number but just in case
+ $sectionnum = $data->section;
+ }
+ if (!empty($CFG->enableavailability)) {
+ // Update grade and completion conditions
+ $sectioninfo = get_fast_modinfo($course)->get_section_info($sectionnum);
+ condition_info_section::update_section_from_form($sectioninfo, $data);
+ rebuild_course_cache($course->id, true);
+ }
+ course_get_format($course->id)->update_section_format_options($data);
+
+ add_to_log($course->id, "course", "editsection", "editsection.php?id=$id", "$sectionnum");
+ $PAGE->navigation->clear_cache();
+ redirect(course_get_url($course, $section, array('sr' => $sectionreturn)));
+}
+
+// the edit form is displayed for the first time or there was a validation
+// error on the previous step. Display the edit form:
+$sectionname = get_section_name($course, $sectionnum);
+$stredit = get_string('edita', '', " $sectionname");
+$strsummaryof = get_string('summaryof', '', " $sectionname");
+
+$PAGE->set_title($stredit);
+$PAGE->set_heading($course->fullname);
+$PAGE->navbar->add($stredit);
+echo $OUTPUT->header();
+
+echo $OUTPUT->heading($strsummaryof);
+
+$mform->display();
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+
+require_once($CFG->libdir.'/formslib.php');
+require_once($CFG->libdir.'/filelib.php');
+require_once($CFG->libdir.'/completionlib.php');
+require_once($CFG->libdir.'/gradelib.php');
+
+/**
+ * Default form for editing course section
+ *
+ * Course format plugins may specify different editing form to use
+ */
+class editsection_form extends moodleform {
+
+ function definition() {
+
+ $mform = $this->_form;
+ $course = $this->_customdata['course'];
+ $mform->addElement('checkbox', 'usedefaultname', get_string('sectionusedefaultname'));
+ $mform->setDefault('usedefaultname', true);
+
+ $mform->addElement('text', 'name', get_string('sectionname'), array('size'=>'30'));
+ $mform->setType('name', PARAM_TEXT);
+ $mform->disabledIf('name','usedefaultname','checked');
+
+ /// Prepare course and the editor
+
+ $mform->addElement('editor', 'summary_editor', get_string('summary'), null, $this->_customdata['editoroptions']);
+ $mform->addHelpButton('summary_editor', 'summary');
+ $mform->setType('summary_editor', PARAM_RAW);
+
+ $mform->addElement('hidden', 'id');
+ $mform->setType('id', PARAM_INT);
+
+ // additional fields that course format has defined
+ $courseformat = course_get_format($course);
+ $formatoptions = $courseformat->section_format_options(true);
+ if (!empty($formatoptions)) {
+ $elements = $courseformat->create_edit_form_elements($mform, true);
+ }
+
+ $mform->_registerCancelButton('cancel');
+ }
+
+ public function definition_after_data() {
+ global $CFG, $DB;
+
+ $mform = $this->_form;
+ $course = $this->_customdata['course'];
+
+ if (!empty($CFG->enableavailability)) {
+ $mform->addElement('header', '', get_string('availabilityconditions', 'condition'));
+ // String used by conditions more than once
+ $strcondnone = get_string('none', 'condition');
+ // Grouping conditions - only if grouping is enabled at site level
+ if (!empty($CFG->enablegroupmembersonly)) {
+ $options = array();
+ $options[0] = get_string('none');
+ if ($groupings = $DB->get_records('groupings', array('courseid' => $course->id))) {
+ foreach ($groupings as $grouping) {
+ $context = context_course::instance($course->id);
+ $options[$grouping->id] = format_string(
+ $grouping->name, true, array('context' => $context));
+ }
+ }
+ $mform->addElement('select', 'groupingid', get_string('groupingsection', 'group'), $options);
+ $mform->addHelpButton('groupingid', 'groupingsection', 'group');
+ }
+
+ // Available from/to defaults to midnight because then the display
+ // will be nicer where it tells users when they can access it (it
+ // shows only the date and not time).
+ $date = usergetdate(time());
+ $midnight = make_timestamp($date['year'], $date['mon'], $date['mday']);
+
+ // Date and time conditions.
+ $mform->addElement('date_time_selector', 'availablefrom',
+ get_string('availablefrom', 'condition'),
+ array('optional' => true, 'defaulttime' => $midnight));
+ $mform->addElement('date_time_selector', 'availableuntil',
+ get_string('availableuntil', 'condition'),
+ array('optional' => true, 'defaulttime' => $midnight));
+
+ // Conditions based on grades
+ $gradeoptions = array();
+ $items = grade_item::fetch_all(array('courseid' => $course->id));
+ $items = $items ? $items : array();
+ foreach ($items as $id => $item) {
+ $gradeoptions[$id] = $item->get_name();
+ }
+ asort($gradeoptions);
+ $gradeoptions = array(0 => $strcondnone) + $gradeoptions;
+
+ $grouparray = array();
+ $grouparray[] = $mform->createElement('select', 'conditiongradeitemid', '', $gradeoptions);
+ $grouparray[] = $mform->createElement('static', '', '',
+ ' ' . get_string('grade_atleast', 'condition').' ');
+ $grouparray[] = $mform->createElement('text', 'conditiongrademin', '', array('size' => 3));
+ $grouparray[] = $mform->createElement('static', '', '',
+ '% ' . get_string('grade_upto', 'condition') . ' ');
+ $grouparray[] = $mform->createElement('text', 'conditiongrademax', '', array('size' => 3));
+ $grouparray[] = $mform->createElement('static', '', '', '%');
+ $group = $mform->createElement('group', 'conditiongradegroup',
+ get_string('gradecondition', 'condition'), $grouparray);
+
+ // Get full version (including condition info) of section object
+ $ci = new condition_info_section($this->_customdata['cs']);
+ $fullcs = $ci->get_full_section();
+ $count = count($fullcs->conditionsgrade) + 1;
+
+ // Grade conditions
+ $this->repeat_elements(array($group), $count, array(), 'conditiongraderepeats',
+ 'conditiongradeadds', 2, get_string('addgrades', 'condition'), true);
+ $mform->addHelpButton('conditiongradegroup[0]', 'gradecondition', 'condition');
+
+ // Conditions based on user fields
+ $operators = condition_info::get_condition_user_field_operators();
+ $useroptions = condition_info::get_condition_user_fields();
+ asort($useroptions);
+
+ $useroptions = array(0 => $strcondnone) + $useroptions;
+ $grouparray = array();
+ $grouparray[] =& $mform->createElement('select', 'conditionfield', '', $useroptions);
+ $grouparray[] =& $mform->createElement('select', 'conditionfieldoperator', '', $operators);
+ $grouparray[] =& $mform->createElement('text', 'conditionfieldvalue');
+ $mform->setType('conditionfieldvalue', PARAM_RAW);
+ $group = $mform->createElement('group', 'conditionfieldgroup', get_string('userfield', 'condition'), $grouparray);
+
+ $fieldcount = count($fullcs->conditionsfield) + 1;
+
+ $this->repeat_elements(array($group), $fieldcount, array(), 'conditionfieldrepeats', 'conditionfieldadds', 2,
+ get_string('adduserfields', 'condition'), true);
+ $mform->addHelpButton('conditionfieldgroup[0]', 'userfield', 'condition');
+
+ // Conditions based on completion
+ $completion = new completion_info($course);
+ if ($completion->is_enabled()) {
+ $completionoptions = array();
+ $modinfo = get_fast_modinfo($course);
+ foreach ($modinfo->cms as $id => $cm) {
+ // Add each course-module if it:
+ // (a) has completion turned on
+ // (b) does not belong to current course-section
+ if ($cm->completion && ($fullcs->id != $cm->section)) {
+ $completionoptions[$id] = $cm->name;
+ }
+ }
+ asort($completionoptions);
+ $completionoptions = array(0 => $strcondnone) +
+ $completionoptions;
+
+ $completionvalues = array(
+ COMPLETION_COMPLETE => get_string('completion_complete', 'condition'),
+ COMPLETION_INCOMPLETE => get_string('completion_incomplete', 'condition'),
+ COMPLETION_COMPLETE_PASS => get_string('completion_pass', 'condition'),
+ COMPLETION_COMPLETE_FAIL => get_string('completion_fail', 'condition'));
+
+ $grouparray = array();
+ $grouparray[] = $mform->createElement('select', 'conditionsourcecmid', '',
+ $completionoptions);
+ $grouparray[] = $mform->createElement('select', 'conditionrequiredcompletion', '',
+ $completionvalues);
+ $group = $mform->createElement('group', 'conditioncompletiongroup',
+ get_string('completioncondition', 'condition'), $grouparray);
+
+ $count = count($fullcs->conditionscompletion) + 1;
+ $this->repeat_elements(array($group), $count, array(),
+ 'conditioncompletionrepeats', 'conditioncompletionadds', 2,
+ get_string('addcompletions', 'condition'), true);
+ $mform->addHelpButton('conditioncompletiongroup[0]',
+ 'completionconditionsection', 'condition');
+ }
+
+ // Availability conditions - set up form values
+ if (!empty($CFG->enableavailability)) {
+ $num = 0;
+ foreach ($fullcs->conditionsgrade as $gradeitemid => $minmax) {
+ $groupelements = $mform->getElement(
+ 'conditiongradegroup[' . $num . ']')->getElements();
+ $groupelements[0]->setValue($gradeitemid);
+ $groupelements[2]->setValue(is_null($minmax->min) ? '' :
+ format_float($minmax->min, 5, true, true));
+ $groupelements[4]->setValue(is_null($minmax->max) ? '' :
+ format_float($minmax->max, 5, true, true));
+ $num++;
+ }
+
+ $num = 0;
+ foreach ($fullcs->conditionsfield as $fieldid => $data) {
+ $groupelements = $mform->getElement(
+ 'conditionfieldgroup[' . $num . ']')->getElements();
+ $groupelements[0]->setValue($fieldid);
+ $groupelements[1]->setValue(is_null($data->operator) ? '' :
+ $data->operator);
+ $groupelements[2]->setValue(is_null($data->value) ? '' :
+ $data->value);
+ $num++;
+ }
+
+ if ($completion->is_enabled()) {
+ $num = 0;
+ foreach ($fullcs->conditionscompletion as $othercmid => $state) {
+ $groupelements = $mform->getElement('conditioncompletiongroup[' . $num . ']')->getElements();
+ $groupelements[0]->setValue($othercmid);
+ $groupelements[1]->setValue($state);
+ $num++;
+ }
+ }
+ }
+
+ // Do we display availability info to students?
+ $showhide = array(
+ CONDITION_STUDENTVIEW_SHOW => get_string('showavailabilitysection_show', 'condition'),
+ CONDITION_STUDENTVIEW_HIDE => get_string('showavailabilitysection_hide', 'condition'));
+ $mform->addElement('select', 'showavailability',
+ get_string('showavailabilitysection', 'condition'), $showhide);
+ }
+
+ $this->add_action_buttons();
+ }
+
+ public function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+ // Conditions: Don't let them set dates which make no sense
+ if (array_key_exists('availablefrom', $data) &&
+ $data['availablefrom'] && $data['availableuntil'] &&
+ $data['availablefrom'] >= $data['availableuntil']) {
+ $errors['availablefrom'] = get_string('badavailabledates', 'condition');
+ }
+
+ // Conditions: Verify that the grade conditions are numbers, and make sense.
+ if (array_key_exists('conditiongradegroup', $data)) {
+ foreach ($data['conditiongradegroup'] as $i => $gradedata) {
+ if ($gradedata['conditiongrademin'] !== '' &&
+ !is_numeric(unformat_float($gradedata['conditiongrademin']))) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradesmustbenumeric', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademax'] !== '' &&
+ !is_numeric(unformat_float($gradedata['conditiongrademax']))) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradesmustbenumeric', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademin'] !== '' && $gradedata['conditiongrademax'] !== '' &&
+ unformat_float($gradedata['conditiongrademax']) <= unformat_float($gradedata['conditiongrademin'])) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('badgradelimits', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademin'] === '' && $gradedata['conditiongrademax'] === '' &&
+ $gradedata['conditiongradeitemid']) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradeitembutnolimits', 'condition');
+ continue;
+ }
+ if (($gradedata['conditiongrademin'] !== '' || $gradedata['conditiongrademax'] !== '') &&
+ !$gradedata['conditiongradeitemid']) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradelimitsbutnoitem', 'condition');
+ continue;
+ }
+ }
+ }
+
+ // Conditions: Verify that the user profile field has not been declared more than once
+ if (array_key_exists('conditionfieldgroup', $data)) {
+ // Array to store the existing fields
+ $arrcurrentfields = array();
+ // Error message displayed if any condition is declared more than once. We use lang string because
+ // this way we don't actually generate the string unless there is an error.
+ $stralreadydeclaredwarning = new lang_string('fielddeclaredmultipletimes', 'condition');
+ foreach ($data['conditionfieldgroup'] as $i => $fielddata) {
+ if ($fielddata['conditionfield'] == 0) { // Don't need to bother if none is selected
+ continue;
+ }
+ if (in_array($fielddata['conditionfield'], $arrcurrentfields)) {
+ $errors["conditionfieldgroup[{$i}]"] = $stralreadydeclaredwarning->out();
+ }
+ // Add the field to the array
+ $arrcurrentfields[] = $fielddata['conditionfield'];
+ }
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Load in existing data as form defaults
+ *
+ * @param stdClass|array $default_values object or array of default values
+ */
+ function set_data($default_values) {
+ if (!is_object($default_values)) {
+ // we need object for file_prepare_standard_editor
+ $default_values = (object)$default_values;
+ }
+ $editoroptions = $this->_customdata['editoroptions'];
+ $default_values = file_prepare_standard_editor($default_values, 'summary', $editoroptions,
+ $editoroptions['context'], 'course', 'section', $default_values->id);
+ $default_values->usedefaultname = (is_null($default_values->name));
+ parent::set_data($default_values);
+ }
+
+ /**
+ * Return submitted data if properly submitted or returns NULL if validation fails or
+ * if there is no submitted data.
+ *
+ * @return object submitted data; NULL if not valid or not submitted or cancelled
+ */
+ function get_data() {
+ $data = parent::get_data();
+ if ($data !== null) {
+ $editoroptions = $this->_customdata['editoroptions'];
+ if (!empty($data->usedefaultname)) {
+ $data->name = null;
+ }
+ $data = file_postupdate_standard_editor($data, 'summary', $editoroptions,
+ $editoroptions['context'], 'course', 'section', $data->id);
+ $course = $this->_customdata['course'];
+ foreach (course_get_format($course)->section_format_options() as $option => $unused) {
+ // fix issue with unset checkboxes not being returned at all
+ if (!isset($data->$option)) {
+ $data->$option = null;
+ }
+ }
+ }
+ return $data;
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Redirection of old enrol entry point.
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require('../config.php');
+
+$id = required_param('id', PARAM_INT);
+
+redirect(new moodle_url('/enrol/index.php', array('id'=>$id)));
--- /dev/null
+<?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/>.
+
+
+/**
+ * External course API
+ *
+ * @package core_course
+ * @category external
+ * @copyright 2009 Petr Skodak
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/externallib.php");
+
+/**
+ * Course external functions
+ *
+ * @package core_course
+ * @category external
+ * @copyright 2011 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.2
+ */
+class core_course_external extends external_api {
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.2
+ */
+ public static function get_course_contents_parameters() {
+ return new external_function_parameters(
+ array('courseid' => new external_value(PARAM_INT, 'course id'),
+ 'options' => new external_multiple_structure (
+ new external_single_structure(
+ array('name' => new external_value(PARAM_ALPHANUM, 'option name'),
+ 'value' => new external_value(PARAM_RAW, 'the value of the option, this param is personaly validated in the external function.')
+ )
+ ), 'Options, not used yet, might be used in later version', VALUE_DEFAULT, array())
+ )
+ );
+ }
+
+ /**
+ * Get course contents
+ *
+ * @param int $courseid course id
+ * @param array $options These options are not used yet, might be used in later version
+ * @return array
+ * @since Moodle 2.2
+ */
+ public static function get_course_contents($courseid, $options = array()) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot . "/course/lib.php");
+
+ //validate parameter
+ $params = self::validate_parameters(self::get_course_contents_parameters(),
+ array('courseid' => $courseid, 'options' => $options));
+
+ //retrieve the course
+ $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
+
+ //check course format exist
+ if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
+ throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null, get_string('courseformatnotfound', 'error', '', $course->format));
+ } else {
+ require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
+ }
+
+ // now security checks
+ $context = context_course::instance($course->id, IGNORE_MISSING);
+ try {
+ self::validate_context($context);
+ } catch (Exception $e) {
+ $exceptionparam = new stdClass();
+ $exceptionparam->message = $e->getMessage();
+ $exceptionparam->courseid = $course->id;
+ throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
+ }
+
+ $canupdatecourse = has_capability('moodle/course:update', $context);
+
+ //create return value
+ $coursecontents = array();
+
+ if ($canupdatecourse or $course->visible
+ or has_capability('moodle/course:viewhiddencourses', $context)) {
+
+ //retrieve sections
+ $modinfo = get_fast_modinfo($course);
+ $sections = $modinfo->get_section_info_all();
+
+ //for each sections (first displayed to last displayed)
+ foreach ($sections as $key => $section) {
+
+ if (!$section->uservisible) {
+ continue;
+ }
+
+ // reset $sectioncontents
+ $sectionvalues = array();
+ $sectionvalues['id'] = $section->id;
+ $sectionvalues['name'] = get_section_name($course, $section);
+ $sectionvalues['visible'] = $section->visible;
+ list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
+ external_format_text($section->summary, $section->summaryformat,
+ $context->id, 'course', 'section', $section->id);
+ $sectioncontents = array();
+
+ //for each module of the section
+ foreach ($modinfo->sections[$section->section] as $cmid) { //matching /course/lib.php:print_section() logic
+ $cm = $modinfo->cms[$cmid];
+
+ // stop here if the module is not visible to the user
+ if (!$cm->uservisible) {
+ continue;
+ }
+
+ $module = array();
+
+ //common info (for people being able to see the module or availability dates)
+ $module['id'] = $cm->id;
+ $module['name'] = format_string($cm->name, true);
+ $module['modname'] = $cm->modname;
+ $module['modplural'] = $cm->modplural;
+ $module['modicon'] = $cm->get_icon_url()->out(false);
+ $module['indent'] = $cm->indent;
+
+ $modcontext = context_module::instance($cm->id);
+
+ if (!empty($cm->showdescription)) {
+ $module['description'] = $cm->get_content();
+ }
+
+ //url of the module
+ $url = $cm->get_url();
+ if ($url) { //labels don't have url
+ $module['url'] = $cm->get_url()->out();
+ }
+
+ $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
+ context_module::instance($cm->id));
+ //user that can view hidden module should know about the visibility
+ $module['visible'] = $cm->visible;
+
+ //availability date (also send to user who can see hidden module when the showavailabilyt is ON)
+ if ($canupdatecourse or ($CFG->enableavailability && $canviewhidden && $cm->showavailability)) {
+ $module['availablefrom'] = $cm->availablefrom;
+ $module['availableuntil'] = $cm->availableuntil;
+ }
+
+ $baseurl = 'webservice/pluginfile.php';
+
+ //call $modulename_export_contents
+ //(each module callback take care about checking the capabilities)
+ require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
+ $getcontentfunction = $cm->modname.'_export_contents';
+ if (function_exists($getcontentfunction)) {
+ if ($contents = $getcontentfunction($cm, $baseurl)) {
+ $module['contents'] = $contents;
+ }
+ }
+
+ //assign result to $sectioncontents
+ $sectioncontents[] = $module;
+
+ }
+ $sectionvalues['modules'] = $sectioncontents;
+
+ // assign result to $coursecontents
+ $coursecontents[] = $sectionvalues;
+ }
+ }
+ return $coursecontents;
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.2
+ */
+ public static function get_course_contents_returns() {
+ return new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'Section ID'),
+ 'name' => new external_value(PARAM_TEXT, 'Section name'),
+ 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
+ 'summary' => new external_value(PARAM_RAW, 'Section description'),
+ 'summaryformat' => new external_format_value('summary'),
+ 'modules' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'activity id'),
+ 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
+ 'name' => new external_value(PARAM_RAW, 'activity module name'),
+ 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
+ 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
+ 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
+ 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
+ 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
+ 'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
+ 'availableuntil' => new external_value(PARAM_INT, 'module availability en date', VALUE_OPTIONAL),
+ 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
+ 'contents' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ // content info
+ 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
+ 'filename'=> new external_value(PARAM_FILE, 'filename'),
+ 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
+ 'filesize'=> new external_value(PARAM_INT, 'filesize'),
+ 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
+ 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
+ 'timecreated' => new external_value(PARAM_INT, 'Time created'),
+ 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
+ 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
+
+ // copyright related info
+ 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
+ 'author' => new external_value(PARAM_TEXT, 'Content owner'),
+ 'license' => new external_value(PARAM_TEXT, 'Content license'),
+ )
+ ), VALUE_DEFAULT, array()
+ )
+ )
+ ), 'list of module'
+ )
+ )
+ )
+ );
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function get_courses_parameters() {
+ return new external_function_parameters(
+ array('options' => new external_single_structure(
+ array('ids' => new external_multiple_structure(
+ new external_value(PARAM_INT, 'Course id')
+ , 'List of course id. If empty return all courses
+ except front page course.',
+ VALUE_OPTIONAL)
+ ), 'options - operator OR is used', VALUE_DEFAULT, array())
+ )
+ );
+ }
+
+ /**
+ * Get courses
+ *
+ * @param array $options It contains an array (list of ids)
+ * @return array
+ * @since Moodle 2.2
+ */
+ public static function get_courses($options = array()) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot . "/course/lib.php");
+
+ //validate parameter
+ $params = self::validate_parameters(self::get_courses_parameters(),
+ array('options' => $options));
+
+ //retrieve courses
+ if (!array_key_exists('ids', $params['options'])
+ or empty($params['options']['ids'])) {
+ $courses = $DB->get_records('course');
+ } else {
+ $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
+ }
+
+ //create return value
+ $coursesinfo = array();
+ foreach ($courses as $course) {
+
+ // now security checks
+ $context = context_course::instance($course->id, IGNORE_MISSING);
+ $courseformatoptions = course_get_format($course)->get_format_options();
+ try {
+ self::validate_context($context);
+ } catch (Exception $e) {
+ $exceptionparam = new stdClass();
+ $exceptionparam->message = $e->getMessage();
+ $exceptionparam->courseid = $course->id;
+ throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
+ }
+ require_capability('moodle/course:view', $context);
+
+ $courseinfo = array();
+ $courseinfo['id'] = $course->id;
+ $courseinfo['fullname'] = $course->fullname;
+ $courseinfo['shortname'] = $course->shortname;
+ $courseinfo['categoryid'] = $course->category;
+ list($courseinfo['summary'], $courseinfo['summaryformat']) =
+ external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
+ $courseinfo['format'] = $course->format;
+ $courseinfo['startdate'] = $course->startdate;
+ if (array_key_exists('numsections', $courseformatoptions)) {
+ // For backward-compartibility
+ $courseinfo['numsections'] = $courseformatoptions['numsections'];
+ }
+
+ //some field should be returned only if the user has update permission
+ $courseadmin = has_capability('moodle/course:update', $context);
+ if ($courseadmin) {
+ $courseinfo['categorysortorder'] = $course->sortorder;
+ $courseinfo['idnumber'] = $course->idnumber;
+ $courseinfo['showgrades'] = $course->showgrades;
+ $courseinfo['showreports'] = $course->showreports;
+ $courseinfo['newsitems'] = $course->newsitems;
+ $courseinfo['visible'] = $course->visible;
+ $courseinfo['maxbytes'] = $course->maxbytes;
+ if (array_key_exists('hiddensections', $courseformatoptions)) {
+ // For backward-compartibility
+ $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
+ }
+ $courseinfo['groupmode'] = $course->groupmode;
+ $courseinfo['groupmodeforce'] = $course->groupmodeforce;
+ $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
+ $courseinfo['lang'] = $course->lang;
+ $courseinfo['timecreated'] = $course->timecreated;
+ $courseinfo['timemodified'] = $course->timemodified;
+ $courseinfo['forcetheme'] = $course->theme;
+ $courseinfo['enablecompletion'] = $course->enablecompletion;
+ $courseinfo['completionstartonenrol'] = $course->completionstartonenrol;
+ $courseinfo['completionnotify'] = $course->completionnotify;
+ $courseinfo['courseformatoptions'] = array();
+ foreach ($courseformatoptions as $key => $value) {
+ $courseinfo['courseformatoptions'][] = array(
+ 'name' => $key,
+ 'value' => $value
+ );
+ }
+ }
+
+ if ($courseadmin or $course->visible
+ or has_capability('moodle/course:viewhiddencourses', $context)) {
+ $coursesinfo[] = $courseinfo;
+ }
+ }
+
+ return $coursesinfo;
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.2
+ */
+ public static function get_courses_returns() {
+ return new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'course id'),
+ 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
+ 'categoryid' => new external_value(PARAM_INT, 'category id'),
+ 'categorysortorder' => new external_value(PARAM_INT,
+ 'sort order into the category', VALUE_OPTIONAL),
+ 'fullname' => new external_value(PARAM_TEXT, 'full name'),
+ 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
+ 'summary' => new external_value(PARAM_RAW, 'summary'),
+ 'summaryformat' => new external_format_value('summary'),
+ 'format' => new external_value(PARAM_PLUGIN,
+ 'course format: weeks, topics, social, site,..'),
+ 'showgrades' => new external_value(PARAM_INT,
+ '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
+ 'newsitems' => new external_value(PARAM_INT,
+ 'number of recent items appearing on the course page', VALUE_OPTIONAL),
+ 'startdate' => new external_value(PARAM_INT,
+ 'timestamp when the course start'),
+ 'numsections' => new external_value(PARAM_INT,
+ '(deprecated, use courseformatoptions) number of weeks/topics',
+ VALUE_OPTIONAL),
+ 'maxbytes' => new external_value(PARAM_INT,
+ 'largest size of file that can be uploaded into the course',
+ VALUE_OPTIONAL),
+ 'showreports' => new external_value(PARAM_INT,
+ 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
+ 'visible' => new external_value(PARAM_INT,
+ '1: available to student, 0:not available', VALUE_OPTIONAL),
+ 'hiddensections' => new external_value(PARAM_INT,
+ '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
+ VALUE_OPTIONAL),
+ 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
+ VALUE_OPTIONAL),
+ 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
+ VALUE_OPTIONAL),
+ 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
+ VALUE_OPTIONAL),
+ 'timecreated' => new external_value(PARAM_INT,
+ 'timestamp when the course have been created', VALUE_OPTIONAL),
+ 'timemodified' => new external_value(PARAM_INT,
+ 'timestamp when the course have been modified', VALUE_OPTIONAL),
+ 'enablecompletion' => new external_value(PARAM_INT,
+ 'Enabled, control via completion and activity settings. Disbaled,
+ not shown in activity settings.',
+ VALUE_OPTIONAL),
+ 'completionstartonenrol' => new external_value(PARAM_INT,
+ '1: begin tracking a student\'s progress in course completion
+ after course enrolment. 0: does not',
+ VALUE_OPTIONAL),
+ 'completionnotify' => new external_value(PARAM_INT,
+ '1: yes 0: no', VALUE_OPTIONAL),
+ 'lang' => new external_value(PARAM_SAFEDIR,
+ 'forced course language', VALUE_OPTIONAL),
+ 'forcetheme' => new external_value(PARAM_PLUGIN,
+ 'name of the force theme', VALUE_OPTIONAL),
+ 'courseformatoptions' => new external_multiple_structure(
+ new external_single_structure(
+ array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
+ 'value' => new external_value(PARAM_RAW, 'course format option value')
+ )),
+ 'additional options for particular course format', VALUE_OPTIONAL
+ ),
+ ), 'course'
+ )
+ );
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.2
+ */
+ public static function create_courses_parameters() {
+ $courseconfig = get_config('moodlecourse'); //needed for many default values
+ return new external_function_parameters(
+ array(
+ 'courses' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'fullname' => new external_value(PARAM_TEXT, 'full name'),
+ 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
+ 'categoryid' => new external_value(PARAM_INT, 'category id'),
+ 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
+ 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
+ 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
+ 'format' => new external_value(PARAM_PLUGIN,
+ 'course format: weeks, topics, social, site,..',
+ VALUE_DEFAULT, $courseconfig->format),
+ 'showgrades' => new external_value(PARAM_INT,
+ '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
+ $courseconfig->showgrades),
+ 'newsitems' => new external_value(PARAM_INT,
+ 'number of recent items appearing on the course page',
+ VALUE_DEFAULT, $courseconfig->newsitems),
+ 'startdate' => new external_value(PARAM_INT,
+ 'timestamp when the course start', VALUE_OPTIONAL),
+ 'numsections' => new external_value(PARAM_INT,
+ '(deprecated, use courseformatoptions) number of weeks/topics',
+ VALUE_OPTIONAL),
+ 'maxbytes' => new external_value(PARAM_INT,
+ 'largest size of file that can be uploaded into the course',
+ VALUE_DEFAULT, $courseconfig->maxbytes),
+ 'showreports' => new external_value(PARAM_INT,
+ 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
+ $courseconfig->showreports),
+ 'visible' => new external_value(PARAM_INT,
+ '1: available to student, 0:not available', VALUE_OPTIONAL),
+ 'hiddensections' => new external_value(PARAM_INT,
+ '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
+ VALUE_OPTIONAL),
+ 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
+ VALUE_DEFAULT, $courseconfig->groupmode),
+ 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
+ VALUE_DEFAULT, $courseconfig->groupmodeforce),
+ 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
+ VALUE_DEFAULT, 0),
+ 'enablecompletion' => new external_value(PARAM_INT,
+ 'Enabled, control via completion and activity settings. Disabled,
+ not shown in activity settings.',
+ VALUE_OPTIONAL),
+ 'completionstartonenrol' => new external_value(PARAM_INT,
+ '1: begin tracking a student\'s progress in course completion after
+ course enrolment. 0: does not',
+ VALUE_OPTIONAL),
+ 'completionnotify' => new external_value(PARAM_INT,
+ '1: yes 0: no', VALUE_OPTIONAL),
+ 'lang' => new external_value(PARAM_SAFEDIR,
+ 'forced course language', VALUE_OPTIONAL),
+ 'forcetheme' => new external_value(PARAM_PLUGIN,
+ 'name of the force theme', VALUE_OPTIONAL),
+ 'courseformatoptions' => new external_multiple_structure(
+ new external_single_structure(
+ array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
+ 'value' => new external_value(PARAM_RAW, 'course format option value')
+ )),
+ 'additional options for particular course format', VALUE_OPTIONAL),
+ )
+ ), 'courses to create'
+ )
+ )
+ );
+ }
+
+ /**
+ * Create courses
+ *
+ * @param array $courses
+ * @return array courses (id and shortname only)
+ * @since Moodle 2.2
+ */
+ public static function create_courses($courses) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot . "/course/lib.php");
+ require_once($CFG->libdir . '/completionlib.php');
+
+ $params = self::validate_parameters(self::create_courses_parameters(),
+ array('courses' => $courses));
+
+ $availablethemes = get_plugin_list('theme');
+ $availablelangs = get_string_manager()->get_list_of_translations();
+
+ $transaction = $DB->start_delegated_transaction();
+
+ foreach ($params['courses'] as $course) {
+
+ // Ensure the current user is allowed to run this function
+ $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
+ try {
+ self::validate_context($context);
+ } catch (Exception $e) {
+ $exceptionparam = new stdClass();
+ $exceptionparam->message = $e->getMessage();
+ $exceptionparam->catid = $course['categoryid'];
+ throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
+ }
+ require_capability('moodle/course:create', $context);
+
+ // Make sure lang is valid
+ if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
+ throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
+ }
+
+ // Make sure theme is valid
+ if (array_key_exists('forcetheme', $course)) {
+ if (!empty($CFG->allowcoursethemes)) {
+ if (empty($availablethemes[$course['forcetheme']])) {
+ throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
+ } else {
+ $course['theme'] = $course['forcetheme'];
+ }
+ }
+ }
+
+ //force visibility if ws user doesn't have the permission to set it
+ $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
+ if (!has_capability('moodle/course:visibility', $context)) {
+ $course['visible'] = $category->visible;
+ }
+
+ //set default value for completion
+ $courseconfig = get_config('moodlecourse');
+ if (completion_info::is_enabled_for_site()) {
+ if (!array_key_exists('enablecompletion', $course)) {
+ $course['enablecompletion'] = $courseconfig->enablecompletion;
+ }
+ if (!array_key_exists('completionstartonenrol', $course)) {
+ $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
+ }
+ } else {
+ $course['enablecompletion'] = 0;
+ $course['completionstartonenrol'] = 0;
+ }
+
+ $course['category'] = $course['categoryid'];
+
+ // Summary format.
+ $course['summaryformat'] = external_validate_format($course['summaryformat']);
+
+ if (!empty($course['courseformatoptions'])) {
+ foreach ($course['courseformatoptions'] as $option) {
+ $course[$option['name']] = $option['value'];
+ }
+ }
+
+ //Note: create_course() core function check shortname, idnumber, category
+ $course['id'] = create_course((object) $course)->id;
+
+ $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
+ }
+
+ $transaction->allow_commit();
+
+ return $resultcourses;
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.2
+ */
+ public static function create_courses_returns() {
+ return new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'course id'),
+ 'shortname' => new external_value(PARAM_TEXT, 'short name'),
+ )
+ )
+ );
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.2
+ */
+ public static function delete_courses_parameters() {
+ return new external_function_parameters(
+ array(
+ 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
+ )
+ );
+ }
+
+ /**
+ * Delete courses
+ *
+ * @param array $courseids A list of course ids
+ * @since Moodle 2.2
+ */
+ public static function delete_courses($courseids) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot."/course/lib.php");
+
+ // Parameter validation.
+ $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
+
+ $transaction = $DB->start_delegated_transaction();
+
+ foreach ($params['courseids'] as $courseid) {
+ $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+
+ // Check if the context is valid.
+ $coursecontext = context_course::instance($course->id);
+ self::validate_context($coursecontext);
+
+ // Check if the current user has enought permissions.
+ if (!can_delete_course($courseid)) {
+ throw new moodle_exception('cannotdeletecategorycourse', 'error',
+ '', format_string($course->fullname)." (id: $courseid)");
+ }
+
+ delete_course($course, false);
+ }
+
+ $transaction->allow_commit();
+
+ return null;
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.2
+ */
+ public static function delete_courses_returns() {
+ return null;
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function duplicate_course_parameters() {
+ return new external_function_parameters(
+ array(
+ 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
+ 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
+ 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
+ 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
+ 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
+ 'options' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
+ "activities" (int) Include course activites (default to 1 that is equal to yes),
+ "blocks" (int) Include course blocks (default to 1 that is equal to yes),
+ "filters" (int) Include course filters (default to 1 that is equal to yes),
+ "users" (int) Include users (default to 0 that is equal to no),
+ "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
+ "comments" (int) Include user comments (default to 0 that is equal to no),
+ "completion_information" (int) Include user course completion information (default to 0 that is equal to no),
+ "logs" (int) Include course logs (default to 0 that is equal to no),
+ "histories" (int) Include histories (default to 0 that is equal to no)'
+ ),
+ 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
+ )
+ )
+ ), VALUE_DEFAULT, array()
+ ),
+ )
+ );
+ }
+
+ /**
+ * Duplicate a course
+ *
+ * @param int $courseid
+ * @param string $fullname Duplicated course fullname
+ * @param string $shortname Duplicated course shortname
+ * @param int $categoryid Duplicated course parent category id
+ * @param int $visible Duplicated course availability
+ * @param array $options List of backup options
+ * @return array New course info
+ * @since Moodle 2.3
+ */
+ public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
+ global $CFG, $USER, $DB;
+ require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+ require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+
+ // Parameter validation.
+ $params = self::validate_parameters(
+ self::duplicate_course_parameters(),
+ array(
+ 'courseid' => $courseid,
+ 'fullname' => $fullname,
+ 'shortname' => $shortname,
+ 'categoryid' => $categoryid,
+ 'visible' => $visible,
+ 'options' => $options
+ )
+ );
+
+ // Context validation.
+
+ if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
+ throw new moodle_exception('invalidcourseid', 'error');
+ }
+
+ // Category where duplicated course is going to be created.
+ $categorycontext = context_coursecat::instance($params['categoryid']);
+ self::validate_context($categorycontext);
+
+ // Course to be duplicated.
+ $coursecontext = context_course::instance($course->id);
+ self::validate_context($coursecontext);
+
+ $backupdefaults = array(
+ 'activities' => 1,
+ 'blocks' => 1,
+ 'filters' => 1,
+ 'users' => 0,
+ 'role_assignments' => 0,
+ 'comments' => 0,
+ 'completion_information' => 0,
+ 'logs' => 0,
+ 'histories' => 0
+ );
+
+ $backupsettings = array();
+ // Check for backup and restore options.
+ if (!empty($params['options'])) {
+ foreach ($params['options'] as $option) {
+
+ // Strict check for a correct value (allways 1 or 0, true or false).
+ $value = clean_param($option['value'], PARAM_INT);
+
+ if ($value !== 0 and $value !== 1) {
+ throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
+ }
+
+ if (!isset($backupdefaults[$option['name']])) {
+ throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
+ }
+
+ $backupsettings[$option['name']] = $value;
+ }
+ }
+
+ // Capability checking.
+
+ // The backup controller check for this currently, this may be redundant.
+ require_capability('moodle/course:create', $categorycontext);
+ require_capability('moodle/restore:restorecourse', $categorycontext);
+ require_capability('moodle/backup:backupcourse', $coursecontext);
+
+ if (!empty($backupsettings['users'])) {
+ require_capability('moodle/backup:userinfo', $coursecontext);
+ require_capability('moodle/restore:userinfo', $categorycontext);
+ }
+
+ // Check if the shortname is used.
+ if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
+ foreach ($foundcourses as $foundcourse) {
+ $foundcoursenames[] = $foundcourse->fullname;
+ }
+
+ $foundcoursenamestring = implode(',', $foundcoursenames);
+ throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
+ }
+
+ // Backup the course.
+
+ $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
+
+ foreach ($backupsettings as $name => $value) {
+ $bc->get_plan()->get_setting($name)->set_value($value);
+ }
+
+ $backupid = $bc->get_backupid();
+ $backupbasepath = $bc->get_plan()->get_basepath();
+
+ $bc->execute_plan();
+ $results = $bc->get_results();
+ $file = $results['backup_destination'];
+
+ $bc->destroy();
+
+ // Restore the backup immediately.
+
+ // Check if we need to unzip the file because the backup temp dir does not contains backup files.
+ if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
+ $file->extract_to_pathname(get_file_packer(), $backupbasepath);
+ }
+
+ // Create new course.
+ $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
+
+ $rc = new restore_controller($backupid, $newcourseid,
+ backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
+
+ foreach ($backupsettings as $name => $value) {
+ $setting = $rc->get_plan()->get_setting($name);
+ if ($setting->get_status() == backup_setting::NOT_LOCKED) {
+ $setting->set_value($value);
+ }
+ }
+
+ if (!$rc->execute_precheck()) {
+ $precheckresults = $rc->get_precheck_results();
+ if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+ }
+
+ $errorinfo = '';
+
+ foreach ($precheckresults['errors'] as $error) {
+ $errorinfo .= $error;
+ }
+
+ if (array_key_exists('warnings', $precheckresults)) {
+ foreach ($precheckresults['warnings'] as $warning) {
+ $errorinfo .= $warning;
+ }
+ }
+
+ throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
+ }
+ }
+
+ $rc->execute_plan();
+ $rc->destroy();
+
+ $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
+ $course->fullname = $params['fullname'];
+ $course->shortname = $params['shortname'];
+ $course->visible = $params['visible'];
+
+ // Set shortname and fullname back.
+ $DB->update_record('course', $course);
+
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+ }
+
+ // Delete the course backup file created by this WebService. Originally located in the course backups area.
+ $file->delete();
+
+ return array('id' => $course->id, 'shortname' => $course->shortname);
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.3
+ */
+ public static function duplicate_course_returns() {
+ return new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'course id'),
+ 'shortname' => new external_value(PARAM_TEXT, 'short name'),
+ )
+ );
+ }
+
+ /**
+ * Returns description of method parameters for import_course
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.4
+ */
+ public static function import_course_parameters() {
+ return new external_function_parameters(
+ array(
+ 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
+ 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
+ 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
+ 'options' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
+ "activities" (int) Include course activites (default to 1 that is equal to yes),
+ "blocks" (int) Include course blocks (default to 1 that is equal to yes),
+ "filters" (int) Include course filters (default to 1 that is equal to yes)'
+ ),
+ 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
+ )
+ )
+ ), VALUE_DEFAULT, array()
+ ),
+ )
+ );
+ }
+
+ /**
+ * Imports a course
+ *
+ * @param int $importfrom The id of the course we are importing from
+ * @param int $importto The id of the course we are importing to
+ * @param bool $deletecontent Whether to delete the course we are importing to content
+ * @param array $options List of backup options
+ * @return null
+ * @since Moodle 2.4
+ */
+ public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
+ global $CFG, $USER, $DB;
+ require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+ require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+
+ // Parameter validation.
+ $params = self::validate_parameters(
+ self::import_course_parameters(),
+ array(
+ 'importfrom' => $importfrom,
+ 'importto' => $importto,
+ 'deletecontent' => $deletecontent,
+ 'options' => $options
+ )
+ );
+
+ if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
+ throw new moodle_exception('invalidextparam', 'webservice', '', $option['deletecontent']);
+ }
+
+ // Context validation.
+
+ if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
+ throw new moodle_exception('invalidcourseid', 'error');
+ }
+
+ if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
+ throw new moodle_exception('invalidcourseid', 'error');
+ }
+
+ $importfromcontext = context_course::instance($importfrom->id);
+ self::validate_context($importfromcontext);
+
+ $importtocontext = context_course::instance($importto->id);
+ self::validate_context($importtocontext);
+
+ $backupdefaults = array(
+ 'activities' => 1,
+ 'blocks' => 1,
+ 'filters' => 1
+ );
+
+ $backupsettings = array();
+
+ // Check for backup and restore options.
+ if (!empty($params['options'])) {
+ foreach ($params['options'] as $option) {
+
+ // Strict check for a correct value (allways 1 or 0, true or false).
+ $value = clean_param($option['value'], PARAM_INT);
+
+ if ($value !== 0 and $value !== 1) {
+ throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
+ }
+
+ if (!isset($backupdefaults[$option['name']])) {
+ throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
+ }
+
+ $backupsettings[$option['name']] = $value;
+ }
+ }
+
+ // Capability checking.
+
+ require_capability('moodle/backup:backuptargetimport', $importfromcontext);
+ require_capability('moodle/restore:restoretargetimport', $importtocontext);
+
+ $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
+
+ foreach ($backupsettings as $name => $value) {
+ $bc->get_plan()->get_setting($name)->set_value($value);
+ }
+
+ $backupid = $bc->get_backupid();
+ $backupbasepath = $bc->get_plan()->get_basepath();
+
+ $bc->execute_plan();
+ $bc->destroy();
+
+ // Restore the backup immediately.
+
+ // Check if we must delete the contents of the destination course.
+ if ($params['deletecontent']) {
+ $restoretarget = backup::TARGET_EXISTING_DELETING;
+ } else {
+ $restoretarget = backup::TARGET_EXISTING_ADDING;
+ }
+
+ $rc = new restore_controller($backupid, $importto->id,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
+
+ foreach ($backupsettings as $name => $value) {
+ $rc->get_plan()->get_setting($name)->set_value($value);
+ }
+
+ if (!$rc->execute_precheck()) {
+ $precheckresults = $rc->get_precheck_results();
+ if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+ }
+
+ $errorinfo = '';
+
+ foreach ($precheckresults['errors'] as $error) {
+ $errorinfo .= $error;
+ }
+
+ if (array_key_exists('warnings', $precheckresults)) {
+ foreach ($precheckresults['warnings'] as $warning) {
+ $errorinfo .= $warning;
+ }
+ }
+
+ throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
+ }
+ } else {
+ if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
+ restore_dbops::delete_course_content($importto->id);
+ }
+ }
+
+ $rc->execute_plan();
+ $rc->destroy();
+
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.4
+ */
+ public static function import_course_returns() {
+ return null;
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function get_categories_parameters() {
+ return new external_function_parameters(
+ array(
+ 'criteria' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'key' => new external_value(PARAM_ALPHA,
+ 'The category column to search, expected keys (value format) are:'.
+ '"id" (int) the category id,'.
+ '"name" (string) the category name,'.
+ '"parent" (int) the parent category id,'.
+ '"idnumber" (string) category idnumber'.
+ ' - user must have \'moodle/category:manage\' to search on idnumber,'.
+ '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
+ then the function return all categories that the user can see.'.
+ ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
+ '"theme" (string) only return the categories having this theme'.
+ ' - user must have \'moodle/category:manage\' to search on theme'),
+ 'value' => new external_value(PARAM_RAW, 'the value to match')
+ )
+ ), 'criteria', VALUE_DEFAULT, array()
+ ),
+ 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
+ (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
+ )
+ );
+ }
+
+ /**
+ * Get categories
+ *
+ * @param array $criteria Criteria to match the results
+ * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
+ * @return array list of categories
+ * @since Moodle 2.3
+ */
+ public static function get_categories($criteria = array(), $addsubcategories = true) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot . "/course/lib.php");
+
+ // Validate parameters.
+ $params = self::validate_parameters(self::get_categories_parameters(),
+ array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
+
+ // Retrieve the categories.
+ $categories = array();
+ if (!empty($params['criteria'])) {
+
+ $conditions = array();
+ $wheres = array();
+ foreach ($params['criteria'] as $crit) {
+ $key = trim($crit['key']);
+
+ // Trying to avoid duplicate keys.
+ if (!isset($conditions[$key])) {
+
+ $context = context_system::instance();
+ $value = null;
+ switch ($key) {
+ case 'id':
+ $value = clean_param($crit['value'], PARAM_INT);
+ break;
+
+ case 'idnumber':
+ if (has_capability('moodle/category:manage', $context)) {
+ $value = clean_param($crit['value'], PARAM_RAW);
+ } else {
+ // We must throw an exception.
+ // Otherwise the dev client would think no idnumber exists.
+ throw new moodle_exception('criteriaerror',
+ 'webservice', '', null,
+ 'You don\'t have the permissions to search on the "idnumber" field.');
+ }
+ break;
+
+ case 'name':
+ $value = clean_param($crit['value'], PARAM_TEXT);
+ break;
+
+ case 'parent':
+ $value = clean_param($crit['value'], PARAM_INT);
+ break;
+
+ case 'visible':
+ if (has_capability('moodle/category:manage', $context)
+ or has_capability('moodle/category:viewhiddencategories',
+ context_system::instance())) {
+ $value = clean_param($crit['value'], PARAM_INT);
+ } else {
+ throw new moodle_exception('criteriaerror',
+ 'webservice', '', null,
+ 'You don\'t have the permissions to search on the "visible" field.');
+ }
+ break;
+
+ case 'theme':
+ if (has_capability('moodle/category:manage', $context)) {
+ $value = clean_param($crit['value'], PARAM_THEME);
+ } else {
+ throw new moodle_exception('criteriaerror',
+ 'webservice', '', null,
+ 'You don\'t have the permissions to search on the "theme" field.');
+ }
+ break;
+
+ default:
+ throw new moodle_exception('criteriaerror',
+ 'webservice', '', null,
+ 'You can not search on this criteria: ' . $key);
+ }
+
+ if (isset($value)) {
+ $conditions[$key] = $crit['value'];
+ $wheres[] = $key . " = :" . $key;
+ }
+ }
+ }
+
+ if (!empty($wheres)) {
+ $wheres = implode(" AND ", $wheres);
+
+ $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
+
+ // Retrieve its sub subcategories (all levels).
+ if ($categories and !empty($params['addsubcategories'])) {
+ $newcategories = array();
+
+ // Check if we required visible/theme checks.
+ $additionalselect = '';
+ $additionalparams = array();
+ if (isset($conditions['visible'])) {
+ $additionalselect .= ' AND visible = :visible';
+ $additionalparams['visible'] = $conditions['visible'];
+ }
+ if (isset($conditions['theme'])) {
+ $additionalselect .= ' AND theme= :theme';
+ $additionalparams['theme'] = $conditions['theme'];
+ }
+
+ foreach ($categories as $category) {
+ $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
+ $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
+ $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
+ $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
+ }
+ $categories = $categories + $newcategories;
+ }
+ }
+
+ } else {
+ // Retrieve all categories in the database.
+ $categories = $DB->get_records('course_categories');
+ }
+
+ // The not returned categories. key => category id, value => reason of exclusion.
+ $excludedcats = array();
+
+ // The returned categories.
+ $categoriesinfo = array();
+
+ // We need to sort the categories by path.
+ // The parent cats need to be checked by the algo first.
+ usort($categories, "core_course_external::compare_categories_by_path");
+
+ foreach ($categories as $category) {
+
+ // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
+ $parents = explode('/', $category->path);
+ unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
+ foreach ($parents as $parentid) {
+ // Note: when the parent exclusion was due to the context,
+ // the sub category could still be returned.
+ if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
+ $excludedcats[$category->id] = 'parent';
+ }
+ }
+
+ // Check category depth is <= maxdepth (do not check for user who can manage categories).
+ if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
+ and !has_capability('moodle/category:manage', $context)) {
+ $excludedcats[$category->id] = 'depth';
+ }
+
+ // Check the user can use the category context.
+ $context = context_coursecat::instance($category->id);
+ try {
+ self::validate_context($context);
+ } catch (Exception $e) {
+ $excludedcats[$category->id] = 'context';
+
+ // If it was the requested category then throw an exception.
+ if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
+ $exceptionparam = new stdClass();
+ $exceptionparam->message = $e->getMessage();
+ $exceptionparam->catid = $category->id;
+ throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
+ }
+ }
+
+ // Return the category information.
+ if (!isset($excludedcats[$category->id])) {
+
+ // Final check to see if the category is visible to the user.
+ if ($category->visible
+ or has_capability('moodle/category:viewhiddencategories', context_system::instance())
+ or has_capability('moodle/category:manage', $context)) {
+
+ $categoryinfo = array();
+ $categoryinfo['id'] = $category->id;
+ $categoryinfo['name'] = $category->name;
+ list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
+ external_format_text($category->description, $category->descriptionformat,
+ $context->id, 'coursecat', 'description', null);
+ $categoryinfo['parent'] = $category->parent;
+ $categoryinfo['sortorder'] = $category->sortorder;
+ $categoryinfo['coursecount'] = $category->coursecount;
+ $categoryinfo['depth'] = $category->depth;
+ $categoryinfo['path'] = $category->path;
+
+ // Some fields only returned for admin.
+ if (has_capability('moodle/category:manage', $context)) {
+ $categoryinfo['idnumber'] = $category->idnumber;
+ $categoryinfo['visible'] = $category->visible;
+ $categoryinfo['visibleold'] = $category->visibleold;
+ $categoryinfo['timemodified'] = $category->timemodified;
+ $categoryinfo['theme'] = $category->theme;
+ }
+
+ $categoriesinfo[] = $categoryinfo;
+ } else {
+ $excludedcats[$category->id] = 'visibility';
+ }
+ }
+ }
+
+ // Sorting the resulting array so it looks a bit better for the client developer.
+ usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
+
+ return $categoriesinfo;
+ }
+
+ /**
+ * Sort categories array by path
+ * private function: only used by get_categories
+ *
+ * @param array $category1
+ * @param array $category2
+ * @return int result of strcmp
+ * @since Moodle 2.3
+ */
+ private static function compare_categories_by_path($category1, $category2) {
+ return strcmp($category1->path, $category2->path);
+ }
+
+ /**
+ * Sort categories array by sortorder
+ * private function: only used by get_categories
+ *
+ * @param array $category1
+ * @param array $category2
+ * @return int result of strcmp
+ * @since Moodle 2.3
+ */
+ private static function compare_categories_by_sortorder($category1, $category2) {
+ return strcmp($category1['sortorder'], $category2['sortorder']);
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.3
+ */
+ public static function get_categories_returns() {
+ return new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'category id'),
+ 'name' => new external_value(PARAM_TEXT, 'category name'),
+ 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
+ 'description' => new external_value(PARAM_RAW, 'category description'),
+ 'descriptionformat' => new external_format_value('description'),
+ 'parent' => new external_value(PARAM_INT, 'parent category id'),
+ 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
+ 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
+ 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
+ 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
+ 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
+ 'depth' => new external_value(PARAM_INT, 'category depth'),
+ 'path' => new external_value(PARAM_TEXT, 'category path'),
+ 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
+ ), 'List of categories'
+ )
+ );
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function create_categories_parameters() {
+ return new external_function_parameters(
+ array(
+ 'categories' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_TEXT, 'new category name'),
+ 'parent' => new external_value(PARAM_INT,
+ 'the parent category id inside which the new category will be created
+ - set to 0 for a root category',
+ VALUE_DEFAULT, 0),
+ 'idnumber' => new external_value(PARAM_RAW,
+ 'the new category idnumber', VALUE_OPTIONAL),
+ 'description' => new external_value(PARAM_RAW,
+ 'the new category description', VALUE_OPTIONAL),
+ 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
+ 'theme' => new external_value(PARAM_THEME,
+ 'the new category theme. This option must be enabled on moodle',
+ VALUE_OPTIONAL),
+ )
+ )
+ )
+ )
+ );
+ }
+
+ /**
+ * Create categories
+ *
+ * @param array $categories - see create_categories_parameters() for the array structure
+ * @return array - see create_categories_returns() for the array structure
+ * @since Moodle 2.3
+ */
+ public static function create_categories($categories) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot . "/course/lib.php");
+
+ $params = self::validate_parameters(self::create_categories_parameters(),
+ array('categories' => $categories));
+
+ $transaction = $DB->start_delegated_transaction();
+
+ $createdcategories = array();
+ foreach ($params['categories'] as $category) {
+ if ($category['parent']) {
+ if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
+ throw new moodle_exception('unknowcategory');
+ }
+ $context = context_coursecat::instance($category['parent']);
+ } else {
+ $context = context_system::instance();
+ }
+ self::validate_context($context);
+ require_capability('moodle/category:manage', $context);
+
+ // Check name.
+ if (textlib::strlen($category['name'])>255) {
+ throw new moodle_exception('categorytoolong');
+ }
+
+ $newcategory = new stdClass();
+ $newcategory->name = $category['name'];
+ $newcategory->parent = $category['parent'];
+ $newcategory->sortorder = 999; // Same as in the course/editcategory.php .
+ // Format the description.
+ if (!empty($category['description'])) {
+ $newcategory->description = $category['description'];
+ }
+ $newcategory->descriptionformat = external_validate_format($category['descriptionformat']);
+ if (isset($category['theme']) and !empty($CFG->allowcategorythemes)) {
+ $newcategory->theme = $category['theme'];
+ }
+ // Check id number.
+ if (!empty($category['idnumber'])) { // Same as in course/editcategory_form.php .
+ if (textlib::strlen($category['idnumber'])>100) {
+ throw new moodle_exception('idnumbertoolong');
+ }
+ if ($existing = $DB->get_record('course_categories', array('idnumber' => $category['idnumber']))) {
+ if ($existing->id) {
+ throw new moodle_exception('idnumbertaken');
+ }
+ }
+ $newcategory->idnumber = $category['idnumber'];
+ }
+
+ $newcategory = create_course_category($newcategory);
+ // Populate special fields.
+ fix_course_sortorder();
+
+ $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
+ }
+
+ $transaction->allow_commit();
+
+ return $createdcategories;
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function create_categories_returns() {
+ return new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'new category id'),
+ 'name' => new external_value(PARAM_TEXT, 'new category name'),
+ )
+ )
+ );
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function update_categories_parameters() {
+ return new external_function_parameters(
+ array(
+ 'categories' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'course id'),
+ 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
+ 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
+ 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
+ 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
+ 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
+ 'theme' => new external_value(PARAM_THEME,
+ 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
+ )
+ )
+ )
+ )
+ );
+ }
+
+ /**
+ * Update categories
+ *
+ * @param array $categories The list of categories to update
+ * @return null
+ * @since Moodle 2.3
+ */
+ public static function update_categories($categories) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot . "/course/lib.php");
+
+ // Validate parameters.
+ $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
+
+ $transaction = $DB->start_delegated_transaction();
+
+ foreach ($params['categories'] as $cat) {
+ if (!$category = $DB->get_record('course_categories', array('id' => $cat['id']))) {
+ throw new moodle_exception('unknowcategory');
+ }
+
+ $categorycontext = context_coursecat::instance($cat['id']);
+ self::validate_context($categorycontext);
+ require_capability('moodle/category:manage', $categorycontext);
+
+ if (!empty($cat['name'])) {
+ if (textlib::strlen($cat['name'])>255) {
+ throw new moodle_exception('categorytoolong');
+ }
+ $category->name = $cat['name'];
+ }
+ if (!empty($cat['idnumber'])) {
+ if (textlib::strlen($cat['idnumber'])>100) {
+ throw new moodle_exception('idnumbertoolong');
+ }
+ $category->idnumber = $cat['idnumber'];
+ }
+ if (!empty($cat['description'])) {
+ $category->description = $cat['description'];
+ $category->descriptionformat = external_validate_format($cat['descriptionformat']);
+ }
+ if (!empty($cat['theme'])) {
+ $category->theme = $cat['theme'];
+ }
+ if (!empty($cat['parent']) && ($category->parent != $cat['parent'])) {
+ // First check if parent exists.
+ if (!$parent_cat = $DB->get_record('course_categories', array('id' => $cat['parent']))) {
+ throw new moodle_exception('unknowcategory');
+ }
+ // Then check if we have capability.
+ self::validate_context(get_category_or_system_context((int)$cat['parent']));
+ require_capability('moodle/category:manage', get_category_or_system_context((int)$cat['parent']));
+ // Finally move the category.
+ move_category($category, $parent_cat);
+ $category->parent = $cat['parent'];
+ // Get updated path by move_category().
+ $category->path = $DB->get_field('course_categories', 'path',
+ array('id' => $category->id));
+ }
+ $DB->update_record('course_categories', $category);
+ }
+
+ $transaction->allow_commit();
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.3
+ */
+ public static function update_categories_returns() {
+ return null;
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function delete_categories_parameters() {
+ return new external_function_parameters(
+ array(
+ 'categories' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'category id to delete'),
+ 'newparent' => new external_value(PARAM_INT,
+ 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
+ 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
+ category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
+ )
+ )
+ )
+ )
+ );
+ }
+
+ /**
+ * Delete categories
+ *
+ * @param array $categories A list of category ids
+ * @return array
+ * @since Moodle 2.3
+ */
+ public static function delete_categories($categories) {
+ global $CFG, $DB;
+ require_once($CFG->dirroot . "/course/lib.php");
+
+ // Validate parameters.
+ $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
+
+ $transaction = $DB->start_delegated_transaction();
+
+ foreach ($params['categories'] as $category) {
+ if (!$deletecat = $DB->get_record('course_categories', array('id' => $category['id']))) {
+ throw new moodle_exception('unknowcategory');
+ }
+ $context = context_coursecat::instance($deletecat->id);
+ require_capability('moodle/category:manage', $context);
+ self::validate_context($context);
+ self::validate_context(get_category_or_system_context($deletecat->parent));
+
+ if ($category['recursive']) {
+ // If recursive was specified, then we recursively delete the category's contents.
+ category_delete_full($deletecat, false);
+ } else {
+ // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
+ // If the parent is the root, moving is not supported (because a course must always be inside a category).
+ // We must move to an existing category.
+ if (!empty($category['newparent'])) {
+ if (!$DB->record_exists('course_categories', array('id' => $category['newparent']))) {
+ throw new moodle_exception('unknowcategory');
+ }
+ $newparent = $category['newparent'];
+ } else {
+ $newparent = $deletecat->parent;
+ }
+
+ // This operation is not allowed. We must move contents to an existing category.
+ if ($newparent == 0) {
+ throw new moodle_exception('movecatcontentstoroot');
+ }
+
+ $parentcontext = get_category_or_system_context($newparent);
+ require_capability('moodle/category:manage', $parentcontext);
+ self::validate_context($parentcontext);
+ category_delete_move($deletecat, $newparent, false);
+ }
+ }
+
+ $transaction->allow_commit();
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.3
+ */
+ public static function delete_categories_returns() {
+ return null;
+ }
+
+}
+
+/**
+ * Deprecated course external functions
+ *
+ * @package core_course
+ * @copyright 2009 Petr Skodak
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external
+ */
+class moodle_course_external extends external_api {
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external::get_courses_parameters()
+ */
+ public static function get_courses_parameters() {
+ return core_course_external::get_courses_parameters();
+ }
+
+ /**
+ * Get courses
+ *
+ * @param array $options
+ * @return array
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external::get_courses()
+ */
+ public static function get_courses($options) {
+ return core_course_external::get_courses($options);
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external::get_courses_returns()
+ */
+ public static function get_courses_returns() {
+ return core_course_external::get_courses_returns();
+ }
+
+ /**
+ * Returns description of method parameters
+ *
+ * @return external_function_parameters
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external::create_courses_parameters()
+ */
+ public static function create_courses_parameters() {
+ return core_course_external::create_courses_parameters();
+ }
+
+ /**
+ * Create courses
+ *
+ * @param array $courses
+ * @return array courses (id and shortname only)
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external::create_courses()
+ */
+ public static function create_courses($courses) {
+ return core_course_external::create_courses($courses);
+ }
+
+ /**
+ * Returns description of method result value
+ *
+ * @return external_description
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external::create_courses_returns()
+ */
+ public static function create_courses_returns() {
+ return core_course_external::create_courses_returns();
+ }
+
+}
--- /dev/null
+Course formats
+==============
+
+To create a new course format, make another folder in here.
+
+If you want a basic format, you only need to write the 'standard files' listed
+below.
+
+If you want to store information in the database for your format, or control
+access to features of your format, you need some of the optional files too.
+
+All names below assume that your format is called 'yourformat'.
+
+
+Standard files
+--------------
+
+* yourformat/format.php
+
+ Code that actually displays the course view page. See existing formats for
+ examples.
+
+* yourformat/config.php
+
+ Configuration file, mainly controlling default blocks for the format.
+ See existing formats for examples.
+
+* yourformat/lang/en/format_yourformat.php
+
+ Language file containing basic language strings for your format. Here
+ is a minimal language file:
+
+<?php
+$string['formatyourformat']='Your format'; // Name to display for format
+$string['nameyourformat']='section'; // Name of a section within your format
+?>
+
+ The first string is used in the dropdown menu of course settings. The second
+ is used when editing an activity within a course of your format.
+
+ Note that existing formats store their language strings in the main
+ moodle.php, which you can also do, but this separate file is recommended
+ for contributed formats.
+
+ You can also store other strings in this file if you wish. They can be
+ accessed as follows, for example to get the section name:
+
+ get_string('nameyourformat','format_yourformat');
+
+ Of course you can have other folders as well as just English if you want
+ to provide multiple languages.
+
+
+Optional files (database access)
+--------------------------------
+
+If these files exist, Moodle will use them to set up database tables when you
+visit the admin page.
+
+* yourformat/db/install.xml
+
+ Database table definitions. Use your format name at the start of the table
+ names to increase the chance that they are unique.
+
+* yourformat/db/upgrade.php
+
+ Database upgrade instructions. Similar to other upgrade.php files, so look
+ at those for modules etc. if you want to see.
+
+ The function must look like:
+
+ function xmldb_format_yourformat_upgrade($oldversion) {
+ ...
+
+* yourformat/version.php
+
+ Required if you use database tables.
+
+ <?php
+ $plugin->version = 2006120100; // Plugin version (update when tables change)
+ $plugin->requires = 2006092801; // Required Moodle version
+ ?>
+
+
+Optional files (backup)
+-----------------------
+
+If these files exist, backup and restore run automatically when backing up
+the course. You can't back up the course format data independently.
+
+* yourformat/backuplib.php
+
+ Similar to backup code for other plugins. Must have a function:
+
+ function yourformat_backup_format_data($bf,$preferences) {
+ ...
+
+* yourformat/restorelib.php
+
+ Similar to restore code for other plugins. Must have a function:
+
+ function yourformat_restore_format_data($restore,$data) {
+ ...
+
+ ($data is the xmlized data underneath FORMATDATA in the backup XML file.
+ Do print_object($data); while testing to see how it looks.)
+
+
+Optional file (capabilities)
+----------------------------
+
+If this file exists, Moodle refreshes your format's capabilities
+(checks that they are all included in the database) whenever you increase
+the version in yourformat/version.php.
+
+* yourformat/db/access.php
+
+ Contains capability entries similar to other access.php files.
+
+ The array definition must look like:
+
+ $format_yourformat_capabilities = array(
+ ...
+
+ Format names must look like:
+
+ format/yourformat:specialpower
+
+ Capability definitions in your language file must look like:
+
+ $string['yourformat:specialpower']='Revolutionise the world';
+
+
+
+Optional file (styles)
+----------------------
+
+* yourformat/styles.php
+
+ If this file exists it will be included in the CSS Moodle generates.
+
+
+Optional delete course hook
+---------------------------
+
+* in your yourformat/lib.php add function format_yourformat_delete_course($courseid)
\ No newline at end of file
--- /dev/null
+<?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/>.
+
+/**
+ * Course format class to allow plugins developed for Moodle 2.3 to work in the new API
+ *
+ * @package core_course
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Course format class to allow plugins developed for Moodle 2.3 to work in the new API
+ *
+ * @package core_course
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_legacy extends format_base {
+
+ /**
+ * Returns true if this course format uses sections
+ *
+ * This function calls function callback_FORMATNAME_uses_sections() if it exists
+ *
+ * @return bool
+ */
+ public function uses_sections() {
+ global $CFG;
+ // Note that lib.php in course format folder is already included by now
+ $featurefunction = 'callback_'.$this->format.'_uses_sections';
+ if (function_exists($featurefunction)) {
+ return $featurefunction();
+ }
+ return false;
+ }
+
+ /**
+ * Returns the display name of the given section that the course prefers.
+ *
+ * This function calls function callback_FORMATNAME_get_section_name() if it exists
+ *
+ * @param int|stdClass $section Section object from database or just field section.section
+ * @return string Display name that the course format prefers, e.g. "Topic 2"
+ */
+ public function get_section_name($section) {
+ // Use course formatter callback if it exists
+ $namingfunction = 'callback_'.$this->format.'_get_section_name';
+ if (function_exists($namingfunction) && ($course = $this->get_course())) {
+ return $namingfunction($course, $this->get_section($section));
+ }
+
+ // else, default behavior:
+ return parent::get_section_name($section);
+ }
+
+ /**
+ * The URL to use for the specified course (with section)
+ *
+ * This function calls function callback_FORMATNAME_get_section_url() if it exists
+ *
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * if omitted the course view page is returned
+ * @param array $options options for view URL. At the moment core uses:
+ * 'navigation' (bool) if true and section has no separate page, the function returns null
+ * 'sr' (int) used by multipage formats to specify to which section to return
+ * @return null|moodle_url
+ */
+ public function get_view_url($section, $options = array()) {
+ // Use course formatter callback if it exists
+ $featurefunction = 'callback_'.$this->format.'_get_section_url';
+ if (function_exists($featurefunction) && ($course = $this->get_course())) {
+ if (is_object($section)) {
+ $sectionnum = $section->section;
+ } else {
+ $sectionnum = $section;
+ }
+ if ($sectionnum) {
+ $url = $featurefunction($course, $sectionnum);
+ if ($url || !empty($options['navigation'])) {
+ return $url;
+ }
+ }
+ }
+
+ // if function is not defined
+ if (!$this->uses_sections() ||
+ !array_key_exists('coursedisplay', $this->course_format_options())) {
+ // default behaviour
+ return parent::get_view_url($section, $options);
+ }
+
+ $course = $this->get_course();
+ $url = new moodle_url('/course/view.php', array('id' => $course->id));
+
+ $sr = null;
+ if (array_key_exists('sr', $options)) {
+ $sr = $options['sr'];
+ }
+ if (is_object($section)) {
+ $sectionno = $section->section;
+ } else {
+ $sectionno = $section;
+ }
+ if ($sectionno !== null) {
+ if ($sr !== null) {
+ if ($sr) {
+ $usercoursedisplay = COURSE_DISPLAY_MULTIPAGE;
+ $sectionno = $sr;
+ } else {
+ $usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
+ }
+ } else {
+ $usercoursedisplay = $course->coursedisplay;
+ }
+ if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
+ $url->param('section', $sectionno);
+ } else {
+ if (!empty($options['navigation'])) {
+ return null;
+ }
+ $url->set_anchor('section-'.$sectionno);
+ }
+ }
+ return $url;
+ }
+
+ /**
+ * Returns the information about the ajax support in the given source format
+ *
+ * This function calls function callback_FORMATNAME_ajax_support() if it exists
+ *
+ * The returned object's property (boolean)capable indicates that
+ * the course format supports Moodle course ajax features.
+ * The property (array)testedbrowsers can be used as a parameter for {@link ajaxenabled()}.
+ *
+ * @return stdClass
+ */
+ public function supports_ajax() {
+ // set up default values
+ $ajaxsupport = parent::supports_ajax();
+
+ // get the information from the course format library
+ $featurefunction = 'callback_'.$this->format.'_ajax_support';
+ if (function_exists($featurefunction)) {
+ $formatsupport = $featurefunction();
+ if (isset($formatsupport->capable)) {
+ $ajaxsupport->capable = $formatsupport->capable;
+ }
+ if (is_array($formatsupport->testedbrowsers)) {
+ $ajaxsupport->testedbrowsers = $formatsupport->testedbrowsers;
+ }
+ }
+ return $ajaxsupport;
+ }
+
+ /**
+ * Loads all of the course sections into the navigation
+ *
+ * First this function calls callback_FORMATNAME_display_content() if it exists to check
+ * if the navigation should be extended at all
+ *
+ * Then it calls function callback_FORMATNAME_load_content() if it exists to actually extend
+ * navigation
+ *
+ * By default the parent method is called
+ *
+ * @param global_navigation $navigation
+ * @param navigation_node $node The course node within the navigation
+ */
+ public function extend_course_navigation($navigation, navigation_node $node) {
+ global $PAGE;
+ // if course format displays section on separate pages and we are on course/view.php page
+ // and the section parameter is specified, make sure this section is expanded in
+ // navigation
+ if ($navigation->includesectionnum === false) {
+ $selectedsection = optional_param('section', null, PARAM_INT);
+ if ($selectedsection !== null && (!defined('AJAX_SCRIPT') || AJAX_SCRIPT == '0') &&
+ $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
+ $navigation->includesectionnum = $selectedsection;
+ }
+ }
+
+ // check if there are callbacks to extend course navigation
+ $displayfunc = 'callback_'.$this->format.'_display_content';
+ if (function_exists($displayfunc) && !$displayfunc()) {
+ return;
+ }
+ $featurefunction = 'callback_'.$this->format.'_load_content';
+ if (function_exists($featurefunction) && ($course = $this->get_course())) {
+ $featurefunction($navigation, $course, $node);
+ } else {
+ parent::extend_course_navigation($navigation, $node);
+ }
+ }
+
+ /**
+ * Custom action after section has been moved in AJAX mode
+ *
+ * Used in course/rest.php
+ *
+ * This function calls function callback_FORMATNAME_ajax_section_move() if it exists
+ *
+ * @return array This will be passed in ajax respose
+ */
+ function ajax_section_move() {
+ $featurefunction = 'callback_'.$this->format.'_ajax_section_move';
+ if (function_exists($featurefunction) && ($course = $this->get_course())) {
+ return $featurefunction($course);
+ } else {
+ return parent::ajax_section_move();
+ }
+ }
+
+ /**
+ * Returns the list of blocks to be automatically added for the newly created course
+ *
+ * This function checks the existence of the file config.php in the course format folder.
+ * If file exists and contains the code
+ * $format['defaultblocks'] = 'leftblock1,leftblock2:rightblock1,rightblock2';
+ * these blocks are used, otherwise parent function is called
+ *
+ * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
+ * each of values is an array of block names (for left and right side columns)
+ */
+ public function get_default_blocks() {
+ global $CFG;
+ $formatconfig = $CFG->dirroot.'/course/format/'.$this->format.'/config.php';
+ $format = array(); // initialize array in external file
+ if (is_readable($formatconfig)) {
+ include($formatconfig);
+ }
+ if (!empty($format['defaultblocks'])) {
+ return blocks_parse_default_blocks_list($format['defaultblocks']);
+ }
+ return parent::get_default_blocks();
+ }
+
+ /**
+ * Definitions of the additional options that this course format uses for course
+ *
+ * By default course formats have the options that existed in Moodle 2.3:
+ * - coursedisplay
+ * - numsections
+ * - hiddensections
+ *
+ * @param bool $foreditform
+ * @return array of options
+ */
+ public function course_format_options($foreditform = false) {
+ static $courseformatoptions = false;
+ if ($courseformatoptions === false) {
+ $courseconfig = get_config('moodlecourse');
+ $courseformatoptions = array(
+ 'numsections' => array(
+ 'default' => $courseconfig->numsections,
+ 'type' => PARAM_INT,
+ ),
+ 'hiddensections' => array(
+ 'default' => $courseconfig->hiddensections,
+ 'type' => PARAM_INT,
+ ),
+ 'coursedisplay' => array(
+ 'default' => $courseconfig->coursedisplay,
+ 'type' => PARAM_INT,
+ ),
+ );
+ }
+ if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {
+ $courseconfig = get_config('moodlecourse');
+ $sectionmenu = array();
+ for ($i = 0; $i <= $courseconfig->maxsections; $i++) {
+ $sectionmenu[$i] = "$i";
+ }
+ $courseformatoptionsedit = array(
+ 'numsections' => array(
+ 'label' => new lang_string('numberweeks'),
+ 'element_type' => 'select',
+ 'element_attributes' => array($sectionmenu),
+ ),
+ 'hiddensections' => array(
+ 'label' => new lang_string('hiddensections'),
+ 'help' => 'hiddensections',
+ 'help_component' => 'moodle',
+ 'element_type' => 'select',
+ 'element_attributes' => array(
+ array(
+ 0 => new lang_string('hiddensectionscollapsed'),
+ 1 => new lang_string('hiddensectionsinvisible')
+ )
+ ),
+ ),
+ 'coursedisplay' => array(
+ 'label' => new lang_string('coursedisplay'),
+ 'element_type' => 'select',
+ 'element_attributes' => array(
+ array(
+ COURSE_DISPLAY_SINGLEPAGE => new lang_string('coursedisplay_single'),
+ COURSE_DISPLAY_MULTIPAGE => new lang_string('coursedisplay_multi')
+ )
+ ),
+ 'help' => 'coursedisplay',
+ 'help_component' => 'moodle',
+ )
+ );
+ $courseformatoptions = array_merge_recursive($courseformatoptions, $courseformatoptionsedit);
+ }
+ return $courseformatoptions;
+ }
+
+ /**
+ * Updates format options for a course
+ *
+ * Legacy course formats may assume that course format options
+ * ('coursedisplay', 'numsections' and 'hiddensections') are shared between formats.
+ * Therefore we make sure to copy them from the previous format
+ *
+ * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
+ * @param stdClass $oldcourse if this function is called from {@link update_course()}
+ * this object contains information about the course before update
+ * @return bool whether there were any changes to the options values
+ */
+ public function update_course_format_options($data, $oldcourse = null) {
+ global $DB;
+ if ($oldcourse !== null) {
+ $data = (array)$data;
+ $oldcourse = (array)$oldcourse;
+ $options = $this->course_format_options();
+ foreach ($options as $key => $unused) {
+ if (!array_key_exists($key, $data)) {
+ if (array_key_exists($key, $oldcourse)) {
+ $data[$key] = $oldcourse[$key];
+ } else if ($key === 'numsections') {
+ // If previous format does not have the field 'numsections' and this one does,
+ // and $data['numsections'] is not set fill it with the maximum section number from the DB
+ $maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
+ WHERE course = ?', array($this->courseid));
+ if ($maxsection) {
+ // If there are no sections, or just default 0-section, 'numsections' will be set to default
+ $data['numsections'] = $maxsection;
+ }
+ }
+ }
+ }
+ }
+ return $this->update_format_options($data);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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/>.
+
+/**
+ * Base class for course format plugins
+ *
+ * @package core_course
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Returns an instance of format class (extending format_base) for given course
+ *
+ * @param int|stdClass $courseorid either course id or
+ * an object that has the property 'format' and may contain property 'id'
+ * @return format_base
+ */
+function course_get_format($courseorid) {
+ return format_base::instance($courseorid);
+}
+
+/**
+ * Base class for course formats
+ *
+ * Each course format must declare class
+ * class format_FORMATNAME extends format_base {}
+ * in file lib.php
+ *
+ * For each course just one instance of this class is created and it will always be returned by
+ * course_get_format($courseorid). Format may store it's specific course-dependent options in
+ * variables of this class.
+ *
+ * In rare cases instance of child class may be created just for format without course id
+ * i.e. to check if format supports AJAX.
+ *
+ * Also course formats may extend class section_info and overwrite
+ * format_base::build_section_cache() to return more information about sections.
+ *
+ * If you are upgrading from Moodle 2.3 start with copying the class format_legacy and renaming
+ * it to format_FORMATNAME, then move the code from your callback functions into
+ * appropriate functions of the class.
+ *
+ * @package core_course
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class format_base {
+ /** @var int Id of the course in this instance (maybe 0) */
+ protected $courseid;
+ /** @var string format used for this course. Please note that it can be different from
+ * course.format field if course referes to non-existing of disabled format */
+ protected $format;
+ /** @var stdClass data for course object, please use {@link format_base::get_course()} */
+ protected $course = false;
+ /** @var array caches format options, please use {@link format_base::get_format_options()} */
+ protected $formatoptions = array();
+ /** @var array cached instances */
+ private static $instances = array();
+
+ /**
+ * Creates a new instance of class
+ *
+ * Please use {@link course_get_format($courseorid)} to get an instance of the format class
+ *
+ * @param string $format
+ * @param int $courseid
+ * @return format_base
+ */
+ protected function __construct($format, $courseid) {
+ $this->format = $format;
+ $this->courseid = $courseid;
+ }
+
+ /**
+ * Validates that course format exists and enabled and returns either itself or default format
+ *
+ * @param string $format
+ * @return string
+ */
+ protected static final function get_format_or_default($format) {
+ if ($format === 'site') {
+ return $format;
+ }
+ $plugins = get_sorted_course_formats();
+ if (in_array($format, $plugins)) {
+ return $format;
+ }
+ // Else return default format
+ $defaultformat = get_config('moodlecourse', 'format');
+ if (!in_array($defaultformat, $plugins)) {
+ // when default format is not set correctly, use the first available format
+ $defaultformat = reset($plugins);
+ }
+ static $warningprinted = array();
+ if (empty($warningprinted[$format])) {
+ debugging('Format plugin format_'.$format.' is not found. Using default format_'.$defaultformat, DEBUG_DEVELOPER);
+ $warningprinted[$format] = true;
+ }
+ return $defaultformat;
+ }
+
+ /**
+ * Get class name for the format
+ *
+ * If course format xxx does not declare class format_xxx, format_legacy will be returned.
+ * This function also includes lib.php file from corresponding format plugin
+ *
+ * @param string $format
+ * @return string
+ */
+ protected static final function get_class_name($format) {
+ global $CFG;
+ static $classnames = array('site' => 'format_site');
+ if (!isset($classnames[$format])) {
+ $plugins = get_plugin_list('format');
+ $usedformat = self::get_format_or_default($format);
+ if (file_exists($plugins[$usedformat].'/lib.php')) {
+ require_once($plugins[$usedformat].'/lib.php');
+ }
+ $classnames[$format] = 'format_'. $usedformat;
+ if (!class_exists($classnames[$format])) {
+ require_once($CFG->dirroot.'/course/format/formatlegacy.php');
+ $classnames[$format] = 'format_legacy';
+ }
+ }
+ return $classnames[$format];
+ }
+
+ /**
+ * Returns an instance of the class
+ *
+ * @todo MDL-35727 use MUC for caching of instances, limit the number of cached instances
+ *
+ * @param int|stdClass $courseorid either course id or
+ * an object that has the property 'format' and may contain property 'id'
+ * @return format_base
+ */
+ public static final function instance($courseorid) {
+ global $DB;
+ if (!is_object($courseorid)) {
+ $courseid = (int)$courseorid;
+ if ($courseid && isset(self::$instances[$courseid]) && count(self::$instances[$courseid]) == 1) {
+ $formats = array_keys(self::$instances[$courseid]);
+ $format = reset($formats);
+ } else {
+ $format = $DB->get_field('course', 'format', array('id' => $courseid), MUST_EXIST);
+ }
+ } else {
+ $format = $courseorid->format;
+ if (isset($courseorid->id)) {
+ $courseid = clean_param($courseorid->id, PARAM_INT);
+ } else {
+ $courseid = 0;
+ }
+ }
+ // validate that format exists and enabled, use default otherwise
+ $format = self::get_format_or_default($format);
+ if (!isset(self::$instances[$courseid][$format])) {
+ $classname = self::get_class_name($format);
+ self::$instances[$courseid][$format] = new $classname($format, $courseid);
+ }
+ return self::$instances[$courseid][$format];
+ }
+
+ /**
+ * Resets cache for the course (or all caches)
+ * To be called from {@link rebuild_course_cache()}
+ *
+ * @param int $courseid
+ */
+ public static final function reset_course_cache($courseid = 0) {
+ if ($courseid) {
+ if (isset(self::$instances[$courseid])) {
+ foreach (self::$instances[$courseid] as $format => $object) {
+ // in case somebody keeps the reference to course format object
+ self::$instances[$courseid][$format]->course = false;
+ self::$instances[$courseid][$format]->formatoptions = array();
+ }
+ unset(self::$instances[$courseid]);
+ }
+ } else {
+ self::$instances = array();
+ }
+ }
+
+ /**
+ * Returns the format name used by this course
+ *
+ * @return string
+ */
+ public final function get_format() {
+ return $this->format;
+ }
+
+ /**
+ * Returns id of the course (0 if course is not specified)
+ *
+ * @return int
+ */
+ public final function get_courseid() {
+ return $this->courseid;
+ }
+
+ /**
+ * Returns a record from course database table plus additional fields
+ * that course format defines
+ *
+ * @return stdClass
+ */
+ public function get_course() {
+ global $DB;
+ if (!$this->courseid) {
+ return null;
+ }
+ if ($this->course === false) {
+ $this->course = $DB->get_record('course', array('id' => $this->courseid));
+ $options = $this->get_format_options();
+ foreach ($options as $optionname => $optionvalue) {
+ if (!isset($this->course->$optionname)) {
+ $this->course->$optionname = $optionvalue;
+ } else {
+ debugging('The option name '.$optionname.' in course format '.$this->format.
+ ' is invalid because the field with the same name exists in {course} table',
+ DEBUG_DEVELOPER);
+ }
+ }
+ }
+ return $this->course;
+ }
+
+ /**
+ * Returns true if this course format uses sections
+ *
+ * This function may be called without specifying the course id
+ * i.e. in {@link course_format_uses_sections()}
+ *
+ * Developers, note that if course format does use sections there should be defined a language
+ * string with the name 'sectionname' defining what the section relates to in the format, i.e.
+ * $string['sectionname'] = 'Topic';
+ * or
+ * $string['sectionname'] = 'Week';
+ *
+ * @return bool
+ */
+ public function uses_sections() {
+ return false;
+ }
+
+ /**
+ * Returns a list of sections used in the course
+ *
+ * This is a shortcut to get_fast_modinfo()->get_section_info_all()
+ * @see get_fast_modinfo()
+ * @see course_modinfo::get_section_info_all()
+ *
+ * @return array of section_info objects
+ */
+ public final function get_sections() {
+ if ($course = $this->get_course()) {
+ $modinfo = get_fast_modinfo($course);
+ return $modinfo->get_section_info_all();
+ }
+ return array();
+ }
+
+ /**
+ * Returns information about section used in course
+ *
+ * @param int|stdClass $section either section number (field course_section.section) or row from course_section table
+ * @param int $strictness
+ * @return section_info
+ */
+ public final function get_section($section, $strictness = IGNORE_MISSING) {
+ if (is_object($section)) {
+ $sectionnum = $section->section;
+ } else {
+ $sectionnum = $section;
+ }
+ $sections = $this->get_sections();
+ if (array_key_exists($sectionnum, $sections)) {
+ return $sections[$sectionnum];
+ }
+ if ($strictness == MUST_EXIST) {
+ throw new moodle_exception('sectionnotexist');
+ }
+ return null;
+ }
+
+ /**
+ * Returns the display name of the given section that the course prefers.
+ *
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * @return Display name that the course format prefers, e.g. "Topic 2"
+ */
+ public function get_section_name($section) {
+ if (is_object($section)) {
+ $sectionnum = $section->section;
+ } else {
+ $sectionnum = $section;
+ }
+ return get_string('sectionname', 'format_'.$this->format) . ' ' . $sectionnum;
+ }
+
+ /**
+ * Returns the information about the ajax support in the given source format
+ *
+ * The returned object's property (boolean)capable indicates that
+ * the course format supports Moodle course ajax features.
+ * The property (array)testedbrowsers can be used as a parameter for {@link ajaxenabled()}.
+ *
+ * @return stdClass
+ */
+ public function supports_ajax() {
+ // no support by default
+ $ajaxsupport = new stdClass();
+ $ajaxsupport->capable = false;
+ $ajaxsupport->testedbrowsers = array();
+ return $ajaxsupport;
+ }
+
+ /**
+ * Custom action after section has been moved in AJAX mode
+ *
+ * Used in course/rest.php
+ *
+ * @return array This will be passed in ajax respose
+ */
+ public function ajax_section_move() {
+ return null;
+ }
+
+ /**
+ * The URL to use for the specified course (with section)
+ *
+ * Please note that course view page /course/view.php?id=COURSEID is hardcoded in many
+ * places in core and contributed modules. If course format wants to change the location
+ * of the view script, it is not enough to change just this function. Do not forget
+ * to add proper redirection.
+ *
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * if null the course view page is returned
+ * @param array $options options for view URL. At the moment core uses:
+ * 'navigation' (bool) if true and section has no separate page, the function returns null
+ * 'sr' (int) used by multipage formats to specify to which section to return
+ * @return null|moodle_url
+ */
+ public function get_view_url($section, $options = array()) {
+ $course = $this->get_course();
+ $url = new moodle_url('/course/view.php', array('id' => $course->id));
+
+ if (array_key_exists('sr', $options)) {
+ $sectionno = $options['sr'];
+ } else if (is_object($section)) {
+ $sectionno = $section->section;
+ } else {
+ $sectionno = $section;
+ }
+ if (!empty($options['navigation']) && $sectionno !== null) {
+ // by default assume that sections are never displayed on separate pages
+ return null;
+ }
+ if ($this->uses_sections() && $sectionno !== null) {
+ $url->set_anchor('section-'.$sectionno);
+ }
+ return $url;
+ }
+
+ /**
+ * Loads all of the course sections into the navigation
+ *
+ * This method is called from {@link global_navigation::load_course_sections()}
+ *
+ * By default the method {@link global_navigation::load_generic_course_sections()} is called
+ *
+ * When overwriting please note that navigationlib relies on using the correct values for
+ * arguments $type and $key in {@link navigation_node::add()}
+ *
+ * Example of code creating a section node:
+ * $sectionnode = $node->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
+ * $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
+ *
+ * Example of code creating an activity node:
+ * $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
+ * if (global_navigation::module_extends_navigation($activity->modname)) {
+ * $activitynode->nodetype = navigation_node::NODETYPE_BRANCH;
+ * } else {
+ * $activitynode->nodetype = navigation_node::NODETYPE_LEAF;
+ * }
+ *
+ * Also note that if $navigation->includesectionnum is not null, the section with this relative
+ * number needs is expected to be loaded
+ *
+ * @param global_navigation $navigation
+ * @param navigation_node $node The course node within the navigation
+ */
+ public function extend_course_navigation($navigation, navigation_node $node) {
+ if ($course = $this->get_course()) {
+ $navigation->load_generic_course_sections($course, $node);
+ }
+ return array();
+ }
+
+ /**
+ * Returns the list of blocks to be automatically added for the newly created course
+ *
+ * @see blocks_add_default_course_blocks()
+ *
+ * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
+ * each of values is an array of block names (for left and right side columns)
+ */
+ public function get_default_blocks() {
+ global $CFG;
+ if (!empty($CFG->defaultblocks)){
+ return blocks_parse_default_blocks_list($CFG->defaultblocks);
+ }
+ $blocknames = array(
+ BLOCK_POS_LEFT => array(),
+ BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
+ );
+ return $blocknames;
+ }
+
+ /**
+ * Returns the localised name of this course format plugin
+ *
+ * @return lang_string
+ */
+ public final function get_format_name() {
+ return new lang_string('pluginname', 'format_'.$this->get_format());
+ }
+
+ /**
+ * Definitions of the additional options that this course format uses for course
+ *
+ * This function may be called often, it should be as fast as possible.
+ * Avoid using get_string() method, use "new lang_string()" instead
+ * It is not recommended to use dynamic or course-dependant expressions here
+ * This function may be also called when course does not exist yet.
+ *
+ * Option names must be different from fields in the {course} talbe or any form elements on
+ * course edit form, it may even make sence to use special prefix for them.
+ *
+ * Each option must have the option name as a key and the array of properties as a value:
+ * 'default' - default value for this option (assumed null if not specified)
+ * 'type' - type of the option value (PARAM_INT, PARAM_RAW, etc.)
+ *
+ * Additional properties used by default implementation of
+ * {@link format_base::create_edit_form_elements()} (calls this method with $foreditform = true)
+ * 'label' - localised human-readable label for the edit form
+ * 'element_type' - type of the form element, default 'text'
+ * 'element_attributes' - additional attributes for the form element, these are 4th and further
+ * arguments in the moodleform::addElement() method
+ * 'help' - string for help button. Note that if 'help' value is 'myoption' then the string with
+ * the name 'myoption_help' must exist in the language file
+ * 'help_component' - language component to look for help string, by default this the component
+ * for this course format
+ *
+ * This is an interface for creating simple form elements. If format plugin wants to use other
+ * methods such as disableIf, it can be done by overriding create_edit_form_elements().
+ *
+ * Course format options can be accessed as:
+ * $this->get_course()->OPTIONNAME (inside the format class)
+ * course_get_format($course)->get_course()->OPTIONNAME (outside of format class)
+ *
+ * All course options are returned by calling:
+ * $this->get_format_options();
+ *
+ * @param bool $foreditform
+ * @return array of options
+ */
+ public function course_format_options($foreditform = false) {
+ return array();
+ }
+
+ /**
+ * Definitions of the additional options that this course format uses for section
+ *
+ * See {@link format_base::course_format_options()} for return array definition.
+ *
+ * Additionally section format options may have property 'cache' set to true
+ * if this option needs to be cached in {@link get_fast_modinfo()}. The 'cache' property
+ * is recommended to be set only for fields used in {@link format_base::get_section_name()},
+ * {@link format_base::extend_course_navigation()} and {@link format_base::get_view_url()}
+ *
+ * For better performance cached options are recommended to have 'cachedefault' property
+ * Unlike 'default', 'cachedefault' should be static and not access get_config().
+ *
+ * Regardless of value of 'cache' all options are accessed in the code as
+ * $sectioninfo->OPTIONNAME
+ * where $sectioninfo is instance of section_info, returned by
+ * get_fast_modinfo($course)->get_section_info($sectionnum)
+ * or get_fast_modinfo($course)->get_section_info_all()
+ *
+ * All format options for particular section are returned by calling:
+ * $this->get_format_options($section);
+ *
+ * @param bool $foreditform
+ * @return array
+ */
+ public function section_format_options($foreditform = false) {
+ return array();
+ }
+
+ /**
+ * Returns the format options stored for this course or course section
+ *
+ * When overriding please note that this function is called from rebuild_course_cache()
+ * and section_info object, therefore using of get_fast_modinfo() and/or any function that
+ * accesses it may lead to recursion.
+ *
+ * @param null|int|stdClass|section_info $section if null the course format options will be returned
+ * otherwise options for specified section will be returned. This can be either
+ * section object or relative section number (field course_sections.section)
+ * @return array
+ */
+ public function get_format_options($section = null) {
+ global $DB;
+ if ($section === null) {
+ $options = $this->course_format_options();
+ } else {
+ $options = $this->section_format_options();
+ }
+ if (empty($options)) {
+ // there are no option for course/sections anyway, no need to go further
+ return array();
+ }
+ if ($section === null) {
+ // course format options will be returned
+ $sectionid = 0;
+ } else if ($this->courseid && isset($section->id)) {
+ // course section format options will be returned
+ $sectionid = $section->id;
+ } else if ($this->courseid && is_int($section) &&
+ ($sectionobj = $DB->get_record('course_sections',
+ array('section' => $section, 'courseid' => $this->courseid), 'id'))) {
+ // course section format options will be returned
+ $sectionid = $sectionobj->id;
+ } else {
+ // non-existing (yet) section was passed as an argument
+ // default format options for course section will be returned
+ $sectionid = -1;
+ }
+ if (!array_key_exists($sectionid, $this->formatoptions)) {
+ $this->formatoptions[$sectionid] = array();
+ // first fill with default values
+ foreach ($options as $optionname => $optionparams) {
+ $this->formatoptions[$sectionid][$optionname] = null;
+ if (array_key_exists('default', $optionparams)) {
+ $this->formatoptions[$sectionid][$optionname] = $optionparams['default'];
+ }
+ }
+ if ($this->courseid && $sectionid !== -1) {
+ // overwrite the default options values with those stored in course_format_options table
+ // nothing can be stored if we are interested in generic course ($this->courseid == 0)
+ // or generic section ($sectionid === 0)
+ $records = $DB->get_records('course_format_options',
+ array('courseid' => $this->courseid,
+ 'format' => $this->format,
+ 'sectionid' => $sectionid
+ ), '', 'id,name,value');
+ foreach ($records as $record) {
+ if (array_key_exists($record->name, $this->formatoptions[$sectionid])) {
+ $value = $record->value;
+ if ($value !== null && isset($options[$record->name]['type'])) {
+ // this will convert string value to number if needed
+ $value = clean_param($value, $options[$record->name]['type']);
+ }
+ $this->formatoptions[$sectionid][$record->name] = $value;
+ }
+ }
+ }
+ }
+ return $this->formatoptions[$sectionid];
+ }
+
+ /**
+ * Adds format options elements to the course/section edit form
+ *
+ * This function is called from {@link course_edit_form::definition_after_data()}
+ *
+ * @param MoodleQuickForm $mform form the elements are added to
+ * @param bool $forsection 'true' if this is a section edit form, 'false' if this is course edit form
+ * @return array array of references to the added form elements
+ */
+ public function create_edit_form_elements(&$mform, $forsection = false) {
+ $elements = array();
+ if ($forsection) {
+ $options = $this->section_format_options(true);
+ } else {
+ $options = $this->course_format_options(true);
+ }
+ foreach ($options as $optionname => $option) {
+ if (!isset($option['element_type'])) {
+ $option['element_type'] = 'text';
+ }
+ $args = array($option['element_type'], $optionname, $option['label']);
+ if (!empty($option['element_attributes'])) {
+ $args = array_merge($args, $option['element_attributes']);
+ }
+ $elements[] = call_user_func_array(array($mform, 'addElement'), $args);
+ if (isset($option['help'])) {
+ $helpcomponent = 'format_'. $this->get_format();
+ if (isset($option['help_component'])) {
+ $helpcomponent = $option['help_component'];
+ }
+ $mform->addHelpButton($optionname, $option['help'], $helpcomponent);
+ }
+ if (isset($option['type'])) {
+ $mform->setType($optionname, $option['type']);
+ }
+ if (is_null($mform->getElementValue($optionname)) && isset($option['default'])) {
+ $mform->setDefault($optionname, $option['default']);
+ }
+ }
+ return $elements;
+ }
+
+ /**
+ * Override if you need to perform some extra validation of the format options
+ *
+ * @param array $data array of ("fieldname"=>value) of submitted data
+ * @param array $files array of uploaded files "element_name"=>tmp_file_path
+ * @param array $errors errors already discovered in edit form validation
+ * @return array of "element_name"=>"error_description" if there are errors,
+ * or an empty array if everything is OK.
+ * Do not repeat errors from $errors param here
+ */
+ public function edit_form_validation($data, $files, $errors) {
+ return array();
+ }
+
+ /**
+ * Updates format options for a course or section
+ *
+ * If $data does not contain property with the option name, the option will not be updated
+ *
+ * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
+ * @param null|int null if these are options for course or section id (course_sections.id)
+ * if these are options for section
+ * @return bool whether there were any changes to the options values
+ */
+ protected function update_format_options($data, $sectionid = null) {
+ global $DB;
+ if (!$sectionid) {
+ $allformatoptions = $this->course_format_options();
+ $sectionid = 0;
+ } else {
+ $allformatoptions = $this->section_format_options();
+ }
+ if (empty($allformatoptions)) {
+ // nothing to update anyway
+ return false;
+ }
+ $defaultoptions = array();
+ $cached = array();
+ foreach ($allformatoptions as $key => $option) {
+ $defaultoptions[$key] = null;
+ if (array_key_exists('default', $option)) {
+ $defaultoptions[$key] = $option['default'];
+ }
+ $cached[$key] = ($sectionid === 0 || !empty($option['cache']));
+ }
+ $records = $DB->get_records('course_format_options',
+ array('courseid' => $this->courseid,
+ 'format' => $this->format,
+ 'sectionid' => $sectionid
+ ), '', 'name,id,value');
+ $changed = $needrebuild = false;
+ $data = (array)$data;
+ foreach ($defaultoptions as $key => $value) {
+ if (isset($records[$key])) {
+ if (array_key_exists($key, $data) && $records[$key]->value !== $data[$key]) {
+ $DB->set_field('course_format_options', 'value',
+ $data[$key], array('id' => $records[$key]->id));
+ $changed = true;
+ $needrebuild = $needrebuild || $cached[$key];
+ }
+ } else {
+ if (array_key_exists($key, $data) && $data[$key] !== $value) {
+ $newvalue = $data[$key];
+ $changed = true;
+ $needrebuild = $needrebuild || $cached[$key];
+ } else {
+ $newvalue = $value;
+ // we still insert entry in DB but there are no changes from user point of
+ // view and no need to call rebuild_course_cache()
+ }
+ $DB->insert_record('course_format_options', array(
+ 'courseid' => $this->courseid,
+ 'format' => $this->format,
+ 'sectionid' => $sectionid,
+ 'name' => $key,
+ 'value' => $newvalue
+ ));
+ }
+ }
+ if ($needrebuild) {
+ rebuild_course_cache($this->courseid, true);
+ }
+ if ($changed) {
+ // reset internal caches
+ if (!$sectionid) {
+ $this->course = false;
+ }
+ unset($this->formatoptions[$sectionid]);
+ }
+ return $changed;
+ }
+
+ /**
+ * Updates format options for a course
+ *
+ * If $data does not contain property with the option name, the option will not be updated
+ *
+ * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
+ * @param stdClass $oldcourse if this function is called from {@link update_course()}
+ * this object contains information about the course before update
+ * @return bool whether there were any changes to the options values
+ */
+ public function update_course_format_options($data, $oldcourse = null) {
+ return $this->update_format_options($data);
+ }
+
+ /**
+ * Updates format options for a section
+ *
+ * Section id is expected in $data->id (or $data['id'])
+ * If $data does not contain property with the option name, the option will not be updated
+ *
+ * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
+ * @return bool whether there were any changes to the options values
+ */
+ public function update_section_format_options($data) {
+ $data = (array)$data;
+ return $this->update_format_options($data, $data['id']);
+ }
+
+ /**
+ * Return an instance of moodleform to edit a specified section
+ *
+ * Default implementation returns instance of editsection_form that automatically adds
+ * additional fields defined in {@link format_base::section_format_options()}
+ *
+ * Format plugins may extend editsection_form if they want to have custom edit section form.
+ *
+ * @param mixed $action the action attribute for the form. If empty defaults to auto detect the
+ * current url. If a moodle_url object then outputs params as hidden variables.
+ * @param array $customdata the array with custom data to be passed to the form
+ * /course/editsection.php passes section_info object in 'cs' field
+ * for filling availability fields
+ * @return moodleform
+ */
+ public function editsection_form($action, $customdata = array()) {
+ global $CFG;
+ require_once($CFG->dirroot. '/course/editsection_form.php');
+ $context = context_course::instance($this->courseid);
+ if (!array_key_exists('course', $customdata)) {
+ $customdata['course'] = $this->get_course();
+ }
+ return new editsection_form($action, $customdata);
+ }
+
+ /**
+ * Allows course format to execute code on moodle_page::set_course()
+ *
+ * @param moodle_page $page instance of page calling set_course
+ */
+ public function page_set_course(moodle_page $page) {
+ }
+
+ /**
+ * Allows course format to execute code on moodle_page::set_cm()
+ *
+ * Current module can be accessed as $page->cm (returns instance of cm_info)
+ *
+ * @param moodle_page $page instance of page calling set_cm
+ */
+ public function page_set_cm(moodle_page $page) {
+ }
+
+ /**
+ * Course-specific information to be output on any course page (usually above navigation bar)
+ *
+ * Example of usage:
+ * define
+ * class format_FORMATNAME_XXX implements renderable {}
+ *
+ * create format renderer in course/format/FORMATNAME/renderer.php, define rendering function:
+ * class format_FORMATNAME_renderer extends plugin_renderer_base {
+ * protected function render_format_FORMATNAME_XXX(format_FORMATNAME_XXX $xxx) {
+ * return html_writer::tag('div', 'This is my header/footer');
+ * }
+ * }
+ *
+ * Return instance of format_FORMATNAME_XXX in this function, the appropriate method from
+ * plugin renderer will be called
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_header() {
+ return null;
+ }
+
+ /**
+ * Course-specific information to be output on any course page (usually in the beginning of
+ * standard footer)
+ *
+ * See {@link format_base::course_header()} for usage
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_footer() {
+ return null;
+ }
+
+ /**
+ * Course-specific information to be output immediately above content on any course page
+ *
+ * See {@link format_base::course_header()} for usage
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_content_header() {
+ return null;
+ }
+
+ /**
+ * Course-specific information to be output immediately below content on any course page
+ *
+ * See {@link format_base::course_header()} for usage
+ *
+ * @return null|renderable null for no output or object with data for plugin renderer
+ */
+ public function course_content_footer() {
+ return null;
+ }
+
+ /**
+ * Returns instance of page renderer used by this plugin
+ *
+ * @param moodle_page $page
+ * @return renderer_base
+ */
+ public function get_renderer(moodle_page $page) {
+ return $page->get_renderer('format_'. $this->get_format());
+ }
+
+ /**
+ * Returns true if the specified section is current
+ *
+ * By default we analyze $course->marker
+ *
+ * @param int|stdClass|section_info $section
+ * @return bool
+ */
+ public function is_section_current($section) {
+ if (is_object($section)) {
+ $sectionnum = $section->section;
+ } else {
+ $sectionnum = $section;
+ }
+ return ($sectionnum && ($course = $this->get_course()) && $course->marker == $sectionnum);
+ }
+}
+
+/**
+ * Pseudo course format used for the site main page
+ *
+ * @package core_course
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_site extends format_base {
+
+ /**
+ * Returns the display name of the given section that the course prefers.
+ *
+ * @param int|stdClass $section Section object from database or just field section.section
+ * @return Display name that the course format prefers, e.g. "Topic 2"
+ */
+ function get_section_name($section) {
+ return get_string('site');
+ }
+
+ /**
+ * For this fake course referring to the whole site, the site homepage is always returned
+ * regardless of arguments
+ *
+ * @param int|stdClass $section
+ * @param array $options
+ * @return null|moodle_url
+ */
+ public function get_view_url($section, $options = array()) {
+ return new moodle_url('/');
+ }
+
+ /**
+ * Returns the list of blocks to be automatically added on the site frontpage when moodle is installed
+ *
+ * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
+ * each of values is an array of block names (for left and right side columns)
+ */
+ public function get_default_blocks() {
+ return blocks_get_default_site_course_blocks();
+ }
+
+ /**
+ * Definitions of the additional options that site uses
+ *
+ * @param bool $foreditform
+ * @return array of options
+ */
+ public function course_format_options($foreditform = false) {
+ static $courseformatoptions = false;
+ if ($courseformatoptions === false) {
+ $courseformatoptions = array(
+ 'numsections' => array(
+ 'default' => 1,
+ 'type' => PARAM_INT,
+ ),
+ );
+ }
+ return $courseformatoptions;
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Base renderer for outputting course formats.
+ *
+ * @package core
+ * @copyright 2012 Dan Poltawski
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.3
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * This is a convenience renderer which can be used by section based formats
+ * to reduce code duplication. It is not necessary for all course formats to
+ * use this and its likely to change in future releases.
+ *
+ * @package core
+ * @copyright 2012 Dan Poltawski
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.3
+ */
+abstract class format_section_renderer_base extends plugin_renderer_base {
+
+ /**
+ * Generate the starting container html for a list of sections
+ * @return string HTML to output.
+ */
+ abstract protected function start_section_list();
+
+ /**
+ * Generate the closing container html for a list of sections
+ * @return string HTML to output.
+ */
+ abstract protected function end_section_list();
+
+ /**
+ * Generate the title for this section page
+ * @return string the page title
+ */
+ abstract protected function page_title();
+
+ /**
+ * Generate the section title
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @param stdClass $course The course entry from DB
+ * @return string HTML to output.
+ */
+ public function section_title($section, $course) {
+ $title = get_section_name($course, $section);
+ $url = course_get_url($course, $section->section, array('navigation' => true));
+ if ($url) {
+ $title = html_writer::link($url, $title);
+ }
+ return $title;
+ }
+
+ /**
+ * Generate the content to displayed on the right part of a section
+ * before course modules are included
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @param stdClass $course The course entry from DB
+ * @param bool $onsectionpage true if being printed on a section page
+ * @return string HTML to output.
+ */
+ protected function section_right_content($section, $course, $onsectionpage) {
+ $o = $this->output->spacer();
+
+ if ($section->section != 0) {
+ $controls = $this->section_edit_controls($course, $section, $onsectionpage);
+ if (!empty($controls)) {
+ $o = implode('<br />', $controls);
+ }
+ }
+
+ return $o;
+ }
+
+ /**
+ * Generate the content to displayed on the left part of a section
+ * before course modules are included
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @param stdClass $course The course entry from DB
+ * @param bool $onsectionpage true if being printed on a section page
+ * @return string HTML to output.
+ */
+ protected function section_left_content($section, $course, $onsectionpage) {
+ $o = $this->output->spacer();
+
+ if ($section->section != 0) {
+ // Only in the non-general sections.
+ if (course_get_format($course)->is_section_current($section)) {
+ $o = get_accesshide(get_string('currentsection', 'format_'.$course->format));
+ }
+ }
+
+ return $o;
+ }
+
+ /**
+ * Generate the display of the header part of a section before
+ * course modules are included
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @param stdClass $course The course entry from DB
+ * @param bool $onsectionpage true if being printed on a single-section page
+ * @param int $sectionreturn The section to return to after an action
+ * @return string HTML to output.
+ */
+ protected function section_header($section, $course, $onsectionpage, $sectionreturn=null) {
+ global $PAGE;
+
+ $o = '';
+ $currenttext = '';
+ $sectionstyle = '';
+
+ if ($section->section != 0) {
+ // Only in the non-general sections.
+ if (!$section->visible) {
+ $sectionstyle = ' hidden';
+ } else if (course_get_format($course)->is_section_current($section)) {
+ $sectionstyle = ' current';
+ }
+ }
+
+ $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section,
+ 'class' => 'section main clearfix'.$sectionstyle));
+
+ $leftcontent = $this->section_left_content($section, $course, $onsectionpage);
+ $o.= html_writer::tag('div', $leftcontent, array('class' => 'left side'));
+
+ $rightcontent = $this->section_right_content($section, $course, $onsectionpage);
+ $o.= html_writer::tag('div', $rightcontent, array('class' => 'right side'));
+ $o.= html_writer::start_tag('div', array('class' => 'content'));
+
+ // When not on a section page, we display the section titles except the general section if null
+ $hasnamenotsecpg = (!$onsectionpage && ($section->section != 0 || !is_null($section->name)));
+
+ // When on a section page, we only display the general section title, if title is not the default one
+ $hasnamesecpg = ($onsectionpage && ($section->section == 0 && !is_null($section->name)));
+
+ if ($hasnamenotsecpg || $hasnamesecpg) {
+ $o.= $this->output->heading($this->section_title($section, $course), 3, 'sectionname');
+ }
+
+ $o.= html_writer::start_tag('div', array('class' => 'summary'));
+ $o.= $this->format_summary_text($section);
+
+ $context = context_course::instance($course->id);
+ if ($PAGE->user_is_editing() && has_capability('moodle/course:update', $context)) {
+ $url = new moodle_url('/course/editsection.php', array('id'=>$section->id, 'sr'=>$sectionreturn));
+ $o.= html_writer::link($url,
+ html_writer::empty_tag('img', array('src' => $this->output->pix_url('t/edit'),
+ 'class' => 'iconsmall edit', 'alt' => get_string('edit'))),
+ array('title' => get_string('editsummary')));
+ }
+ $o.= html_writer::end_tag('div');
+
+ $o .= $this->section_availability_message($section,
+ has_capability('moodle/course:viewhiddensections', $context));
+
+ return $o;
+ }
+
+ /**
+ * Generate the display of the footer part of a section
+ *
+ * @return string HTML to output.
+ */
+ protected function section_footer() {
+ $o = html_writer::end_tag('div');
+ $o.= html_writer::end_tag('li');
+
+ return $o;
+ }
+
+ /**
+ * Generate the edit controls of a section
+ *
+ * @param stdClass $course The course entry from DB
+ * @param stdClass $section The course_section entry from DB
+ * @param bool $onsectionpage true if being printed on a section page
+ * @return array of links with edit controls
+ */
+ protected function section_edit_controls($course, $section, $onsectionpage = false) {
+ global $PAGE;
+
+ if (!$PAGE->user_is_editing()) {
+ return array();
+ }
+
+ $coursecontext = context_course::instance($course->id);
+
+ if ($onsectionpage) {
+ $baseurl = course_get_url($course, $section->section);
+ } else {
+ $baseurl = course_get_url($course);
+ }
+ $baseurl->param('sesskey', sesskey());
+
+ $controls = array();
+
+ $url = clone($baseurl);
+ if (has_capability('moodle/course:sectionvisibility', $coursecontext)) {
+ if ($section->visible) { // Show the hide/show eye.
+ $strhidefromothers = get_string('hidefromothers', 'format_'.$course->format);
+ $url->param('hide', $section->section);
+ $controls[] = html_writer::link($url,
+ html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/hide'),
+ 'class' => 'icon hide', 'alt' => $strhidefromothers)),
+ array('title' => $strhidefromothers, 'class' => 'editing_showhide'));
+ } else {
+ $strshowfromothers = get_string('showfromothers', 'format_'.$course->format);
+ $url->param('show', $section->section);
+ $controls[] = html_writer::link($url,
+ html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/show'),
+ 'class' => 'icon hide', 'alt' => $strshowfromothers)),
+ array('title' => $strshowfromothers, 'class' => 'editing_showhide'));
+ }
+ }
+
+ if (!$onsectionpage && has_capability('moodle/course:movesections', $coursecontext)) {
+ $url = clone($baseurl);
+ if ($section->section > 1) { // Add a arrow to move section up.
+ $url->param('section', $section->section);
+ $url->param('move', -1);
+ $strmoveup = get_string('moveup');
+
+ $controls[] = html_writer::link($url,
+ html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/up'),
+ 'class' => 'icon up', 'alt' => $strmoveup)),
+ array('title' => $strmoveup, 'class' => 'moveup'));
+ }
+
+ $url = clone($baseurl);
+ if ($section->section < $course->numsections) { // Add a arrow to move section down.
+ $url->param('section', $section->section);
+ $url->param('move', 1);
+ $strmovedown = get_string('movedown');
+
+ $controls[] = html_writer::link($url,
+ html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/down'),
+ 'class' => 'icon down', 'alt' => $strmovedown)),
+ array('title' => $strmovedown, 'class' => 'movedown'));
+ }
+ }
+
+ return $controls;
+ }
+
+ /**
+ * Generate a summary of a section for display on the 'coruse index page'
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @param stdClass $course The course entry from DB
+ * @param array $mods (argument not used)
+ * @return string HTML to output.
+ */
+ protected function section_summary($section, $course, $mods) {
+ $classattr = 'section main section-summary clearfix';
+ $linkclasses = '';
+
+ // If section is hidden then display grey section link
+ if (!$section->visible) {
+ $classattr .= ' hidden';
+ $linkclasses .= ' dimmed_text';
+ } else if (course_get_format($course)->is_section_current($section)) {
+ $classattr .= ' current';
+ }
+
+ $o = '';
+ $o .= html_writer::start_tag('li', array('id' => 'section-'.$section->section, 'class' => $classattr));
+
+ $o .= html_writer::tag('div', '', array('class' => 'left side'));
+ $o .= html_writer::tag('div', '', array('class' => 'right side'));
+ $o .= html_writer::start_tag('div', array('class' => 'content'));
+
+ $title = get_section_name($course, $section);
+ if ($section->uservisible) {
+ $title = html_writer::tag('a', $title,
+ array('href' => course_get_url($course, $section->section), 'class' => $linkclasses));
+ }
+ $o .= $this->output->heading($title, 3, 'section-title');
+
+ $o.= html_writer::start_tag('div', array('class' => 'summarytext'));
+ $o.= $this->format_summary_text($section);
+ $o.= html_writer::end_tag('div');
+ $o.= $this->section_activity_summary($section, $course, null);
+
+ $context = context_course::instance($course->id);
+ $o .= $this->section_availability_message($section,
+ has_capability('moodle/course:viewhiddensections', $context));
+
+ $o .= html_writer::end_tag('div');
+ $o .= html_writer::end_tag('li');
+
+ return $o;
+ }
+
+ /**
+ * Generate a summary of the activites in a section
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @param stdClass $course the course record from DB
+ * @param array $mods (argument not used)
+ * @return string HTML to output.
+ */
+ private function section_activity_summary($section, $course, $mods) {
+ $modinfo = get_fast_modinfo($course);
+ if (empty($modinfo->sections[$section->section])) {
+ return '';
+ }
+
+ // Generate array with count of activities in this section:
+ $sectionmods = array();
+ $total = 0;
+ $complete = 0;
+ $cancomplete = isloggedin() && !isguestuser();
+ $completioninfo = new completion_info($course);
+ foreach ($modinfo->sections[$section->section] as $cmid) {
+ $thismod = $modinfo->cms[$cmid];
+
+ if ($thismod->modname == 'label') {
+ // Labels are special (not interesting for students)!
+ continue;
+ }
+
+ if ($thismod->uservisible) {
+ if (isset($sectionmods[$thismod->modname])) {
+ $sectionmods[$thismod->modname]['name'] = $thismod->modplural;
+ $sectionmods[$thismod->modname]['count']++;
+ } else {
+ $sectionmods[$thismod->modname]['name'] = $thismod->modfullname;
+ $sectionmods[$thismod->modname]['count'] = 1;
+ }
+ if ($cancomplete && $completioninfo->is_enabled($thismod) != COMPLETION_TRACKING_NONE) {
+ $total++;
+ $completiondata = $completioninfo->get_data($thismod, true);
+ if ($completiondata->completionstate == COMPLETION_COMPLETE) {
+ $complete++;
+ }
+ }
+ }
+ }
+
+ if (empty($sectionmods)) {
+ // No sections
+ return '';
+ }
+
+ // Output section activities summary:
+ $o = '';
+ $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities mdl-right'));
+ foreach ($sectionmods as $mod) {
+ $o.= html_writer::start_tag('span', array('class' => 'activity-count'));
+ $o.= $mod['name'].': '.$mod['count'];
+ $o.= html_writer::end_tag('span');
+ }
+ $o.= html_writer::end_tag('div');
+
+ // Output section completion data
+ if ($total > 0) {
+ $a = new stdClass;
+ $a->complete = $complete;
+ $a->total = $total;
+
+ $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities mdl-right'));
+ $o.= html_writer::tag('span', get_string('progresstotal', 'completion', $a), array('class' => 'activity-count'));
+ $o.= html_writer::end_tag('div');
+ }
+
+ return $o;
+ }
+
+ /**
+ * If section is not visible, display the message about that ('Not available
+ * until...', that sort of thing). Otherwise, returns blank.
+ *
+ * For users with the ability to view hidden sections, it shows the
+ * information even though you can view the section and also may include
+ * slightly fuller information (so that teachers can tell when sections
+ * are going to be unavailable etc). This logic is the same as for
+ * activities.
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @param bool $canviewhidden True if user can view hidden sections
+ * @return string HTML to output
+ */
+ protected function section_availability_message($section, $canviewhidden) {
+ global $CFG;
+ $o = '';
+ if (!$section->uservisible) {
+ $o .= html_writer::start_tag('div', array('class' => 'availabilityinfo'));
+ // Note: We only get to this function if availableinfo is non-empty,
+ // so there is definitely something to print.
+ $o .= $section->availableinfo;
+ $o .= html_writer::end_tag('div');
+ } else if ($canviewhidden && !empty($CFG->enableavailability) && $section->visible) {
+ $ci = new condition_info_section($section);
+ $fullinfo = $ci->get_full_information();
+ if ($fullinfo) {
+ $o .= html_writer::start_tag('div', array('class' => 'availabilityinfo'));
+ $o .= get_string(
+ ($section->showavailability ? 'userrestriction_visible' : 'userrestriction_hidden'),
+ 'condition', $fullinfo);
+ $o .= html_writer::end_tag('div');
+ }
+ }
+ return $o;
+ }
+
+ /**
+ * Show if something is on on the course clipboard (moving around)
+ *
+ * @param stdClass $course The course entry from DB
+ * @param int $sectionno The section number in the coruse which is being dsiplayed
+ * @return string HTML to output.
+ */
+ protected function course_activity_clipboard($course, $sectionno = null) {
+ global $USER;
+
+ $o = '';
+ // If currently moving a file then show the current clipboard.
+ if (ismoving($course->id)) {
+ $url = new moodle_url('/course/mod.php',
+ array('sesskey' => sesskey(),
+ 'cancelcopy' => true,
+ 'sr' => $sectionno,
+ )
+ );
+
+ $o.= html_writer::start_tag('div', array('class' => 'clipboard'));
+ $o.= strip_tags(get_string('activityclipboard', '', $USER->activitycopyname));
+ $o.= ' ('.html_writer::link($url, get_string('cancel')).')';
+ $o.= html_writer::end_tag('div');
+ }
+
+ return $o;
+ }
+
+ /**
+ * Generate next/previous section links for naviation
+ *
+ * @param stdClass $course The course entry from DB
+ * @param array $sections The course_sections entries from the DB
+ * @param int $sectionno The section number in the coruse which is being dsiplayed
+ * @return array associative array with previous and next section link
+ */
+ protected function get_nav_links($course, $sections, $sectionno) {
+ // FIXME: This is really evil and should by using the navigation API.
+ $course = course_get_format($course)->get_course();
+ $canviewhidden = has_capability('moodle/course:viewhiddensections', context_course::instance($course->id))
+ or !$course->hiddensections;
+
+ $links = array('previous' => '', 'next' => '');
+ $back = $sectionno - 1;
+ while ($back > 0 and empty($links['previous'])) {
+ if ($canviewhidden || $sections[$back]->uservisible) {
+ $params = array();
+ if (!$sections[$back]->visible) {
+ $params = array('class' => 'dimmed_text');
+ }
+ $previouslink = html_writer::tag('span', $this->output->larrow(), array('class' => 'larrow'));
+ $previouslink .= get_section_name($course, $sections[$back]);
+ $links['previous'] = html_writer::link(course_get_url($course, $back), $previouslink, $params);
+ }
+ $back--;
+ }
+
+ $forward = $sectionno + 1;
+ while ($forward <= $course->numsections and empty($links['next'])) {
+ if ($canviewhidden || $sections[$forward]->uservisible) {
+ $params = array();
+ if (!$sections[$forward]->visible) {
+ $params = array('class' => 'dimmed_text');
+ }
+ $nextlink = get_section_name($course, $sections[$forward]);
+ $nextlink .= html_writer::tag('span', $this->output->rarrow(), array('class' => 'rarrow'));
+ $links['next'] = html_writer::link(course_get_url($course, $forward), $nextlink, $params);
+ }
+ $forward++;
+ }
+
+ return $links;
+ }
+
+ /**
+ * Generate the header html of a stealth section
+ *
+ * @param int $sectionno The section number in the coruse which is being dsiplayed
+ * @return string HTML to output.
+ */
+ protected function stealth_section_header($sectionno) {
+ $o = '';
+ $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix orphaned hidden'));
+ $o.= html_writer::tag('div', '', array('class' => 'left side'));
+ $o.= html_writer::tag('div', '', array('class' => 'right side'));
+ $o.= html_writer::start_tag('div', array('class' => 'content'));
+ $o.= $this->output->heading(get_string('orphanedactivities'), 3, 'sectionname');
+ return $o;
+ }
+
+ /**
+ * Generate footer html of a stealth section
+ *
+ * @return string HTML to output.
+ */
+ protected function stealth_section_footer() {
+ $o = html_writer::end_tag('div');
+ $o.= html_writer::end_tag('li');
+ return $o;
+ }
+
+ /**
+ * Generate the html for a hidden section
+ *
+ * @param int $sectionno The section number in the coruse which is being dsiplayed
+ * @return string HTML to output.
+ */
+ protected function section_hidden($sectionno) {
+ $o = '';
+ $o.= html_writer::start_tag('li', array('id' => 'section-'.$sectionno, 'class' => 'section main clearfix hidden'));
+ $o.= html_writer::tag('div', '', array('class' => 'left side'));
+ $o.= html_writer::tag('div', '', array('class' => 'right side'));
+ $o.= html_writer::start_tag('div', array('class' => 'content'));
+ $o.= get_string('notavailable');
+ $o.= html_writer::end_tag('div');
+ $o.= html_writer::end_tag('li');
+ return $o;
+ }
+
+ /**
+ * Output the html for a single section page .
+ *
+ * @param stdClass $course The course entry from DB
+ * @param array $sections (argument not used)
+ * @param array $mods (argument not used)
+ * @param array $modnames (argument not used)
+ * @param array $modnamesused (argument not used)
+ * @param int $displaysection The section number in the course which is being displayed
+ */
+ public function print_single_section_page($course, $sections, $mods, $modnames, $modnamesused, $displaysection) {
+ global $PAGE;
+
+ $modinfo = get_fast_modinfo($course);
+ $course = course_get_format($course)->get_course();
+
+ // Can we view the section in question?
+ if (!($sectioninfo = $modinfo->get_section_info($displaysection))) {
+ // This section doesn't exist
+ print_error('unknowncoursesection', 'error', null, $course->fullname);
+ return;
+ }
+
+ if (!$sectioninfo->uservisible) {
+ if (!$course->hiddensections) {
+ echo $this->start_section_list();
+ echo $this->section_hidden($displaysection);
+ echo $this->end_section_list();
+ }
+ // Can't view this section.
+ return;
+ }
+
+ // Copy activity clipboard..
+ echo $this->course_activity_clipboard($course, $displaysection);
+ $thissection = $modinfo->get_section_info(0);
+ if ($thissection->summary or !empty($modinfo->sections[0]) or $PAGE->user_is_editing()) {
+ echo $this->start_section_list();
+ echo $this->section_header($thissection, $course, true, $displaysection);
+ print_section($course, $thissection, null, null, true, "100%", false, $displaysection);
+ if ($PAGE->user_is_editing()) {
+ print_section_add_menus($course, 0, null, false, false, $displaysection);
+ }
+ echo $this->section_footer();
+ echo $this->end_section_list();
+ }
+
+ // Start single-section div
+ echo html_writer::start_tag('div', array('class' => 'single-section'));
+
+ // The requested section page.
+ $thissection = $modinfo->get_section_info($displaysection);
+
+ // Title with section navigation links.
+ $sectionnavlinks = $this->get_nav_links($course, $modinfo->get_section_info_all(), $displaysection);
+ $sectiontitle = '';
+ $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation header headingblock'));
+ $sectiontitle .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
+ $sectiontitle .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
+ // Title attributes
+ $titleattr = 'mdl-align title';
+ if (!$thissection->visible) {
+ $titleattr .= ' dimmed_text';
+ }
+ $sectiontitle .= html_writer::tag('div', get_section_name($course, $displaysection), array('class' => $titleattr));
+ $sectiontitle .= html_writer::end_tag('div');
+ echo $sectiontitle;
+
+ // Now the list of sections..
+ echo $this->start_section_list();
+
+ echo $this->section_header($thissection, $course, true, $displaysection);
+ // Show completion help icon.
+ $completioninfo = new completion_info($course);
+ echo $completioninfo->display_help_icon();
+
+ print_section($course, $thissection, null, null, true, '100%', false, $displaysection);
+ if ($PAGE->user_is_editing()) {
+ print_section_add_menus($course, $displaysection, null, false, false, $displaysection);
+ }
+ echo $this->section_footer();
+ echo $this->end_section_list();
+
+ // Display section bottom navigation.
+ $courselink = html_writer::link(course_get_url($course), get_string('returntomaincoursepage'));
+ $sectionbottomnav = '';
+ $sectionbottomnav .= html_writer::start_tag('div', array('class' => 'section-navigation mdl-bottom'));
+ $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
+ $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
+ $sectionbottomnav .= html_writer::tag('div', $courselink, array('class' => 'mdl-align'));
+ $sectionbottomnav .= html_writer::end_tag('div');
+ echo $sectionbottomnav;
+
+ // close single-section div.
+ echo html_writer::end_tag('div');
+ }
+
+ /**
+ * Output the html for a multiple section page
+ *
+ * @param stdClass $course The course entry from DB
+ * @param array $sections (argument not used)
+ * @param array $mods (argument not used)
+ * @param array $modnames (argument not used)
+ * @param array $modnamesused (argument not used)
+ */
+ public function print_multiple_section_page($course, $sections, $mods, $modnames, $modnamesused) {
+ global $PAGE;
+
+ $modinfo = get_fast_modinfo($course);
+ $course = course_get_format($course)->get_course();
+
+ $context = context_course::instance($course->id);
+ // Title with completion help icon.
+ $completioninfo = new completion_info($course);
+ echo $completioninfo->display_help_icon();
+ echo $this->output->heading($this->page_title(), 2, 'accesshide');
+
+ // Copy activity clipboard..
+ echo $this->course_activity_clipboard($course, 0);
+
+ // Now the list of sections..
+ echo $this->start_section_list();
+
+ foreach ($modinfo->get_section_info_all() as $section => $thissection) {
+ if ($section == 0) {
+ // 0-section is displayed a little different then the others
+ if ($thissection->summary or !empty($modinfo->sections[0]) or $PAGE->user_is_editing()) {
+ echo $this->section_header($thissection, $course, false, 0);
+ print_section($course, $thissection, null, null, true, "100%", false, 0);
+ if ($PAGE->user_is_editing()) {
+ print_section_add_menus($course, 0, null, false, false, 0);
+ }
+ echo $this->section_footer();
+ }
+ continue;
+ }
+ if ($section > $course->numsections) {
+ // activities inside this section are 'orphaned', this section will be printed as 'stealth' below
+ continue;
+ }
+ // Show the section if the user is permitted to access it, OR if it's not available
+ // but showavailability is turned on (and there is some available info text).
+ $showsection = $thissection->uservisible ||
+ ($thissection->visible && !$thissection->available && $thissection->showavailability
+ && !empty($thissection->availableinfo));
+ if (!$showsection) {
+ // Hidden section message is overridden by 'unavailable' control
+ // (showavailability option).
+ if (!$course->hiddensections && $thissection->available) {
+ echo $this->section_hidden($section);
+ }
+
+ continue;
+ }
+
+ if (!$PAGE->user_is_editing() && $course->coursedisplay == COURSE_DISPLAY_MULTIPAGE) {
+ // Display section summary only.
+ echo $this->section_summary($thissection, $course, null);
+ } else {
+ echo $this->section_header($thissection, $course, false, 0);
+ if ($thissection->uservisible) {
+ print_section($course, $thissection, null, null, true, "100%", false, 0);
+ if ($PAGE->user_is_editing()) {
+ print_section_add_menus($course, $section, null, false, false, 0);
+ }
+ }
+ echo $this->section_footer();
+ }
+ }
+
+ if ($PAGE->user_is_editing() and has_capability('moodle/course:update', $context)) {
+ // Print stealth sections if present.
+ foreach ($modinfo->get_section_info_all() as $section => $thissection) {
+ if ($section <= $course->numsections or empty($modinfo->sections[$section])) {
+ // this is not stealth section or it is empty
+ continue;
+ }
+ echo $this->stealth_section_header($section);
+ print_section($course, $thissection, null, null, true, "100%", false, 0);
+ echo $this->stealth_section_footer();
+ }
+
+ echo $this->end_section_list();
+
+ echo html_writer::start_tag('div', array('id' => 'changenumsections', 'class' => 'mdl-right'));
+
+ // Increase number of sections.
+ $straddsection = get_string('increasesections', 'moodle');
+ $url = new moodle_url('/course/changenumsections.php',
+ array('courseid' => $course->id,
+ 'increase' => true,
+ 'sesskey' => sesskey()));
+ $icon = $this->output->pix_icon('t/switch_plus', $straddsection);
+ echo html_writer::link($url, $icon.get_accesshide($straddsection), array('class' => 'increase-sections'));
+
+ if ($course->numsections > 0) {
+ // Reduce number of sections sections.
+ $strremovesection = get_string('reducesections', 'moodle');
+ $url = new moodle_url('/course/changenumsections.php',
+ array('courseid' => $course->id,
+ 'increase' => false,
+ 'sesskey' => sesskey()));
+ $icon = $this->output->pix_icon('t/switch_minus', $strremovesection);
+ echo html_writer::link($url, $icon.get_accesshide($strremovesection), array('class' => 'reduce-sections'));
+ }
+
+ echo html_writer::end_tag('div');
+ } else {
+ echo $this->end_section_list();
+ }
+
+ }
+
+ /**
+ * Generate html for a section summary text
+ *
+ * @param stdClass $section The course_section entry from DB
+ * @return string HTML to output.
+ */
+ protected function format_summary_text($section) {
+ $context = context_course::instance($section->course);
+ $summarytext = file_rewrite_pluginfile_urls($section->summary, 'pluginfile.php',
+ $context->id, 'course', 'section', $section->id);
+
+ $options = new stdClass();
+ $options->noclean = true;
+ $options->overflowdiv = true;
+ return format_text($summarytext, $section->summaryformat, $options);
+ }
+
+ /**
+ * Is the section passed in the current section?
+ *
+ * @deprecated since 2.4
+ * @see format_base::is_section_current()
+ *
+ * @param stdClass $course The course entry from DB
+ * @param stdClass $section The course_section entry from the DB
+ * @return bool true if the section is current
+ */
+ protected final function is_section_current($section, $course) {
+ debugging('Function format_section_renderer_base::is_section_current() is deprecated. '.
+ 'Use course_get_format($course)->is_section_current($section) instead', DEBUG_DEVELOPER);
+ return course_get_format($course)->is_section_current($section);
+ }
+}
--- /dev/null
+<?php
+ // format.php - course format featuring single activity
+ // included from view.php
+
+ $module = $course->format;
+ require_once($CFG->dirroot.'/mod/'.$module.'/locallib.php');
+
+ $strgroups = get_string('groups');
+ $strgroupmy = get_string('groupmy');
+ $editing = $PAGE->user_is_editing();
+
+ $moduleformat = $module.'_course_format_display';
+ if (function_exists($moduleformat)) {
+ $moduleformat($USER,$course);
+ } else {
+ echo $OUTPUT->notification('The module '. $module. ' does not support single activity course format');
+ }
--- /dev/null
+<?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/>.
+
+/**
+ * Strings for component 'format_scorm', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package format_scorm
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['sectionname'] = 'SCORM';
+$string['pluginname'] = 'SCORM format';
--- /dev/null
+<?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/>.
+
+/**
+ * This file contains main class for the course format SCORM
+ *
+ * @since 2.0
+ * @package format_scorm
+ * @copyright 2009 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot. '/course/format/lib.php');
+
+/**
+ * Main class for the Scorm course format
+ *
+ * @package format_scorm
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_scorm extends format_base {
+
+ /**
+ * The URL to use for the specified course
+ *
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * if null the course view page is returned
+ * @param array $options options for view URL. At the moment core uses:
+ * 'navigation' (bool) if true and section has no separate page, the function returns null
+ * 'sr' (int) used by multipage formats to specify to which section to return
+ * @return null|moodle_url
+ */
+ public function get_view_url($section, $options = array()) {
+ if (!empty($options['navigation']) && $section !== null) {
+ return null;
+ }
+ return new moodle_url('/course/view.php', array('id' => $this->courseid));
+ }
+
+ /**
+ * Loads all of the course sections into the navigation
+ *
+ * @param global_navigation $navigation
+ * @param navigation_node $node The course node within the navigation
+ */
+ public function extend_course_navigation($navigation, navigation_node $node) {
+ // Scorm course format does not extend course navigation
+ }
+
+ /**
+ * Returns the list of blocks to be automatically added for the newly created course
+ *
+ * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
+ * each of values is an array of block names (for left and right side columns)
+ */
+ public function get_default_blocks() {
+ return array(
+ BLOCK_POS_LEFT => array(),
+ BLOCK_POS_RIGHT => array('news_items', 'recent_activity', 'calendar_upcoming')
+ );
+ }
+
+ /**
+ * Allows course format to execute code on moodle_page::set_course()
+ *
+ * If user is on course view page and there is no scorm module added to the course
+ * and the user has 'moodle/course:update' capability, redirect to create module
+ * form. This function is executed before the output starts
+ *
+ * @param moodle_page $page instance of page calling set_course
+ */
+ public function page_set_course(moodle_page $page) {
+ global $PAGE;
+ if ($PAGE == $page && $page->has_set_url() &&
+ $page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
+ $modinfo = get_fast_modinfo($this->courseid);
+ if (empty($modinfo->instances['scorm'])
+ && has_capability('moodle/course:update', context_course::instance($this->courseid))) {
+ // Redirect to create a new activity
+ $url = new moodle_url('/course/modedit.php',
+ array('course' => $this->courseid, 'section' => 0, 'add' => 'scorm'));
+ redirect($url);
+ }
+ }
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Version details
+ *
+ * @package format
+ * @subpackage scorm
+ * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2012112900; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires = 2012112900; // Requires this Moodle version
+$plugin->component = 'format_scorm'; // Full name of the plugin (used for diagnostics)
--- /dev/null
+<?php
+ // format.php - course format featuring social forum
+ // included from view.php
+
+ $strgroups = get_string('groups');
+ $strgroupmy = get_string('groupmy');
+ $editing = $PAGE->user_is_editing();
+
+ if ($forum = forum_get_course_forum($course->id, 'social')) {
+
+ $cm = get_coursemodule_from_instance('forum', $forum->id);
+ $context = context_module::instance($cm->id);
+
+ /// Print forum intro above posts MDL-18483
+ if (trim($forum->intro) != '') {
+ $options = new stdClass();
+ $options->para = false;
+ $introcontent = format_module_intro('forum', $forum, $cm->id);
+
+ if ($PAGE->user_is_editing() && has_capability('moodle/course:update', $context)) {
+ $streditsummary = get_string('editsummary');
+ $introcontent .= '<div class="editinglink"><a title="'.$streditsummary.'" '.
+ ' href="modedit.php?update='.$cm->id.'&sesskey='.sesskey().'">'.
+ '<img src="'.$OUTPUT->pix_url('t/edit') . '" '.
+ ' class="icon edit" alt="'.$streditsummary.'" /></a></div>';
+ }
+ echo $OUTPUT->box($introcontent, 'generalbox', 'intro');
+ }
+
+ echo '<div class="subscribelink">', forum_get_subscribe_link($forum, $context), '</div>';
+ forum_print_latest_discussions($course, $forum, 10, 'plain', '', false);
+
+ } else {
+ echo $OUTPUT->notification('Could not find or create a social forum here');
+ }
--- /dev/null
+<?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/>.
+
+/**
+ * Strings for component 'format_social', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package format_social
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['sectionname'] = 'section';
+$string['pluginname'] = 'Social format';
--- /dev/null
+<?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/>.
+
+/**
+ * This file contains main class for the course format Social
+ *
+ * @since 2.0
+ * @package format_social
+ * @copyright 2009 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot. '/course/format/lib.php');
+
+/**
+ * Main class for the Social course format
+ *
+ * @package format_social
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_social extends format_base {
+
+ /**
+ * The URL to use for the specified course
+ *
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * if null the course view page is returned
+ * @param array $options options for view URL. At the moment core uses:
+ * 'navigation' (bool) if true and section has no separate page, the function returns null
+ * 'sr' (int) used by multipage formats to specify to which section to return
+ * @return null|moodle_url
+ */
+ public function get_view_url($section, $options = array()) {
+ if (!empty($options['navigation']) && $section !== null) {
+ return null;
+ }
+ return new moodle_url('/course/view.php', array('id' => $this->courseid));
+ }
+
+ /**
+ * Loads all of the course sections into the navigation
+ *
+ * @param global_navigation $navigation
+ * @param navigation_node $node The course node within the navigation
+ */
+ public function extend_course_navigation($navigation, navigation_node $node) {
+ // Social course format does not extend navigation, it uses social_activities block instead
+ }
+
+ /**
+ * Returns the list of blocks to be automatically added for the newly created course
+ *
+ * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
+ * each of values is an array of block names (for left and right side columns)
+ */
+ public function get_default_blocks() {
+ return array(
+ BLOCK_POS_LEFT => array(),
+ BLOCK_POS_RIGHT => array('search_forums', 'calendar_upcoming', 'social_activities',
+ 'recent_activity', 'course_list')
+ );
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Version details
+ *
+ * @package format
+ * @subpackage social
+ * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2012112900; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires = 2012112900; // Requires this Moodle version
+$plugin->component = 'format_social'; // Full name of the plugin (used for diagnostics)
--- /dev/null
+// Javascript functions for Topics course format
+
+M.course = M.course || {};
+
+M.course.format = M.course.format || {};
+
+/**
+ * Get sections config for this format
+ *
+ * The section structure is:
+ * <ul class="topics">
+ * <li class="section">...</li>
+ * <li class="section">...</li>
+ * ...
+ * </ul>
+ *
+ * @return {object} section list configuration
+ */
+M.course.format.get_config = function() {
+ return {
+ container_node : 'ul',
+ container_class : 'topics',
+ section_node : 'li',
+ section_class : 'section'
+ };
+}
+
+/**
+ * Swap section
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {string} node1 node to swap to
+ * @param {string} node2 node to swap with
+ * @return {NodeList} section list
+ */
+M.course.format.swap_sections = function(Y, node1, node2) {
+ var CSS = {
+ COURSECONTENT : 'course-content',
+ SECTIONADDMENUS : 'section_add_menus'
+ };
+
+ var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
+ // Swap menus.
+ sectionlist.item(node1).one('.'+CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.'+CSS.SECTIONADDMENUS));
+}
+
+/**
+ * Process sections after ajax response
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {array} response ajax response
+ * @param {string} sectionfrom first affected section
+ * @param {string} sectionto last affected section
+ * @return void
+ */
+M.course.format.process_sections = function(Y, sectionlist, response, sectionfrom, sectionto) {
+ var CSS = {
+ SECTIONNAME : 'sectionname'
+ };
+
+ if (response.action == 'move') {
+ // If moving up swap around 'sectionfrom' and 'sectionto' so the that loop operates.
+ if (sectionfrom > sectionto) {
+ var temp = sectionto;
+ sectionto = sectionfrom;
+ sectionfrom = temp;
+ }
+ // Update titles in all affected sections.
+ for (var i = sectionfrom; i <= sectionto; i++) {
+ sectionlist.item(i).one('.'+CSS.SECTIONNAME).setContent(response.sectiontitles[i]);
+ }
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Topics course format. Display the whole course as "topics" made of modules.
+ *
+ * @package format_topics
+ * @copyright 2006 The Open University
+ * @author N.D.Freear@open.ac.uk, and others.
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir.'/filelib.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+// Horrible backwards compatible parameter aliasing..
+if ($topic = optional_param('topic', 0, PARAM_INT)) {
+ $url = $PAGE->url;
+ $url->param('section', $topic);
+ debugging('Outdated topic param passed to course/view.php', DEBUG_DEVELOPER);
+ redirect($url);
+}
+// End backwards-compatible aliasing..
+
+$context = context_course::instance($course->id);
+
+if (($marker >=0) && has_capability('moodle/course:setcurrentsection', $context) && confirm_sesskey()) {
+ $course->marker = $marker;
+ course_set_marker($course->id, $marker);
+}
+
+// make sure all sections are created
+$course = course_get_format($course)->get_course();
+course_create_sections_if_missing($course, range(0, $course->numsections));
+
+$renderer = $PAGE->get_renderer('format_topics');
+
+if (!empty($displaysection)) {
+ $renderer->print_single_section_page($course, null, null, null, null, $displaysection);
+} else {
+ $renderer->print_multiple_section_page($course, null, null, null, null);
+}
+
+// Include course format js module
+$PAGE->requires->js('/course/format/topics/format.js');
--- /dev/null
+<?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/>.
+
+/**
+ * Strings for component 'format_topics', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package format_topics
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['currentsection'] = 'This topic';
+$string['sectionname'] = 'Topic';
+$string['pluginname'] = 'Topics format';
+$string['section0name'] = 'General';
+$string['page-course-view-topics'] = 'Any course main page in topics format';
+$string['page-course-view-topics-x'] = 'Any course page in topics format';
+$string['hidefromothers'] = 'Hide topic';
+$string['showfromothers'] = 'Show topic';
--- /dev/null
+<?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/>.
+
+/**
+ * This file contains main class for the course format Topic
+ *
+ * @since 2.0
+ * @package format_topics
+ * @copyright 2009 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot. '/course/format/lib.php');
+
+/**
+ * Main class for the Topics course format
+ *
+ * @package format_topics
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_topics extends format_base {
+
+ /**
+ * Returns true if this course format uses sections
+ *
+ * @return bool
+ */
+ public function uses_sections() {
+ return true;
+ }
+
+ /**
+ * Returns the display name of the given section that the course prefers.
+ *
+ * Use section name is specified by user. Otherwise use default ("Topic #")
+ *
+ * @param int|stdClass $section Section object from database or just field section.section
+ * @return string Display name that the course format prefers, e.g. "Topic 2"
+ */
+ public function get_section_name($section) {
+ $section = $this->get_section($section);
+ if ((string)$section->name !== '') {
+ return format_string($section->name, true,
+ array('context' => context_course::instance($this->courseid)));
+ } else if ($section->section == 0) {
+ return get_string('section0name', 'format_topics');
+ } else {
+ return get_string('topic').' '.$section->section;
+ }
+ }
+
+ /**
+ * The URL to use for the specified course (with section)
+ *
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * if omitted the course view page is returned
+ * @param array $options options for view URL. At the moment core uses:
+ * 'navigation' (bool) if true and section has no separate page, the function returns null
+ * 'sr' (int) used by multipage formats to specify to which section to return
+ * @return null|moodle_url
+ */
+ public function get_view_url($section, $options = array()) {
+ $course = $this->get_course();
+ $url = new moodle_url('/course/view.php', array('id' => $course->id));
+
+ $sr = null;
+ if (array_key_exists('sr', $options)) {
+ $sr = $options['sr'];
+ }
+ if (is_object($section)) {
+ $sectionno = $section->section;
+ } else {
+ $sectionno = $section;
+ }
+ if ($sectionno !== null) {
+ if ($sr !== null) {
+ if ($sr) {
+ $usercoursedisplay = COURSE_DISPLAY_MULTIPAGE;
+ $sectionno = $sr;
+ } else {
+ $usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
+ }
+ } else {
+ $usercoursedisplay = $course->coursedisplay;
+ }
+ if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
+ $url->param('section', $sectionno);
+ } else {
+ if (!empty($options['navigation'])) {
+ return null;
+ }
+ $url->set_anchor('section-'.$sectionno);
+ }
+ }
+ return $url;
+ }
+
+ /**
+ * Returns the information about the ajax support in the given source format
+ *
+ * The returned object's property (boolean)capable indicates that
+ * the course format supports Moodle course ajax features.
+ * The property (array)testedbrowsers can be used as a parameter for {@link ajaxenabled()}.
+ *
+ * @return stdClass
+ */
+ public function supports_ajax() {
+ $ajaxsupport = new stdClass();
+ $ajaxsupport->capable = true;
+ $ajaxsupport->testedbrowsers = array('MSIE' => 6.0, 'Gecko' => 20061111, 'Safari' => 531, 'Chrome' => 6.0);
+ return $ajaxsupport;
+ }
+
+ /**
+ * Loads all of the course sections into the navigation
+ *
+ * @param global_navigation $navigation
+ * @param navigation_node $node The course node within the navigation
+ */
+ public function extend_course_navigation($navigation, navigation_node $node) {
+ global $PAGE;
+ // if section is specified in course/view.php, make sure it is expanded in navigation
+ if ($navigation->includesectionnum === false) {
+ $selectedsection = optional_param('section', null, PARAM_INT);
+ if ($selectedsection !== null && (!defined('AJAX_SCRIPT') || AJAX_SCRIPT == '0') &&
+ $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
+ $navigation->includesectionnum = $selectedsection;
+ }
+ }
+
+ // check if there are callbacks to extend course navigation
+ parent::extend_course_navigation($navigation, $node);
+ }
+
+ /**
+ * Custom action after section has been moved in AJAX mode
+ *
+ * Used in course/rest.php
+ *
+ * @return array This will be passed in ajax respose
+ */
+ function ajax_section_move() {
+ global $PAGE;
+ $titles = array();
+ $course = $this->get_course();
+ $modinfo = get_fast_modinfo($course);
+ $renderer = $this->get_renderer($PAGE);
+ if ($renderer && ($sections = $modinfo->get_section_info_all())) {
+ foreach ($sections as $number => $section) {
+ $titles[$number] = $renderer->section_title($section, $course);
+ }
+ }
+ return array('sectiontitles' => $titles, 'action' => 'move');
+ }
+
+ /**
+ * Returns the list of blocks to be automatically added for the newly created course
+ *
+ * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
+ * each of values is an array of block names (for left and right side columns)
+ */
+ public function get_default_blocks() {
+ return array(
+ BLOCK_POS_LEFT => array(),
+ BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
+ );
+ }
+
+ /**
+ * Definitions of the additional options that this course format uses for course
+ *
+ * Topics format uses the following options:
+ * - coursedisplay
+ * - numsections
+ * - hiddensections
+ *
+ * @param bool $foreditform
+ * @return array of options
+ */
+ public function course_format_options($foreditform = false) {
+ static $courseformatoptions = false;
+ if ($courseformatoptions === false) {
+ $courseconfig = get_config('moodlecourse');
+ $courseformatoptions = array(
+ 'numsections' => array(
+ 'default' => $courseconfig->numsections,
+ 'type' => PARAM_INT,
+ ),
+ 'hiddensections' => array(
+ 'default' => $courseconfig->hiddensections,
+ 'type' => PARAM_INT,
+ ),
+ 'coursedisplay' => array(
+ 'default' => $courseconfig->coursedisplay,
+ 'type' => PARAM_INT,
+ ),
+ );
+ }
+ if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {
+ $courseconfig = get_config('moodlecourse');
+ $max = $courseconfig->maxsections;
+ if (!isset($max) || !is_numeric($max)) {
+ $max = 52;
+ }
+ $sectionmenu = array();
+ for ($i = 0; $i <= $max; $i++) {
+ $sectionmenu[$i] = "$i";
+ }
+ $courseformatoptionsedit = array(
+ 'numsections' => array(
+ 'label' => new lang_string('numberweeks'),
+ 'element_type' => 'select',
+ 'element_attributes' => array($sectionmenu),
+ ),
+ 'hiddensections' => array(
+ 'label' => new lang_string('hiddensections'),
+ 'help' => 'hiddensections',
+ 'help_component' => 'moodle',
+ 'element_type' => 'select',
+ 'element_attributes' => array(
+ array(
+ 0 => new lang_string('hiddensectionscollapsed'),
+ 1 => new lang_string('hiddensectionsinvisible')
+ )
+ ),
+ ),
+ 'coursedisplay' => array(
+ 'label' => new lang_string('coursedisplay'),
+ 'element_type' => 'select',
+ 'element_attributes' => array(
+ array(
+ COURSE_DISPLAY_SINGLEPAGE => new lang_string('coursedisplay_single'),
+ COURSE_DISPLAY_MULTIPAGE => new lang_string('coursedisplay_multi')
+ )
+ ),
+ 'help' => 'coursedisplay',
+ 'help_component' => 'moodle',
+ )
+ );
+ $courseformatoptions = array_merge_recursive($courseformatoptions, $courseformatoptionsedit);
+ }
+ return $courseformatoptions;
+ }
+
+ /**
+ * Updates format options for a course
+ *
+ * In case if course format was changed to 'topics', we try to copy options
+ * 'coursedisplay', 'numsections' and 'hiddensections' from the previous format.
+ * If previous course format did not have 'numsections' option, we populate it with the
+ * current number of sections
+ *
+ * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
+ * @param stdClass $oldcourse if this function is called from {@link update_course()}
+ * this object contains information about the course before update
+ * @return bool whether there were any changes to the options values
+ */
+ public function update_course_format_options($data, $oldcourse = null) {
+ global $DB;
+ if ($oldcourse !== null) {
+ $data = (array)$data;
+ $oldcourse = (array)$oldcourse;
+ $options = $this->course_format_options();
+ foreach ($options as $key => $unused) {
+ if (!array_key_exists($key, $data)) {
+ if (array_key_exists($key, $oldcourse)) {
+ $data[$key] = $oldcourse[$key];
+ } else if ($key === 'numsections') {
+ // If previous format does not have the field 'numsections'
+ // and $data['numsections'] is not set,
+ // we fill it with the maximum section number from the DB
+ $maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
+ WHERE course = ?', array($this->courseid));
+ if ($maxsection) {
+ // If there are no sections, or just default 0-section, 'numsections' will be set to default
+ $data['numsections'] = $maxsection;
+ }
+ }
+ }
+ }
+ }
+ return $this->update_format_options($data);
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Renderer for outputting the topics course format.
+ *
+ * @package format_topics
+ * @copyright 2012 Dan Poltawski
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.3
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot.'/course/format/renderer.php');
+
+/**
+ * Basic renderer for topics format.
+ *
+ * @copyright 2012 Dan Poltawski
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_topics_renderer extends format_section_renderer_base {
+
+ /**
+ * Generate the starting container html for a list of sections
+ * @return string HTML to output.
+ */
+ protected function start_section_list() {
+ return html_writer::start_tag('ul', array('class' => 'topics'));
+ }
+
+ /**
+ * Generate the closing container html for a list of sections
+ * @return string HTML to output.
+ */
+ protected function end_section_list() {
+ return html_writer::end_tag('ul');
+ }
+
+ /**
+ * Generate the title for this section page
+ * @return string the page title
+ */
+ protected function page_title() {
+ return get_string('topicoutline');
+ }
+
+ /**
+ * Generate the edit controls of a section
+ *
+ * @param stdClass $course The course entry from DB
+ * @param stdClass $section The course_section entry from DB
+ * @param bool $onsectionpage true if being printed on a section page
+ * @return array of links with edit controls
+ */
+ protected function section_edit_controls($course, $section, $onsectionpage = false) {
+ global $PAGE;
+
+ if (!$PAGE->user_is_editing()) {
+ return array();
+ }
+
+ $coursecontext = context_course::instance($course->id);
+
+ if ($onsectionpage) {
+ $url = course_get_url($course, $section->section);
+ } else {
+ $url = course_get_url($course);
+ }
+ $url->param('sesskey', sesskey());
+
+ $controls = array();
+ if (has_capability('moodle/course:setcurrentsection', $coursecontext)) {
+ if ($course->marker == $section->section) { // Show the "light globe" on/off.
+ $url->param('marker', 0);
+ $controls[] = html_writer::link($url,
+ html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/marked'),
+ 'class' => 'icon ', 'alt' => get_string('markedthistopic'))),
+ array('title' => get_string('markedthistopic'), 'class' => 'editing_highlight'));
+ } else {
+ $url->param('marker', $section->section);
+ $controls[] = html_writer::link($url,
+ html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/marker'),
+ 'class' => 'icon', 'alt' => get_string('markthistopic'))),
+ array('title' => get_string('markthistopic'), 'class' => 'editing_highlight'));
+ }
+ }
+
+ return array_merge($controls, parent::section_edit_controls($course, $section, $onsectionpage));
+ }
+}
--- /dev/null
+.course-content ul.topics {margin:0;}
+.course-content ul.topics li.section {list-style: none;margin:0 0 5px 0;padding:0;}
+.course-content ul.topics li.section .content {margin:0 40px;}
+.course-content ul.topics li.section .left {float:left;}
+.course-content ul.topics li.section .right {float:right;}
+.course-content ul.topics li.section .left,
+.course-content ul.topics li.section .right {width:40px;text-align:center;padding: 6px 0;}
+.course-content ul.topics li.section .right img.icon { padding: 0 0 4px 0;}
+.course-content ul.topics li.section .left .section-handle img.icon { padding:0; vertical-align: baseline; }
+.jumpmenu {text-align:center;}
--- /dev/null
+<?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/>.
+
+/**
+ * Version details
+ *
+ * @package format
+ * @subpackage topics
+ * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2012112900; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2012112900; // Requires this Moodle version.
+$plugin->component = 'format_topics'; // Full name of the plugin (used for diagnostics).
--- /dev/null
+This files describes API changes for course formats
+
+Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
+
+=== 2.4 ===
+
+Course format API has been changed significantly. Instead of implementing callbacks course formats
+may overwrite the class format_base. See format_legacy class for a template for upgrading course
+format.
+
+* Function settings_navigation::add_course_editing_links() is completely removed, course format
+ functions callback_XXXX_request_key() are no longer used (where XXXX is the course format name)
+* functions get_generic_section_name(), get_all_sections(), add_mod_to_section(), get_all_mods()
+ are deprecated. See their phpdocs in lib/deprecatedlib.php on how to replace them
+* Course formats may now have their settings.php file as the most of other plugin types
+* Function format_section_renderer_base::is_section_current() is deprecated, overwrite/use
+ function is_section_current in format class
+
+=== 2.3 ===
+
+* The new $course->coursedisplay option was introduced, users can now choose to display
+ a section at a time if the course formats support it:
+ - COURSE_DISPLAY_SINGLEPAGE indicates the teacher has chosen to display all sections on one page
+ - COURSE_DISPLAY_MULTIPAGE indicates the teacher has chose to have seperate pages with each section.
+
+* The parameter for 'currently active section' was standardised in core:
+ - The course format is passed the currently live section through the $displaysection varaible to format.php
+ - A 'section' paramter is the standardised way to pass around the current section in a course
+ - Navigation no longer looks for custom parameters defined in callback_format_request_key
--- /dev/null
+// Javascript functions for Weeks course format
+
+M.course = M.course || {};
+
+M.course.format = M.course.format || {};
+
+/**
+ * Get sections config for this format
+ *
+ * The section structure is:
+ * <ul class="weeks">
+ * <li class="section">...</li>
+ * <li class="section">...</li>
+ * ...
+ * </ul>
+ *
+ * @return {object} section list configuration
+ */
+M.course.format.get_config = function() {
+ return {
+ container_node : 'ul',
+ container_class : 'weeks',
+ section_node : 'li',
+ section_class : 'section'
+ };
+}
+
+/**
+ * Swap section
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {string} node1 node to swap to
+ * @param {string} node2 node to swap with
+ * @return {NodeList} section list
+ */
+M.course.format.swap_sections = function(Y, node1, node2) {
+ var CSS = {
+ COURSECONTENT : 'course-content',
+ SECTIONADDMENUS : 'section_add_menus'
+ };
+
+ var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
+ // Swap menus.
+ sectionlist.item(node1).one('.'+CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.'+CSS.SECTIONADDMENUS));
+}
+
+/**
+ * Process sections after ajax response
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {array} response ajax response
+ * @param {string} sectionfrom first affected section
+ * @param {string} sectionto last affected section
+ * @return void
+ */
+M.course.format.process_sections = function(Y, sectionlist, response, sectionfrom, sectionto) {
+ var CSS = {
+ SECTIONNAME : 'sectionname'
+ };
+
+ if (response.action == 'move') {
+ // If moving up swap around 'sectionfrom' and 'sectionto' so the that loop operates.
+ if (sectionfrom > sectionto) {
+ var temp = sectionto;
+ sectionto = sectionfrom;
+ sectionfrom = temp;
+ }
+ // Update titles in all affected sections.
+ for (var i = sectionfrom; i <= sectionto; i++) {
+ sectionlist.item(i).one('.'+CSS.SECTIONNAME).setContent(response.sectiontitles[i]);
+ }
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Weeks course format. Display the whole course as "weeks" made of modules.
+ *
+ * @package format_weeks
+ * @copyright 2006 The Open University
+ * @author N.D.Freear@open.ac.uk, and others.
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir.'/filelib.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+// Horrible backwards compatible parameter aliasing..
+if ($week = optional_param('week', 0, PARAM_INT)) {
+ $url = $PAGE->url;
+ $url->param('section', $week);
+ debugging('Outdated week param passed to course/view.php', DEBUG_DEVELOPER);
+ redirect($url);
+}
+// End backwards-compatible aliasing..
+
+// make sure all sections are created
+$course = course_get_format($course)->get_course();
+course_create_sections_if_missing($course, range(0, $course->numsections));
+
+$renderer = $PAGE->get_renderer('format_weeks');
+
+if (!empty($displaysection)) {
+ $renderer->print_single_section_page($course, null, null, null, null, $displaysection);
+} else {
+ $renderer->print_multiple_section_page($course, null, null, null, null);
+}
+
+$PAGE->requires->js('/course/format/weeks/format.js');
--- /dev/null
+<?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/>.
+
+/**
+ * Strings for component 'format_weeks', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package format_weeks
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['currentsection'] = 'This week';
+$string['sectionname'] = 'Week';
+$string['pluginname'] = 'Weekly format';
+$string['section0name'] = 'General';
+$string['page-course-view-weeks'] = 'Any course main page in weeks format';
+$string['page-course-view-weeks-x'] = 'Any course page in weeks format';
+$string['hidefromothers'] = 'Hide week';
+$string['showfromothers'] = 'Show week';
--- /dev/null
+<?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/>.
+
+/**
+ * This file contains main class for the course format Weeks
+ *
+ * @since 2.0
+ * @package format_weeks
+ * @copyright 2009 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot. '/course/format/lib.php');
+
+/**
+ * Main class for the Weeks course format
+ *
+ * @package format_weeks
+ * @copyright 2012 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_weeks extends format_base {
+
+ /**
+ * Returns true if this course format uses sections
+ *
+ * @return bool
+ */
+ public function uses_sections() {
+ return true;
+ }
+
+ /**
+ * Returns the display name of the given section that the course prefers.
+ *
+ * @param int|stdClass $section Section object from database or just field section.section
+ * @return string Display name that the course format prefers, e.g. "Topic 2"
+ */
+ public function get_section_name($section) {
+ $section = $this->get_section($section);
+ if ((string)$section->name !== '') {
+ // Return the name the user set.
+ return format_string($section->name, true, array('context' => context_course::instance($this->courseid)));
+ } else if ($section->section == 0) {
+ // Return the general section.
+ return get_string('section0name', 'format_weeks');
+ } else {
+ $dates = $this->get_section_dates($section);
+
+ // We subtract 24 hours for display purposes.
+ $dates->end = ($dates->end - 86400);
+
+ $dateformat = ' '.get_string('strftimedateshort');
+ $weekday = userdate($dates->start, $dateformat);
+ $endweekday = userdate($dates->end, $dateformat);
+ return $weekday.' - '.$endweekday;
+ }
+ }
+
+ /**
+ * The URL to use for the specified course (with section)
+ *
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * if omitted the course view page is returned
+ * @param array $options options for view URL. At the moment core uses:
+ * 'navigation' (bool) if true and section has no separate page, the function returns null
+ * 'sr' (int) used by multipage formats to specify to which section to return
+ * @return null|moodle_url
+ */
+ public function get_view_url($section, $options = array()) {
+ $course = $this->get_course();
+ $url = new moodle_url('/course/view.php', array('id' => $course->id));
+
+ $sr = null;
+ if (array_key_exists('sr', $options)) {
+ $sr = $options['sr'];
+ }
+ if (is_object($section)) {
+ $sectionno = $section->section;
+ } else {
+ $sectionno = $section;
+ }
+ if ($sectionno !== null) {
+ if ($sr !== null) {
+ if ($sr) {
+ $usercoursedisplay = COURSE_DISPLAY_MULTIPAGE;
+ $sectionno = $sr;
+ } else {
+ $usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
+ }
+ } else {
+ $usercoursedisplay = $course->coursedisplay;
+ }
+ if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
+ $url->param('section', $sectionno);
+ } else {
+ if (!empty($options['navigation'])) {
+ return null;
+ }
+ $url->set_anchor('section-'.$sectionno);
+ }
+ }
+ return $url;
+ }
+
+ /**
+ * Returns the information about the ajax support in the given source format
+ *
+ * The returned object's property (boolean)capable indicates that
+ * the course format supports Moodle course ajax features.
+ * The property (array)testedbrowsers can be used as a parameter for {@link ajaxenabled()}.
+ *
+ * @return stdClass
+ */
+ public function supports_ajax() {
+ $ajaxsupport = new stdClass();
+ $ajaxsupport->capable = true;
+ $ajaxsupport->testedbrowsers = array('MSIE' => 6.0, 'Gecko' => 20061111, 'Safari' => 531, 'Chrome' => 6.0);
+ return $ajaxsupport;
+ }
+
+ /**
+ * Loads all of the course sections into the navigation
+ *
+ * @param global_navigation $navigation
+ * @param navigation_node $node The course node within the navigation
+ */
+ public function extend_course_navigation($navigation, navigation_node $node) {
+ global $PAGE;
+ // if section is specified in course/view.php, make sure it is expanded in navigation
+ if ($navigation->includesectionnum === false) {
+ $selectedsection = optional_param('section', null, PARAM_INT);
+ if ($selectedsection !== null && (!defined('AJAX_SCRIPT') || AJAX_SCRIPT == '0') &&
+ $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
+ $navigation->includesectionnum = $selectedsection;
+ }
+ }
+ parent::extend_course_navigation($navigation, $node);
+ }
+
+ /**
+ * Custom action after section has been moved in AJAX mode
+ *
+ * Used in course/rest.php
+ *
+ * @return array This will be passed in ajax respose
+ */
+ function ajax_section_move() {
+ global $PAGE;
+ $titles = array();
+ $course = $this->get_course();
+ $modinfo = get_fast_modinfo($course);
+ $renderer = $this->get_renderer($PAGE);
+ if ($renderer && ($sections = $modinfo->get_section_info_all())) {
+ foreach ($sections as $number => $section) {
+ $titles[$number] = $renderer->section_title($section, $course);
+ }
+ }
+ return array('sectiontitles' => $titles, 'action' => 'move');
+ }
+
+ /**
+ * Returns the list of blocks to be automatically added for the newly created course
+ *
+ * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
+ * each of values is an array of block names (for left and right side columns)
+ */
+ public function get_default_blocks() {
+ return array(
+ BLOCK_POS_LEFT => array(),
+ BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
+ );
+ }
+
+ /**
+ * Definitions of the additional options that this course format uses for course
+ *
+ * Weeks format uses the following options:
+ * - coursedisplay
+ * - numsections
+ * - hiddensections
+ *
+ * @param bool $foreditform
+ * @return array of options
+ */
+ public function course_format_options($foreditform = false) {
+ static $courseformatoptions = false;
+ if ($courseformatoptions === false) {
+ $courseconfig = get_config('moodlecourse');
+ $courseformatoptions = array(
+ 'numsections' => array(
+ 'default' => $courseconfig->numsections,
+ 'type' => PARAM_INT,
+ ),
+ 'hiddensections' => array(
+ 'default' => $courseconfig->hiddensections,
+ 'type' => PARAM_INT,
+ ),
+ 'coursedisplay' => array(
+ 'default' => $courseconfig->coursedisplay,
+ 'type' => PARAM_INT,
+ ),
+ );
+ }
+ if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {
+ $courseconfig = get_config('moodlecourse');
+ $sectionmenu = array();
+ $max = $courseconfig->maxsections;
+ if (!isset($max) || !is_numeric($max)) {
+ $max = 52;
+ }
+ for ($i = 0; $i <= $max; $i++) {
+ $sectionmenu[$i] = "$i";
+ }
+ $courseformatoptionsedit = array(
+ 'numsections' => array(
+ 'label' => new lang_string('numberweeks'),
+ 'element_type' => 'select',
+ 'element_attributes' => array($sectionmenu),
+ ),
+ 'hiddensections' => array(
+ 'label' => new lang_string('hiddensections'),
+ 'help' => 'hiddensections',
+ 'help_component' => 'moodle',
+ 'element_type' => 'select',
+ 'element_attributes' => array(
+ array(
+ 0 => new lang_string('hiddensectionscollapsed'),
+ 1 => new lang_string('hiddensectionsinvisible')
+ )
+ ),
+ ),
+ 'coursedisplay' => array(
+ 'label' => new lang_string('coursedisplay'),
+ 'element_type' => 'select',
+ 'element_attributes' => array(
+ array(
+ COURSE_DISPLAY_SINGLEPAGE => new lang_string('coursedisplay_single'),
+ COURSE_DISPLAY_MULTIPAGE => new lang_string('coursedisplay_multi')
+ )
+ ),
+ 'help' => 'coursedisplay',
+ 'help_component' => 'moodle',
+ )
+ );
+ $courseformatoptions = array_merge_recursive($courseformatoptions, $courseformatoptionsedit);
+ }
+ return $courseformatoptions;
+ }
+
+ /**
+ * Updates format options for a course
+ *
+ * In case if course format was changed to 'weeks', we try to copy options
+ * 'coursedisplay', 'numsections' and 'hiddensections' from the previous format.
+ * If previous course format did not have 'numsections' option, we populate it with the
+ * current number of sections
+ *
+ * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
+ * @param stdClass $oldcourse if this function is called from {@link update_course()}
+ * this object contains information about the course before update
+ * @return bool whether there were any changes to the options values
+ */
+ public function update_course_format_options($data, $oldcourse = null) {
+ global $DB;
+ if ($oldcourse !== null) {
+ $data = (array)$data;
+ $oldcourse = (array)$oldcourse;
+ $options = $this->course_format_options();
+ foreach ($options as $key => $unused) {
+ if (!array_key_exists($key, $data)) {
+ if (array_key_exists($key, $oldcourse)) {
+ $data[$key] = $oldcourse[$key];
+ } else if ($key === 'numsections') {
+ // If previous format does not have the field 'numsections'
+ // and $data['numsections'] is not set,
+ // we fill it with the maximum section number from the DB
+ $maxsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
+ WHERE course = ?', array($this->courseid));
+ if ($maxsection) {
+ // If there are no sections, or just default 0-section, 'numsections' will be set to default
+ $data['numsections'] = $maxsection;
+ }
+ }
+ }
+ }
+ }
+ return $this->update_format_options($data);
+ }
+
+ /**
+ * Return the start and end date of the passed section
+ *
+ * @param int|stdClass|section_info $section section to get the dates for
+ * @return stdClass property start for startdate, property end for enddate
+ */
+ public function get_section_dates($section) {
+ $course = $this->get_course();
+ if (is_object($section)) {
+ $sectionnum = $section->section;
+ } else {
+ $sectionnum = $section;
+ }
+ $oneweekseconds = 604800;
+ // Hack alert. We add 2 hours to avoid possible DST problems. (e.g. we go into daylight
+ // savings and the date changes.
+ $startdate = $course->startdate + 7200;
+
+ $dates = new stdClass();
+ $dates->start = $startdate + ($oneweekseconds * ($sectionnum - 1));
+ $dates->end = $dates->start + $oneweekseconds;
+
+ return $dates;
+ }
+
+ /**
+ * Returns true if the specified week is current
+ *
+ * @param int|stdClass|section_info $section
+ * @return bool
+ */
+ public function is_section_current($section) {
+ if (is_object($section)) {
+ $sectionnum = $section->section;
+ } else {
+ $sectionnum = $section;
+ }
+ if ($sectionnum < 1) {
+ return false;
+ }
+ $timenow = time();
+ $dates = $this->get_section_dates($section);
+ return (($timenow >= $dates->start) && ($timenow < $dates->end));
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Renderer for outputting the weeks course format.
+ *
+ * @package format_weeks
+ * @copyright 2012 Dan Poltawski
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.3
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot.'/course/format/renderer.php');
+require_once($CFG->dirroot.'/course/format/weeks/lib.php');
+
+
+/**
+ * Basic renderer for weeks format.
+ *
+ * @copyright 2012 Dan Poltawski
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_weeks_renderer extends format_section_renderer_base {
+ /**
+ * Generate the starting container html for a list of sections
+ * @return string HTML to output.
+ */
+ protected function start_section_list() {
+ return html_writer::start_tag('ul', array('class' => 'weeks'));
+ }
+
+ /**
+ * Generate the closing container html for a list of sections
+ * @return string HTML to output.
+ */
+ protected function end_section_list() {
+ return html_writer::end_tag('ul');
+ }
+
+ /**
+ * Generate the title for this section page
+ * @return string the page title
+ */
+ protected function page_title() {
+ return get_string('weeklyoutline');
+ }
+}
--- /dev/null
+.course-content ul.weeks {margin:0;}
+.course-content ul.weeks li.section {list-style: none;margin:0 0 5px 0;padding:0;}
+.course-content ul.weeks li.section .content {margin:0 40px;}
+.course-content ul.weeks li.section .left {float:left;}
+.course-content ul.weeks li.section .right {float:right;}
+.course-content ul.weeks li.section .left,
+.course-content ul.weeks li.section .right {width:40px;text-align:center;padding: 6px 0;}
+.course-content ul.weeks li.section .right img.icon { padding: 0 0 4px 0;}
+.course-content ul.weeks li.section .left .section-handle img.icon { padding:0; vertical-align: baseline; }
+.jumpmenu {text-align:center;}
\ No newline at end of file
--- /dev/null
+<?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/>.
+
+/**
+ * Version details
+ *
+ * @package format
+ * @subpackage weeks
+ * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2012112900; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2012112900; // Requires this Moodle version.
+$plugin->component = 'format_weeks'; // Full name of the plugin (used for diagnostics).
--- /dev/null
+<?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/>.
+
+/**
+ * For most people, just lists the course categories
+ * Allows the admin to create, delete and rename course categories
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require_once("../config.php");
+require_once("lib.php");
+
+$categoryedit = optional_param('categoryedit', -1,PARAM_BOOL);
+$delete = optional_param('delete',0,PARAM_INT);
+$hide = optional_param('hide',0,PARAM_INT);
+$show = optional_param('show',0,PARAM_INT);
+$move = optional_param('move',0,PARAM_INT);
+$moveto = optional_param('moveto',-1,PARAM_INT);
+$moveup = optional_param('moveup',0,PARAM_INT);
+$movedown = optional_param('movedown',0,PARAM_INT);
+
+$site = get_site();
+
+$systemcontext = context_system::instance();
+
+$PAGE->set_url('/course/index.php');
+$PAGE->set_context($systemcontext);
+$PAGE->set_pagelayout('admin');
+
+if (can_edit_in_category()) {
+ if ($categoryedit !== -1) {
+ $USER->editing = $categoryedit;
+ }
+ require_login();
+ $adminediting = $PAGE->user_is_editing();
+} else {
+ if ($CFG->forcelogin) {
+ require_login();
+ }
+ $adminediting = false;
+}
+
+$stradministration = get_string('administration');
+$strcategories = get_string('categories');
+$strcategory = get_string('category');
+$strcourses = get_string('courses');
+$stredit = get_string('edit');
+$strdelete = get_string('delete');
+$straction = get_string('action');
+$strfulllistofcourses = get_string('fulllistofcourses');
+
+
+// Unless it's an editing admin, just print the regular listing of courses/categories.
+if (!$adminediting) {
+ $showaddcoursebutton = true;
+ // Print form for creating new categories.
+ $countcategories = $DB->count_records('course_categories');
+ if ($countcategories > 1 || ($countcategories == 1 && $DB->count_records('course') > 200)) {
+ $strcourses = get_string('courses');
+ $strcategories = get_string('categories');
+
+ $PAGE->navbar->add($strcategories);
+ $PAGE->set_title("$site->shortname: $strcategories");
+ $PAGE->set_heading($COURSE->fullname);
+ $PAGE->set_button(update_category_button());
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading($strcategories);
+ echo $OUTPUT->skip_link_target();
+ echo $OUTPUT->box_start('categorybox');
+ print_whole_category_list();
+ echo $OUTPUT->box_end();
+ print_course_search();
+ } else {
+ $PAGE->navbar->add($strfulllistofcourses);
+ $PAGE->set_title("$site->shortname: $strfulllistofcourses");
+ $PAGE->set_heading($COURSE->fullname);
+ $PAGE->set_button(update_category_button());
+ echo $OUTPUT->header();
+ echo $OUTPUT->skip_link_target();
+ echo $OUTPUT->box_start('courseboxes');
+ $showaddcoursebutton = print_courses(0);
+ echo $OUTPUT->box_end();
+ }
+
+ echo $OUTPUT->container_start('buttons');
+ if (has_capability('moodle/course:create', $systemcontext) && $showaddcoursebutton) {
+ // Print link to create a new course, for the 1st available category.
+ $options = array('category' => $CFG->defaultrequestcategory);
+ echo $OUTPUT->single_button(new moodle_url('edit.php', $options), get_string('addnewcourse'), 'get');
+ }
+ print_course_request_buttons($systemcontext);
+ echo $OUTPUT->container_end();
+ echo $OUTPUT->footer();
+ exit;
+}
+/// Everything else is editing on mode.
+require_once($CFG->libdir.'/adminlib.php');
+admin_externalpage_setup('coursemgmt');
+
+/// Delete a category.
+if (!empty($delete) and confirm_sesskey()) {
+ if (!$deletecat = $DB->get_record('course_categories', array('id'=>$delete))) {
+ print_error('invalidcategoryid');
+ }
+ $context = context_coursecat::instance($delete);
+ require_capability('moodle/category:manage', $context);
+ require_capability('moodle/category:manage', get_category_or_system_context($deletecat->parent));
+
+ $heading = get_string('deletecategory', 'moodle', format_string($deletecat->name, true, array('context' => $context)));
+ require_once('delete_category_form.php');
+ $mform = new delete_category_form(null, $deletecat);
+ $mform->set_data(array('delete'=>$delete));
+
+ if ($mform->is_cancelled()) {
+ redirect('index.php');
+
+ } else if (!$data= $mform->get_data()) {
+ require_once($CFG->libdir . '/questionlib.php');
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading($heading);
+ $mform->display();
+ echo $OUTPUT->footer();
+ exit();
+ }
+
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading($heading);
+
+ if ($data->fulldelete) {
+ $deletedcourses = category_delete_full($deletecat, true);
+
+ foreach($deletedcourses as $course) {
+ echo $OUTPUT->notification(get_string('coursedeleted', '', $course->shortname), 'notifysuccess');
+ }
+ echo $OUTPUT->notification(get_string('coursecategorydeleted', '', format_string($deletecat->name, true, array('context' => $context))), 'notifysuccess');
+
+ } else {
+ category_delete_move($deletecat, $data->newparent, true);
+ }
+
+ // If we deleted $CFG->defaultrequestcategory, make it point somewhere else.
+ if ($delete == $CFG->defaultrequestcategory) {
+ set_config('defaultrequestcategory', $DB->get_field('course_categories', 'MIN(id)', array('parent'=>0)));
+ }
+
+ echo $OUTPUT->continue_button('index.php');
+
+ echo $OUTPUT->footer();
+ die;
+}
+
+/// Create a default category if necessary
+if (!$categories = get_categories()) { /// No category yet!
+ // Try and make one
+ $tempcat = new stdClass();
+ $tempcat->name = get_string('miscellaneous');
+ $tempcat->id = $DB->insert_record('course_categories', $tempcat);
+ $tempcat->context = context_coursecat::instance($tempcat->id);
+ mark_context_dirty('/'.SYSCONTEXTID);
+ fix_course_sortorder(); // Required to build course_categories.depth and .path.
+ set_config('defaultrequestcategory', $tempcat->id);
+}
+
+/// Move a category to a new parent if required
+if (!empty($move) and ($moveto >= 0) and confirm_sesskey()) {
+ if ($cattomove = $DB->get_record('course_categories', array('id'=>$move))) {
+ require_capability('moodle/category:manage', get_category_or_system_context($cattomove->parent));
+ if ($cattomove->parent != $moveto) {
+ $newparent = $DB->get_record('course_categories', array('id'=>$moveto));
+ require_capability('moodle/category:manage', get_category_or_system_context($moveto));
+ move_category($cattomove, $newparent);
+ }
+ }
+}
+
+/// Hide or show a category
+if ($hide and confirm_sesskey()) {
+ if ($tempcat = $DB->get_record('course_categories', array('id'=>$hide))) {
+ require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent));
+ if ($tempcat->visible == 1) {
+ course_category_hide($tempcat);
+ }
+ }
+} else if ($show and confirm_sesskey()) {
+ if ($tempcat = $DB->get_record('course_categories', array('id'=>$show))) {
+ require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent));
+ if ($tempcat->visible == 0) {
+ course_category_show($tempcat);
+ }
+ }
+}
+
+/// Move a category up or down
+if ((!empty($moveup) or !empty($movedown)) and confirm_sesskey()) {
+ fix_course_sortorder();
+ $swapcategory = NULL;
+
+ if (!empty($moveup)) {
+ require_capability('moodle/category:manage', context_coursecat::instance($moveup));
+ if ($movecategory = $DB->get_record('course_categories', array('id'=>$moveup))) {
+ if ($swapcategory = $DB->get_records_select('course_categories', "sortorder<? AND parent=?", array($movecategory->sortorder, $movecategory->parent), 'sortorder DESC', '*', 0, 1)) {
+ $swapcategory = reset($swapcategory);
+ }
+ }
+ } else {
+ require_capability('moodle/category:manage', context_coursecat::instance($movedown));
+ if ($movecategory = $DB->get_record('course_categories', array('id'=>$movedown))) {
+ if ($swapcategory = $DB->get_records_select('course_categories', "sortorder>? AND parent=?", array($movecategory->sortorder, $movecategory->parent), 'sortorder ASC', '*', 0, 1)) {
+ $swapcategory = reset($swapcategory);
+ }
+ }
+ }
+ if ($swapcategory and $movecategory) {
+ $DB->set_field('course_categories', 'sortorder', $swapcategory->sortorder, array('id'=>$movecategory->id));
+ $DB->set_field('course_categories', 'sortorder', $movecategory->sortorder, array('id'=>$swapcategory->id));
+ add_to_log(SITEID, "category", "move", "editcategory.php?id=$movecategory->id", $movecategory->id);
+ }
+
+ // finally reorder courses
+ fix_course_sortorder();
+}
+
+/// Print headings
+echo $OUTPUT->header();
+echo $OUTPUT->heading($strcategories);
+
+/// Print out the categories with all the knobs
+$strcategories = get_string('categories');
+$strcourses = get_string('courses');
+$strmovecategoryto = get_string('movecategoryto');
+$stredit = get_string('edit');
+
+$displaylist = array();
+$parentlist = array();
+
+$displaylist[0] = get_string('top');
+make_categories_list($displaylist, $parentlist);
+
+echo '<table class="generaltable editcourse boxaligncenter"><tr class="header">';
+echo '<th class="header" scope="col">'.$strcategories.'</th>';
+echo '<th class="header" scope="col">'.$strcourses.'</th>';
+echo '<th class="header" scope="col">'.$stredit.'</th>';
+echo '<th class="header" scope="col">'.$strmovecategoryto.'</th>';
+echo '</tr>';
+
+print_category_edit(NULL, $displaylist, $parentlist);
+echo '</table>';
+
+echo '<div class="buttons">';
+if (has_capability('moodle/course:create', $systemcontext)) {
+ // print create course link to first category
+ $options = array('category' => $CFG->defaultrequestcategory);
+ $options['returnto'] = 'topcat';
+ echo $OUTPUT->single_button(new moodle_url('edit.php', $options), get_string('addnewcourse'), 'get');
+}
+
+// Print button for creating new categories
+if (has_capability('moodle/category:manage', $systemcontext)) {
+ $options = array('parent'=>0);
+ echo $OUTPUT->single_button(new moodle_url('editcategory.php', $options), get_string('addnewcategory'), 'get');
+}
+
+print_course_request_buttons($systemcontext);
+echo '</div>';
+
+echo $OUTPUT->footer();
+
+function print_category_edit($category, $displaylist, $parentslist, $depth=-1, $up=false, $down=false) {
+/// Recursive function to print all the categories ready for editing
+
+ global $CFG, $USER, $OUTPUT;
+
+ static $str = NULL;
+
+ if (is_null($str)) {
+ $str = new stdClass;
+ $str->edit = get_string('edit');
+ $str->delete = get_string('delete');
+ $str->moveup = get_string('moveup');
+ $str->movedown = get_string('movedown');
+ $str->edit = get_string('editthiscategory');
+ $str->hide = get_string('hide');
+ $str->show = get_string('show');
+ $str->cohorts = get_string('cohorts', 'cohort');
+ $str->spacer = $OUTPUT->spacer().' ';
+ }
+
+ if (!empty($category)) {
+
+ if (!isset($category->context)) {
+ $category->context = context_coursecat::instance($category->id);
+ }
+
+ echo '<tr><td align="left" class="name">';
+ for ($i=0; $i<$depth;$i++) {
+ echo ' ';
+ }
+ $linkcss = $category->visible ? '' : ' class="dimmed" ';
+ echo '<a '.$linkcss.' title="'.$str->edit.'" '.
+ ' href="category.php?id='.$category->id.'&categoryedit=on&sesskey='.sesskey().'">'.
+ format_string($category->name, true, array('context' => $category->context)).'</a>';
+ echo '</td>';
+
+ echo '<td class="count">'.$category->coursecount.'</td>';
+
+ echo '<td class="icons">'; /// Print little icons
+
+ if (has_capability('moodle/category:manage', $category->context)) {
+ echo '<a title="'.$str->edit.'" href="editcategory.php?id='.$category->id.'"><img'.
+ ' src="'.$OUTPUT->pix_url('t/edit') . '" class="iconsmall" alt="'.$str->edit.'" /></a> ';
+
+ echo '<a title="'.$str->delete.'" href="index.php?delete='.$category->id.'&sesskey='.sesskey().'"><img'.
+ ' src="'.$OUTPUT->pix_url('t/delete') . '" class="iconsmall" alt="'.$str->delete.'" /></a> ';
+
+ if (!empty($category->visible)) {
+ echo '<a title="'.$str->hide.'" href="index.php?hide='.$category->id.'&sesskey='.sesskey().'"><img'.
+ ' src="'.$OUTPUT->pix_url('t/hide') . '" class="iconsmall" alt="'.$str->hide.'" /></a> ';
+ } else {
+ echo '<a title="'.$str->show.'" href="index.php?show='.$category->id.'&sesskey='.sesskey().'"><img'.
+ ' src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.$str->show.'" /></a> ';
+ }
+
+ if (has_capability('moodle/cohort:manage', $category->context) or has_capability('moodle/cohort:view', $category->context)) {
+ echo '<a title="'.$str->cohorts.'" href="'.$CFG->wwwroot.'/cohort/index.php?contextid='.$category->context->id.'"><img'.
+ ' src="'.$OUTPUT->pix_url('t/cohort') . '" class="iconsmall" alt="'.$str->cohorts.'" /></a> ';
+ }
+
+ if ($up) {
+ echo '<a title="'.$str->moveup.'" href="index.php?moveup='.$category->id.'&sesskey='.sesskey().'"><img'.
+ ' src="'.$OUTPUT->pix_url('t/up') . '" class="iconsmall" alt="'.$str->moveup.'" /></a> ';
+ } else {
+ echo $str->spacer;
+ }
+ if ($down) {
+ echo '<a title="'.$str->movedown.'" href="index.php?movedown='.$category->id.'&sesskey='.sesskey().'"><img'.
+ ' src="'.$OUTPUT->pix_url('t/down') . '" class="iconsmall" alt="'.$str->movedown.'" /></a> ';
+ } else {
+ echo $str->spacer;
+ }
+ }
+ echo '</td>';
+
+ echo '<td align="left">';
+ if (has_capability('moodle/category:manage', $category->context)) {
+ $tempdisplaylist = $displaylist;
+ unset($tempdisplaylist[$category->id]);
+ foreach ($parentslist as $key => $parents) {
+ if (in_array($category->id, $parents)) {
+ unset($tempdisplaylist[$key]);
+ }
+ }
+ $popupurl = new moodle_url("index.php?move=$category->id&sesskey=".sesskey());
+ $select = new single_select($popupurl, 'moveto', $tempdisplaylist, $category->parent, null, "moveform$category->id");
+ $select->set_label(get_string('frontpagecategorynames'), array('class' => 'accesshide'));
+ echo $OUTPUT->render($select);
+ }
+ echo '</td>';
+ echo '</tr>';
+ } else {
+ $category = new stdClass();
+ $category->id = '0';
+ }
+
+ if ($categories = get_categories($category->id)) { // Print all the children recursively
+ $countcats = count($categories);
+ $count = 0;
+ $first = true;
+ $last = false;
+ foreach ($categories as $cat) {
+ $count++;
+ if ($count == $countcats) {
+ $last = true;
+ }
+ $up = $first ? false : true;
+ $down = $last ? false : true;
+ $first = false;
+
+ print_category_edit($cat, $displaylist, $parentslist, $depth+1, $up, $down);
+ }
+ }
+}
--- /dev/null
+<?php
+
+/// Displays external information about a course
+
+ require_once("../config.php");
+ require_once("lib.php");
+
+ $id = optional_param('id', false, PARAM_INT); // Course id
+ $name = optional_param('name', false, PARAM_RAW); // Course short name
+
+ if (!$id and !$name) {
+ print_error("unspecifycourseid");
+ }
+
+ if ($name) {
+ if (!$course = $DB->get_record("course", array("shortname"=>$name))) {
+ print_error("invalidshortname");
+ }
+ } else {
+ if (!$course = $DB->get_record("course", array("id"=>$id))) {
+ print_error("invalidcourseid");
+ }
+ }
+
+ $site = get_site();
+
+ if ($CFG->forcelogin) {
+ require_login();
+ }
+
+ $context = context_course::instance($course->id);
+ if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+ print_error('coursehidden', '', $CFG->wwwroot .'/');
+ }
+
+ $PAGE->set_context($context);
+ $PAGE->set_pagelayout('popup');
+ $PAGE->set_url('/course/info.php', array('id' => $course->id));
+ $PAGE->set_title(get_string("summaryof", "", $course->fullname));
+ $PAGE->set_heading(get_string('courseinfo'));
+ $PAGE->set_course($course);
+ $PAGE->navbar->add(get_string('summary'));
+
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading('<a href="view.php?id='.$course->id.'">'.format_string($course->fullname) . '</a><br />(' . format_string($course->shortname, true, array('context' => $context)) . ')');
+
+ // print enrol info
+ if ($texts = enrol_get_course_description_texts($course)) {
+ echo $OUTPUT->box_start('generalbox icons');
+ echo implode($texts);
+ echo $OUTPUT->box_end();
+ }
+
+ $courserenderer = $PAGE->get_renderer('core', 'course');
+ echo $courserenderer->course_info_box($course);
+
+ echo "<br />";
+
+ echo $OUTPUT->footer();
+
+
--- /dev/null
+<?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/>.
+
+/**
+ * Jumps to a given relative or Moodle absolute URL.
+ * Mostly used for accessibility.
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require('../config.php');
+
+$jump = required_param('jump', PARAM_RAW);
+
+$PAGE->set_url('/course/jumpto.php');
+
+if (!confirm_sesskey()) {
+ print_error('confirmsesskeybad');
+}
+
+if (strpos($jump, '/') === 0) {
+ redirect(new moodle_url($jump));
+} else {
+ print_error('error');
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Library of useful functions
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package core
+ * @subpackage course
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir.'/completionlib.php');
+require_once($CFG->libdir.'/filelib.php');
+require_once($CFG->dirroot.'/course/dnduploadlib.php');
+require_once($CFG->dirroot.'/course/format/lib.php');
+
+define('COURSE_MAX_LOGS_PER_PAGE', 1000); // records
+define('COURSE_MAX_RECENT_PERIOD', 172800); // Two days, in seconds
+
+/**
+ * Number of courses to display when summaries are included.
+ * @var int
+ * @deprecated since 2.4, use $CFG->courseswithsummarieslimit instead.
+ */
+define('COURSE_MAX_SUMMARIES_PER_PAGE', 10);
+
+define('COURSE_MAX_COURSES_PER_DROPDOWN',1000); // max courses in log dropdown before switching to optional
+define('COURSE_MAX_USERS_PER_DROPDOWN',1000); // max users in log dropdown before switching to optional
+define('FRONTPAGENEWS', '0');
+define('FRONTPAGECOURSELIST', '1');
+define('FRONTPAGECATEGORYNAMES', '2');
+define('FRONTPAGETOPICONLY', '3');
+define('FRONTPAGECATEGORYCOMBO', '4');
+define('FRONTPAGECOURSELIMIT', 200); // maximum number of courses displayed on the frontpage
+define('EXCELROWS', 65535);
+define('FIRSTUSEDEXCELROW', 3);
+
+define('MOD_CLASS_ACTIVITY', 0);
+define('MOD_CLASS_RESOURCE', 1);
+
+function make_log_url($module, $url) {
+ switch ($module) {
+ case 'course':
+ if (strpos($url, 'report/') === 0) {
+ // there is only one report type, course reports are deprecated
+ $url = "/$url";
+ break;
+ }
+ case 'file':
+ case 'login':
+ case 'lib':
+ case 'admin':
+ case 'calendar':
+ case 'category':
+ case 'mnet course':
+ if (strpos($url, '../') === 0) {
+ $url = ltrim($url, '.');
+ } else {
+ $url = "/course/$url";
+ }
+ break;
+ case 'user':
+ case 'blog':
+ $url = "/$module/$url";
+ break;
+ case 'upload':
+ $url = $url;
+ break;
+ case 'coursetags':
+ $url = '/'.$url;
+ break;
+ case 'library':
+ case '':
+ $url = '/';
+ break;
+ case 'message':
+ $url = "/message/$url";
+ break;
+ case 'notes':
+ $url = "/notes/$url";
+ break;
+ case 'tag':
+ $url = "/tag/$url";
+ break;
+ case 'role':
+ $url = '/'.$url;
+ break;
+ default:
+ $url = "/mod/$module/$url";
+ break;
+ }
+
+ //now let's sanitise urls - there might be some ugly nasties:-(
+ $parts = explode('?', $url);
+ $script = array_shift($parts);
+ if (strpos($script, 'http') === 0) {
+ $script = clean_param($script, PARAM_URL);
+ } else {
+ $script = clean_param($script, PARAM_PATH);
+ }
+
+ $query = '';
+ if ($parts) {
+ $query = implode('', $parts);
+ $query = str_replace('&', '&', $query); // both & and & are stored in db :-|
+ $parts = explode('&', $query);
+ $eq = urlencode('=');
+ foreach ($parts as $key=>$part) {
+ $part = urlencode(urldecode($part));
+ $part = str_replace($eq, '=', $part);
+ $parts[$key] = $part;
+ }
+ $query = '?'.implode('&', $parts);
+ }
+
+ return $script.$query;
+}
+
+
+function build_mnet_logs_array($hostid, $course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
+ $modname="", $modid=0, $modaction="", $groupid=0) {
+ global $CFG, $DB;
+
+ // It is assumed that $date is the GMT time of midnight for that day,
+ // and so the next 86400 seconds worth of logs are printed.
+
+ /// Setup for group handling.
+
+ // TODO: I don't understand group/context/etc. enough to be able to do
+ // something interesting with it here
+ // What is the context of a remote course?
+
+ /// If the group mode is separate, and this user does not have editing privileges,
+ /// then only the user's group can be viewed.
+ //if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
+ // $groupid = get_current_group($course->id);
+ //}
+ /// If this course doesn't have groups, no groupid can be specified.
+ //else if (!$course->groupmode) {
+ // $groupid = 0;
+ //}
+
+ $groupid = 0;
+
+ $joins = array();
+ $where = '';
+
+ $qry = "SELECT l.*, u.firstname, u.lastname, u.picture
+ FROM {mnet_log} l
+ LEFT JOIN {user} u ON l.userid = u.id
+ WHERE ";
+ $params = array();
+
+ $where .= "l.hostid = :hostid";
+ $params['hostid'] = $hostid;
+
+ // TODO: Is 1 really a magic number referring to the sitename?
+ if ($course != SITEID || $modid != 0) {
+ $where .= " AND l.course=:courseid";
+ $params['courseid'] = $course;
+ }
+
+ if ($modname) {
+ $where .= " AND l.module = :modname";
+ $params['modname'] = $modname;
+ }
+
+ if ('site_errors' === $modid) {
+ $where .= " AND ( l.action='error' OR l.action='infected' )";
+ } else if ($modid) {
+ //TODO: This assumes that modids are the same across sites... probably
+ //not true
+ $where .= " AND l.cmid = :modid";
+ $params['modid'] = $modid;
+ }
+
+ if ($modaction) {
+ $firstletter = substr($modaction, 0, 1);
+ if ($firstletter == '-') {
+ $where .= " AND ".$DB->sql_like('l.action', ':modaction', false, true, true);
+ $params['modaction'] = '%'.substr($modaction, 1).'%';
+ } else {
+ $where .= " AND ".$DB->sql_like('l.action', ':modaction', false);
+ $params['modaction'] = '%'.$modaction.'%';
+ }
+ }
+
+ if ($user) {
+ $where .= " AND l.userid = :user";
+ $params['user'] = $user;
+ }
+
+ if ($date) {
+ $enddate = $date + 86400;
+ $where .= " AND l.time > :date AND l.time < :enddate";
+ $params['date'] = $date;
+ $params['enddate'] = $enddate;
+ }
+
+ $result = array();
+ $result['totalcount'] = $DB->count_records_sql("SELECT COUNT('x') FROM {mnet_log} l WHERE $where", $params);
+ if(!empty($result['totalcount'])) {
+ $where .= " ORDER BY $order";
+ $result['logs'] = $DB->get_records_sql("$qry $where", $params, $limitfrom, $limitnum);
+ } else {
+ $result['logs'] = array();
+ }
+ return $result;
+}
+
+function build_logs_array($course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
+ $modname="", $modid=0, $modaction="", $groupid=0) {
+ global $DB, $SESSION, $USER;
+ // It is assumed that $date is the GMT time of midnight for that day,
+ // and so the next 86400 seconds worth of logs are printed.
+
+ /// Setup for group handling.
+
+ /// If the group mode is separate, and this user does not have editing privileges,
+ /// then only the user's group can be viewed.
+ if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
+ if (isset($SESSION->currentgroup[$course->id])) {
+ $groupid = $SESSION->currentgroup[$course->id];
+ } else {
+ $groupid = groups_get_all_groups($course->id, $USER->id);
+ if (is_array($groupid)) {
+ $groupid = array_shift(array_keys($groupid));
+ $SESSION->currentgroup[$course->id] = $groupid;
+ } else {
+ $groupid = 0;
+ }
+ }
+ }
+ /// If this course doesn't have groups, no groupid can be specified.
+ else if (!$course->groupmode) {
+ $groupid = 0;
+ }
+
+ $joins = array();
+ $params = array();
+
+ if ($course->id != SITEID || $modid != 0) {
+ $joins[] = "l.course = :courseid";
+ $params['courseid'] = $course->id;
+ }
+
+ if ($modname) {
+ $joins[] = "l.module = :modname";
+ $params['modname'] = $modname;
+ }
+
+ if ('site_errors' === $modid) {
+ $joins[] = "( l.action='error' OR l.action='infected' )";
+ } else if ($modid) {
+ $joins[] = "l.cmid = :modid";
+ $params['modid'] = $modid;
+ }
+
+ if ($modaction) {
+ $firstletter = substr($modaction, 0, 1);
+ if ($firstletter == '-') {
+ $joins[] = $DB->sql_like('l.action', ':modaction', false, true, true);
+ $params['modaction'] = '%'.substr($modaction, 1).'%';
+ } else {
+ $joins[] = $DB->sql_like('l.action', ':modaction', false);
+ $params['modaction'] = '%'.$modaction.'%';
+ }
+ }
+
+
+ /// Getting all members of a group.
+ if ($groupid and !$user) {
+ if ($gusers = groups_get_members($groupid)) {
+ $gusers = array_keys($gusers);
+ $joins[] = 'l.userid IN (' . implode(',', $gusers) . ')';
+ } else {
+ $joins[] = 'l.userid = 0'; // No users in groups, so we want something that will always be false.
+ }
+ }
+ else if ($user) {
+ $joins[] = "l.userid = :userid";
+ $params['userid'] = $user;
+ }
+
+ if ($date) {
+ $enddate = $date + 86400;
+ $joins[] = "l.time > :date AND l.time < :enddate";
+ $params['date'] = $date;
+ $params['enddate'] = $enddate;
+ }
+
+ $selector = implode(' AND ', $joins);
+
+ $totalcount = 0; // Initialise
+ $result = array();
+ $result['logs'] = get_logs($selector, $params, $order, $limitfrom, $limitnum, $totalcount);
+ $result['totalcount'] = $totalcount;
+ return $result;
+}
+
+
+function print_log($course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
+ $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
+
+ global $CFG, $DB, $OUTPUT;
+
+ if (!$logs = build_logs_array($course, $user, $date, $order, $page*$perpage, $perpage,
+ $modname, $modid, $modaction, $groupid)) {
+ echo $OUTPUT->notification("No logs found!");
+ echo $OUTPUT->footer();
+ exit;
+ }
+
+ $courses = array();
+
+ if ($course->id == SITEID) {
+ $courses[0] = '';
+ if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
+ foreach ($ccc as $cc) {
+ $courses[$cc->id] = $cc->shortname;
+ }
+ }
+ } else {
+ $courses[$course->id] = $course->shortname;
+ }
+
+ $totalcount = $logs['totalcount'];
+ $count=0;
+ $ldcache = array();
+ $tt = getdate(time());
+ $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
+
+ $strftimedatetime = get_string("strftimedatetime");
+
+ echo "<div class=\"info\">\n";
+ print_string("displayingrecords", "", $totalcount);
+ echo "</div>\n";
+
+ echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
+
+ $table = new html_table();
+ $table->classes = array('logtable','generalbox');
+ $table->align = array('right', 'left', 'left');
+ $table->head = array(
+ get_string('time'),
+ get_string('ip_address'),
+ get_string('fullnameuser'),
+ get_string('action'),
+ get_string('info')
+ );
+ $table->data = array();
+
+ if ($course->id == SITEID) {
+ array_unshift($table->align, 'left');
+ array_unshift($table->head, get_string('course'));
+ }
+
+ // Make sure that the logs array is an array, even it is empty, to avoid warnings from the foreach.
+ if (empty($logs['logs'])) {
+ $logs['logs'] = array();
+ }
+
+ foreach ($logs['logs'] as $log) {
+
+ if (isset($ldcache[$log->module][$log->action])) {
+ $ld = $ldcache[$log->module][$log->action];
+ } else {
+ $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
+ $ldcache[$log->module][$log->action] = $ld;
+ }
+ if ($ld && is_numeric($log->info)) {
+ // ugly hack to make sure fullname is shown correctly
+ if ($ld->mtable == 'user' && $ld->field == $DB->sql_concat('firstname', "' '" , 'lastname')) {
+ $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
+ } else {
+ $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
+ }
+ }
+
+ //Filter log->info
+ $log->info = format_string($log->info);
+
+ // If $log->url has been trimmed short by the db size restriction
+ // code in add_to_log, keep a note so we don't add a link to a broken url
+ $brokenurl=(textlib::strlen($log->url)==100 && textlib::substr($log->url,97)=='...');
+
+ $row = array();
+ if ($course->id == SITEID) {
+ if (empty($log->course)) {
+ $row[] = get_string('site');
+ } else {
+ $row[] = "<a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">". format_string($courses[$log->course])."</a>";
+ }
+ }
+
+ $row[] = userdate($log->time, '%a').' '.userdate($log->time, $strftimedatetime);
+
+ $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
+ $row[] = $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 440, 'width' => 700)));
+
+ $row[] = html_writer::link(new moodle_url("/user/view.php?id={$log->userid}&course={$log->course}"), fullname($log, has_capability('moodle/site:viewfullnames', context_course::instance($course->id))));
+
+ $displayaction="$log->module $log->action";
+ if ($brokenurl) {
+ $row[] = $displayaction;
+ } else {
+ $link = make_log_url($log->module,$log->url);
+ $row[] = $OUTPUT->action_link($link, $displayaction, new popup_action('click', $link, 'fromloglive'), array('height' => 440, 'width' => 700));
+ }
+ $row[] = $log->info;
+ $table->data[] = $row;
+ }
+
+ echo html_writer::table($table);
+ echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
+}
+
+
+function print_mnet_log($hostid, $course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
+ $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
+
+ global $CFG, $DB, $OUTPUT;
+
+ if (!$logs = build_mnet_logs_array($hostid, $course, $user, $date, $order, $page*$perpage, $perpage,
+ $modname, $modid, $modaction, $groupid)) {
+ echo $OUTPUT->notification("No logs found!");
+ echo $OUTPUT->footer();
+ exit;
+ }
+
+ if ($course->id == SITEID) {
+ $courses[0] = '';
+ if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname,c.visible')) {
+ foreach ($ccc as $cc) {
+ $courses[$cc->id] = $cc->shortname;
+ }
+ }
+ }
+
+ $totalcount = $logs['totalcount'];
+ $count=0;
+ $ldcache = array();
+ $tt = getdate(time());
+ $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
+
+ $strftimedatetime = get_string("strftimedatetime");
+
+ echo "<div class=\"info\">\n";
+ print_string("displayingrecords", "", $totalcount);
+ echo "</div>\n";
+
+ echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
+
+ echo "<table class=\"logtable\" cellpadding=\"3\" cellspacing=\"0\">\n";
+ echo "<tr>";
+ if ($course->id == SITEID) {
+ echo "<th class=\"c0 header\">".get_string('course')."</th>\n";
+ }
+ echo "<th class=\"c1 header\">".get_string('time')."</th>\n";
+ echo "<th class=\"c2 header\">".get_string('ip_address')."</th>\n";
+ echo "<th class=\"c3 header\">".get_string('fullnameuser')."</th>\n";
+ echo "<th class=\"c4 header\">".get_string('action')."</th>\n";
+ echo "<th class=\"c5 header\">".get_string('info')."</th>\n";
+ echo "</tr>\n";
+
+ if (empty($logs['logs'])) {
+ echo "</table>\n";
+ return;
+ }
+
+ $row = 1;
+ foreach ($logs['logs'] as $log) {
+
+ $log->info = $log->coursename;
+ $row = ($row + 1) % 2;
+
+ if (isset($ldcache[$log->module][$log->action])) {
+ $ld = $ldcache[$log->module][$log->action];
+ } else {
+ $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
+ $ldcache[$log->module][$log->action] = $ld;
+ }
+ if (0 && $ld && !empty($log->info)) {
+ // ugly hack to make sure fullname is shown correctly
+ if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
+ $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
+ } else {
+ $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
+ }
+ }
+
+ //Filter log->info
+ $log->info = format_string($log->info);
+
+ echo '<tr class="r'.$row.'">';
+ if ($course->id == SITEID) {
+ $courseshortname = format_string($courses[$log->course], true, array('context' => context_course::instance(SITEID)));
+ echo "<td class=\"r$row c0\" >\n";
+ echo " <a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">".$courseshortname."</a>\n";
+ echo "</td>\n";
+ }
+ echo "<td class=\"r$row c1\" align=\"right\">".userdate($log->time, '%a').
+ ' '.userdate($log->time, $strftimedatetime)."</td>\n";
+ echo "<td class=\"r$row c2\" >\n";
+ $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
+ echo $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 400, 'width' => 700)));
+ echo "</td>\n";
+ $fullname = fullname($log, has_capability('moodle/site:viewfullnames', context_course::instance($course->id)));
+ echo "<td class=\"r$row c3\" >\n";
+ echo " <a href=\"$CFG->wwwroot/user/view.php?id={$log->userid}\">$fullname</a>\n";
+ echo "</td>\n";
+ echo "<td class=\"r$row c4\">\n";
+ echo $log->action .': '.$log->module;
+ echo "</td>\n";;
+ echo "<td class=\"r$row c5\">{$log->info}</td>\n";
+ echo "</tr>\n";
+ }
+ echo "</table>\n";
+
+ echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
+}
+
+
+function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
+ $modid, $modaction, $groupid) {
+ global $DB, $CFG;
+
+ require_once($CFG->libdir . '/csvlib.class.php');
+
+ $csvexporter = new csv_export_writer('tab');
+
+ $header = array();
+ $header[] = get_string('course');
+ $header[] = get_string('time');
+ $header[] = get_string('ip_address');
+ $header[] = get_string('fullnameuser');
+ $header[] = get_string('action');
+ $header[] = get_string('info');
+
+ if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
+ $modname, $modid, $modaction, $groupid)) {
+ return false;
+ }
+
+ $courses = array();
+
+ if ($course->id == SITEID) {
+ $courses[0] = '';
+ if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
+ foreach ($ccc as $cc) {
+ $courses[$cc->id] = $cc->shortname;
+ }
+ }
+ } else {
+ $courses[$course->id] = $course->shortname;
+ }
+
+ $count=0;
+ $ldcache = array();
+ $tt = getdate(time());
+ $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
+
+ $strftimedatetime = get_string("strftimedatetime");
+
+ $csvexporter->set_filename('logs', '.txt');
+ $title = array(get_string('savedat').userdate(time(), $strftimedatetime));
+ $csvexporter->add_data($title);
+ $csvexporter->add_data($header);
+
+ if (empty($logs['logs'])) {
+ return true;
+ }
+
+ foreach ($logs['logs'] as $log) {
+ if (isset($ldcache[$log->module][$log->action])) {
+ $ld = $ldcache[$log->module][$log->action];
+ } else {
+ $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
+ $ldcache[$log->module][$log->action] = $ld;
+ }
+ if ($ld && is_numeric($log->info)) {
+ // ugly hack to make sure fullname is shown correctly
+ if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
+ $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
+ } else {
+ $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
+ }
+ }
+
+ //Filter log->info
+ $log->info = format_string($log->info);
+ $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
+
+ $coursecontext = context_course::instance($course->id);
+ $firstField = format_string($courses[$log->course], true, array('context' => $coursecontext));
+ $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
+ $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
+ $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action.' ('.$actionurl.')', $log->info);
+ $csvexporter->add_data($row);
+ }
+ $csvexporter->download_file();
+ return true;
+}
+
+
+function print_log_xls($course, $user, $date, $order='l.time DESC', $modname,
+ $modid, $modaction, $groupid) {
+
+ global $CFG, $DB;
+
+ require_once("$CFG->libdir/excellib.class.php");
+
+ if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
+ $modname, $modid, $modaction, $groupid)) {
+ return false;
+ }
+
+ $courses = array();
+
+ if ($course->id == SITEID) {
+ $courses[0] = '';
+ if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
+ foreach ($ccc as $cc) {
+ $courses[$cc->id] = $cc->shortname;
+ }
+ }
+ } else {
+ $courses[$course->id] = $course->shortname;
+ }
+
+ $count=0;
+ $ldcache = array();
+ $tt = getdate(time());
+ $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
+
+ $strftimedatetime = get_string("strftimedatetime");
+
+ $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
+ $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
+ $filename .= '.xls';
+
+ $workbook = new MoodleExcelWorkbook('-');
+ $workbook->send($filename);
+
+ $worksheet = array();
+ $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
+ get_string('fullnameuser'), get_string('action'), get_string('info'));
+
+ // Creating worksheets
+ for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
+ $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
+ $worksheet[$wsnumber] = $workbook->add_worksheet($sheettitle);
+ $worksheet[$wsnumber]->set_column(1, 1, 30);
+ $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
+ userdate(time(), $strftimedatetime));
+ $col = 0;
+ foreach ($headers as $item) {
+ $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
+ $col++;
+ }
+ }
+
+ if (empty($logs['logs'])) {
+ $workbook->close();
+ return true;
+ }
+
+ $formatDate =& $workbook->add_format();
+ $formatDate->set_num_format(get_string('log_excel_date_format'));
+
+ $row = FIRSTUSEDEXCELROW;
+ $wsnumber = 1;
+ $myxls =& $worksheet[$wsnumber];
+ foreach ($logs['logs'] as $log) {
+ if (isset($ldcache[$log->module][$log->action])) {
+ $ld = $ldcache[$log->module][$log->action];
+ } else {
+ $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
+ $ldcache[$log->module][$log->action] = $ld;
+ }
+ if ($ld && is_numeric($log->info)) {
+ // ugly hack to make sure fullname is shown correctly
+ if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
+ $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
+ } else {
+ $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
+ }
+ }
+
+ // Filter log->info
+ $log->info = format_string($log->info);
+ $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
+
+ if ($nroPages>1) {
+ if ($row > EXCELROWS) {
+ $wsnumber++;
+ $myxls =& $worksheet[$wsnumber];
+ $row = FIRSTUSEDEXCELROW;
+ }
+ }
+
+ $coursecontext = context_course::instance($course->id);
+
+ $myxls->write($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)), '');
+ $myxls->write_date($row, 1, $log->time, $formatDate); // write_date() does conversion/timezone support. MDL-14934
+ $myxls->write($row, 2, $log->ip, '');
+ $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
+ $myxls->write($row, 3, $fullname, '');
+ $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
+ $myxls->write($row, 4, $log->module.' '.$log->action.' ('.$actionurl.')', '');
+ $myxls->write($row, 5, $log->info, '');
+
+ $row++;
+ }
+
+ $workbook->close();
+ return true;
+}
+
+function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
+ $modid, $modaction, $groupid) {
+
+ global $CFG, $DB;
+
+ require_once("$CFG->libdir/odslib.class.php");
+
+ if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
+ $modname, $modid, $modaction, $groupid)) {
+ return false;
+ }
+
+ $courses = array();
+
+ if ($course->id == SITEID) {
+ $courses[0] = '';
+ if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
+ foreach ($ccc as $cc) {
+ $courses[$cc->id] = $cc->shortname;
+ }
+ }
+ } else {
+ $courses[$course->id] = $course->shortname;
+ }
+
+ $count=0;
+ $ldcache = array();
+ $tt = getdate(time());
+ $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
+
+ $strftimedatetime = get_string("strftimedatetime");
+
+ $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
+ $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
+ $filename .= '.ods';
+
+ $workbook = new MoodleODSWorkbook('-');
+ $workbook->send($filename);
+
+ $worksheet = array();
+ $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
+ get_string('fullnameuser'), get_string('action'), get_string('info'));
+
+ // Creating worksheets
+ for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
+ $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
+ $worksheet[$wsnumber] = $workbook->add_worksheet($sheettitle);
+ $worksheet[$wsnumber]->set_column(1, 1, 30);
+ $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
+ userdate(time(), $strftimedatetime));
+ $col = 0;
+ foreach ($headers as $item) {
+ $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
+ $col++;
+ }
+ }
+
+ if (empty($logs['logs'])) {
+ $workbook->close();
+ return true;
+ }
+
+ $formatDate =& $workbook->add_format();
+ $formatDate->set_num_format(get_string('log_excel_date_format'));
+
+ $row = FIRSTUSEDEXCELROW;
+ $wsnumber = 1;
+ $myxls =& $worksheet[$wsnumber];
+ foreach ($logs['logs'] as $log) {
+ if (isset($ldcache[$log->module][$log->action])) {
+ $ld = $ldcache[$log->module][$log->action];
+ } else {
+ $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
+ $ldcache[$log->module][$log->action] = $ld;
+ }
+ if ($ld && is_numeric($log->info)) {
+ // ugly hack to make sure fullname is shown correctly
+ if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
+ $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
+ } else {
+ $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
+ }
+ }
+
+ // Filter log->info
+ $log->info = format_string($log->info);
+ $log->info = strip_tags(urldecode($log->info)); // Some XSS protection
+
+ if ($nroPages>1) {
+ if ($row > EXCELROWS) {
+ $wsnumber++;
+ $myxls =& $worksheet[$wsnumber];
+ $row = FIRSTUSEDEXCELROW;
+ }
+ }
+
+ $coursecontext = context_course::instance($course->id);
+
+ $myxls->write_string($row, 0, format_string($courses[$log->course], true, array('context' => $coursecontext)));
+ $myxls->write_date($row, 1, $log->time);
+ $myxls->write_string($row, 2, $log->ip);
+ $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
+ $myxls->write_string($row, 3, $fullname);
+ $actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
+ $myxls->write_string($row, 4, $log->module.' '.$log->action.' ('.$actionurl.')');
+ $myxls->write_string($row, 5, $log->info);
+
+ $row++;
+ }
+
+ $workbook->close();
+ return true;
+}
+
+
+function print_overview($courses, array $remote_courses=array()) {
+ global $CFG, $USER, $DB, $OUTPUT;
+
+ $htmlarray = array();
+ if ($modules = $DB->get_records('modules')) {
+ foreach ($modules as $mod) {
+ if (file_exists(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php')) {
+ include_once(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php');
+ $fname = $mod->name.'_print_overview';
+ if (function_exists($fname)) {
+ $fname($courses,$htmlarray);
+ }
+ }
+ }
+ }
+ foreach ($courses as $course) {
+ $fullname = format_string($course->fullname, true, array('context' => context_course::instance($course->id)));
+ echo $OUTPUT->box_start('coursebox');
+ $attributes = array('title' => s($fullname));
+ if (empty($course->visible)) {
+ $attributes['class'] = 'dimmed';
+ }
+ echo $OUTPUT->heading(html_writer::link(
+ new moodle_url('/course/view.php', array('id' => $course->id)), $fullname, $attributes), 3);
+ if (array_key_exists($course->id,$htmlarray)) {
+ foreach ($htmlarray[$course->id] as $modname => $html) {
+ echo $html;
+ }
+ }
+ echo $OUTPUT->box_end();
+ }
+
+ if (!empty($remote_courses)) {
+ echo $OUTPUT->heading(get_string('remotecourses', 'mnet'));
+ }
+ foreach ($remote_courses as $course) {
+ echo $OUTPUT->box_start('coursebox');
+ $attributes = array('title' => s($course->fullname));
+ echo $OUTPUT->heading(html_writer::link(
+ new moodle_url('/auth/mnet/jump.php', array('hostid' => $course->hostid, 'wantsurl' => '/course/view.php?id='.$course->remoteid)),
+ format_string($course->shortname),
+ $attributes) . ' (' . format_string($course->hostname) . ')', 3);
+ echo $OUTPUT->box_end();
+ }
+}
+
+
+/**
+ * This function trawls through the logs looking for
+ * anything new since the user's last login
+ */
+function print_recent_activity($course) {
+ // $course is an object
+ global $CFG, $USER, $SESSION, $DB, $OUTPUT;
+
+ $context = context_course::instance($course->id);
+
+ $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
+
+ $timestart = round(time() - COURSE_MAX_RECENT_PERIOD, -2); // better db caching for guests - 100 seconds
+
+ if (!isguestuser()) {
+ if (!empty($USER->lastcourseaccess[$course->id])) {
+ if ($USER->lastcourseaccess[$course->id] > $timestart) {
+ $timestart = $USER->lastcourseaccess[$course->id];
+ }
+ }
+ }
+
+ echo '<div class="activitydate">';
+ echo get_string('activitysince', '', userdate($timestart));
+ echo '</div>';
+ echo '<div class="activityhead">';
+
+ echo '<a href="'.$CFG->wwwroot.'/course/recent.php?id='.$course->id.'">'.get_string('recentactivityreport').'</a>';
+
+ echo "</div>\n";
+
+ $content = false;
+
+/// Firstly, have there been any new enrolments?
+
+ $users = get_recent_enrolments($course->id, $timestart);
+
+ //Accessibility: new users now appear in an <OL> list.
+ if ($users) {
+ echo '<div class="newusers">';
+ echo $OUTPUT->heading(get_string("newusers").':', 3);
+ $content = true;
+ echo "<ol class=\"list\">\n";
+ foreach ($users as $user) {
+ $fullname = fullname($user, $viewfullnames);
+ echo '<li class="name"><a href="'."$CFG->wwwroot/user/view.php?id=$user->id&course=$course->id\">$fullname</a></li>\n";
+ }
+ echo "</ol>\n</div>\n";
+ }
+
+/// Next, have there been any modifications to the course structure?
+
+ $modinfo = get_fast_modinfo($course);
+
+ $changelist = array();
+
+ $logs = $DB->get_records_select('log', "time > ? AND course = ? AND
+ module = 'course' AND
+ (action = 'add mod' OR action = 'update mod' OR action = 'delete mod')",
+ array($timestart, $course->id), "id ASC");
+
+ if ($logs) {
+ $actions = array('add mod', 'update mod', 'delete mod');
+ $newgones = array(); // added and later deleted items
+ foreach ($logs as $key => $log) {
+ if (!in_array($log->action, $actions)) {
+ continue;
+ }
+ $info = explode(' ', $log->info);
+
+ // note: in most cases I replaced hardcoding of label with use of
+ // $cm->has_view() but it was not possible to do this here because
+ // we don't necessarily have the $cm for it
+ if ($info[0] == 'label') { // Labels are ignored in recent activity
+ continue;
+ }
+
+ if (count($info) != 2) {
+ debugging("Incorrect log entry info: id = ".$log->id, DEBUG_DEVELOPER);
+ continue;
+ }
+
+ $modname = $info[0];
+ $instanceid = $info[1];
+
+ if ($log->action == 'delete mod') {
+ // unfortunately we do not know if the mod was visible
+ if (!array_key_exists($log->info, $newgones)) {
+ $strdeleted = get_string('deletedactivity', 'moodle', get_string('modulename', $modname));
+ $changelist[$log->info] = array ('operation' => 'delete', 'text' => $strdeleted);
+ }
+ } else {
+ if (!isset($modinfo->instances[$modname][$instanceid])) {
+ if ($log->action == 'add mod') {
+ // do not display added and later deleted activities
+ $newgones[$log->info] = true;
+ }
+ continue;
+ }
+ $cm = $modinfo->instances[$modname][$instanceid];
+ if (!$cm->uservisible) {
+ continue;
+ }
+
+ if ($log->action == 'add mod') {
+ $stradded = get_string('added', 'moodle', get_string('modulename', $modname));
+ $changelist[$log->info] = array('operation' => 'add', 'text' => "$stradded:<br /><a href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id={$cm->id}\">".format_string($cm->name, true)."</a>");
+
+ } else if ($log->action == 'update mod' and empty($changelist[$log->info])) {
+ $strupdated = get_string('updated', 'moodle', get_string('modulename', $modname));
+ $changelist[$log->info] = array('operation' => 'update', 'text' => "$strupdated:<br /><a href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id={$cm->id}\">".format_string($cm->name, true)."</a>");
+ }
+ }
+ }
+ }
+
+ if (!empty($changelist)) {
+ echo $OUTPUT->heading(get_string("courseupdates").':', 3);
+ $content = true;
+ foreach ($changelist as $changeinfo => $change) {
+ echo '<p class="activity">'.$change['text'].'</p>';
+ }
+ }
+
+/// Now display new things from each module
+
+ $usedmodules = array();
+ foreach($modinfo->cms as $cm) {
+ if (isset($usedmodules[$cm->modname])) {
+ continue;
+ }
+ if (!$cm->uservisible) {
+ continue;
+ }
+ $usedmodules[$cm->modname] = $cm->modname;
+ }
+
+ foreach ($usedmodules as $modname) { // Each module gets it's own logs and prints them
+ if (file_exists($CFG->dirroot.'/mod/'.$modname.'/lib.php')) {
+ include_once($CFG->dirroot.'/mod/'.$modname.'/lib.php');
+ $print_recent_activity = $modname.'_print_recent_activity';
+ if (function_exists($print_recent_activity)) {
+ // NOTE: original $isteacher (second parameter below) was replaced with $viewfullnames!
+ $content = $print_recent_activity($course, $viewfullnames, $timestart) || $content;
+ }
+ } else {
+ debugging("Missing lib.php in lib/{$modname} - please reinstall files or uninstall the module");
+ }
+ }
+
+ if (! $content) {
+ echo '<p class="message">'.get_string('nothingnew').'</p>';
+ }
+}
+
+/**
+ * For a given course, returns an array of course activity objects
+ * Each item in the array contains he following properties:
+ */
+function get_array_of_activities($courseid) {
+// cm - course module id
+// mod - name of the module (eg forum)
+// section - the number of the section (eg week or topic)
+// name - the name of the instance
+// visible - is the instance visible or not
+// groupingid - grouping id
+// groupmembersonly - is this instance visible to group members only
+// extra - contains extra string to include in any link
+ global $CFG, $DB;
+ if(!empty($CFG->enableavailability)) {
+ require_once($CFG->libdir.'/conditionlib.php');
+ }
+
+ $course = $DB->get_record('course', array('id'=>$courseid));
+
+ if (empty($course)) {
+ throw new moodle_exception('courseidnotfound');
+ }
+
+ $mod = array();
+
+ $rawmods = get_course_mods($courseid);
+ if (empty($rawmods)) {
+ return $mod; // always return array
+ }
+
+ if ($sections = $DB->get_records("course_sections", array("course"=>$courseid), "section ASC")) {
+ foreach ($sections as $section) {
+ if (!empty($section->sequence)) {
+ $sequence = explode(",", $section->sequence);
+ foreach ($sequence as $seq) {
+ if (empty($rawmods[$seq])) {
+ continue;
+ }
+ $mod[$seq] = new stdClass();
+ $mod[$seq]->id = $rawmods[$seq]->instance;
+ $mod[$seq]->cm = $rawmods[$seq]->id;
+ $mod[$seq]->mod = $rawmods[$seq]->modname;
+
+ // Oh dear. Inconsistent names left here for backward compatibility.
+ $mod[$seq]->section = $section->section;
+ $mod[$seq]->sectionid = $rawmods[$seq]->section;
+
+ $mod[$seq]->module = $rawmods[$seq]->module;
+ $mod[$seq]->added = $rawmods[$seq]->added;
+ $mod[$seq]->score = $rawmods[$seq]->score;
+ $mod[$seq]->idnumber = $rawmods[$seq]->idnumber;
+ $mod[$seq]->visible = $rawmods[$seq]->visible;
+ $mod[$seq]->visibleold = $rawmods[$seq]->visibleold;
+ $mod[$seq]->groupmode = $rawmods[$seq]->groupmode;
+ $mod[$seq]->groupingid = $rawmods[$seq]->groupingid;
+ $mod[$seq]->groupmembersonly = $rawmods[$seq]->groupmembersonly;
+ $mod[$seq]->indent = $rawmods[$seq]->indent;
+ $mod[$seq]->completion = $rawmods[$seq]->completion;
+ $mod[$seq]->extra = "";
+ $mod[$seq]->completiongradeitemnumber =
+ $rawmods[$seq]->completiongradeitemnumber;
+ $mod[$seq]->completionview = $rawmods[$seq]->completionview;
+ $mod[$seq]->completionexpected = $rawmods[$seq]->completionexpected;
+ $mod[$seq]->availablefrom = $rawmods[$seq]->availablefrom;
+ $mod[$seq]->availableuntil = $rawmods[$seq]->availableuntil;
+ $mod[$seq]->showavailability = $rawmods[$seq]->showavailability;
+ $mod[$seq]->showdescription = $rawmods[$seq]->showdescription;
+ if (!empty($CFG->enableavailability)) {
+ condition_info::fill_availability_conditions($rawmods[$seq]);
+ $mod[$seq]->conditionscompletion = $rawmods[$seq]->conditionscompletion;
+ $mod[$seq]->conditionsgrade = $rawmods[$seq]->conditionsgrade;
+ $mod[$seq]->conditionsfield = $rawmods[$seq]->conditionsfield;
+ }
+
+ $modname = $mod[$seq]->mod;
+ $functionname = $modname."_get_coursemodule_info";
+
+ if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
+ continue;
+ }
+
+ include_once("$CFG->dirroot/mod/$modname/lib.php");
+
+ if ($hasfunction = function_exists($functionname)) {
+ if ($info = $functionname($rawmods[$seq])) {
+ if (!empty($info->icon)) {
+ $mod[$seq]->icon = $info->icon;
+ }
+ if (!empty($info->iconcomponent)) {
+ $mod[$seq]->iconcomponent = $info->iconcomponent;
+ }
+ if (!empty($info->name)) {
+ $mod[$seq]->name = $info->name;
+ }
+ if ($info instanceof cached_cm_info) {
+ // When using cached_cm_info you can include three new fields
+ // that aren't available for legacy code
+ if (!empty($info->content)) {
+ $mod[$seq]->content = $info->content;
+ }
+ if (!empty($info->extraclasses)) {
+ $mod[$seq]->extraclasses = $info->extraclasses;
+ }
+ if (!empty($info->iconurl)) {
+ $mod[$seq]->iconurl = $info->iconurl;
+ }
+ if (!empty($info->onclick)) {
+ $mod[$seq]->onclick = $info->onclick;
+ }
+ if (!empty($info->customdata)) {
+ $mod[$seq]->customdata = $info->customdata;
+ }
+ } else {
+ // When using a stdclass, the (horrible) deprecated ->extra field
+ // is available for BC
+ if (!empty($info->extra)) {
+ $mod[$seq]->extra = $info->extra;
+ }
+ }
+ }
+ }
+ // When there is no modname_get_coursemodule_info function,
+ // but showdescriptions is enabled, then we use the 'intro'
+ // and 'introformat' fields in the module table
+ if (!$hasfunction && $rawmods[$seq]->showdescription) {
+ if ($modvalues = $DB->get_record($rawmods[$seq]->modname,
+ array('id' => $rawmods[$seq]->instance), 'name, intro, introformat')) {
+ // Set content from intro and introformat. Filters are disabled
+ // because we filter it with format_text at display time
+ $mod[$seq]->content = format_module_intro($rawmods[$seq]->modname,
+ $modvalues, $rawmods[$seq]->id, false);
+
+ // To save making another query just below, put name in here
+ $mod[$seq]->name = $modvalues->name;
+ }
+ }
+ if (!isset($mod[$seq]->name)) {
+ $mod[$seq]->name = $DB->get_field($rawmods[$seq]->modname, "name", array("id"=>$rawmods[$seq]->instance));
+ }
+
+ // Minimise the database size by unsetting default options when they are
+ // 'empty'. This list corresponds to code in the cm_info constructor.
+ foreach (array('idnumber', 'groupmode', 'groupingid', 'groupmembersonly',
+ 'indent', 'completion', 'extra', 'extraclasses', 'iconurl', 'onclick', 'content',
+ 'icon', 'iconcomponent', 'customdata', 'showavailability', 'availablefrom',
+ 'availableuntil', 'conditionscompletion', 'conditionsgrade',
+ 'completionview', 'completionexpected', 'score', 'showdescription')
+ as $property) {
+ if (property_exists($mod[$seq], $property) &&
+ empty($mod[$seq]->{$property})) {
+ unset($mod[$seq]->{$property});
+ }
+ }
+ // Special case: this value is usually set to null, but may be 0
+ if (property_exists($mod[$seq], 'completiongradeitemnumber') &&
+ is_null($mod[$seq]->completiongradeitemnumber)) {
+ unset($mod[$seq]->completiongradeitemnumber);
+ }
+ }
+ }
+ }
+ }
+ return $mod;
+}
+
+/**
+ * Returns the localised human-readable names of all used modules
+ *
+ * @param bool $plural if true returns the plural forms of the names
+ * @return array where key is the module name (component name without 'mod_') and
+ * the value is the human-readable string. Array sorted alphabetically by value
+ */
+function get_module_types_names($plural = false) {
+ static $modnames = null;
+ global $DB, $CFG;
+ if ($modnames === null) {
+ $modnames = array(0 => array(), 1 => array());
+ if ($allmods = $DB->get_records("modules")) {
+ foreach ($allmods as $mod) {
+ if (file_exists("$CFG->dirroot/mod/$mod->name/lib.php") && $mod->visible) {
+ $modnames[0][$mod->name] = get_string("modulename", "$mod->name");
+ $modnames[1][$mod->name] = get_string("modulenameplural", "$mod->name");
+ }
+ }
+ collatorlib::asort($modnames[0]);
+ collatorlib::asort($modnames[1]);
+ }
+ }
+ return $modnames[(int)$plural];
+}
+
+/**
+ * Set highlighted section. Only one section can be highlighted at the time.
+ *
+ * @param int $courseid course id
+ * @param int $marker highlight section with this number, 0 means remove higlightin
+ * @return void
+ */
+function course_set_marker($courseid, $marker) {
+ global $DB;
+ $DB->set_field("course", "marker", $marker, array('id' => $courseid));
+ format_base::reset_course_cache($courseid);
+}
+
+/**
+ * For a given course section, marks it visible or hidden,
+ * and does the same for every activity in that section
+ *
+ * @param int $courseid course id
+ * @param int $sectionnumber The section number to adjust
+ * @param int $visibility The new visibility
+ * @return array A list of resources which were hidden in the section
+ */
+function set_section_visible($courseid, $sectionnumber, $visibility) {
+ global $DB;
+
+ $resourcestotoggle = array();
+ if ($section = $DB->get_record("course_sections", array("course"=>$courseid, "section"=>$sectionnumber))) {
+ $DB->set_field("course_sections", "visible", "$visibility", array("id"=>$section->id));
+ if (!empty($section->sequence)) {
+ $modules = explode(",", $section->sequence);
+ foreach ($modules as $moduleid) {
+ if ($cm = $DB->get_record('course_modules', array('id' => $moduleid), 'visible, visibleold')) {
+ if ($visibility) {
+ // As we unhide the section, we use the previously saved visibility stored in visibleold.
+ set_coursemodule_visible($moduleid, $cm->visibleold);
+ } else {
+ // We hide the section, so we hide the module but we store the original state in visibleold.
+ set_coursemodule_visible($moduleid, 0);
+ $DB->set_field('course_modules', 'visibleold', $cm->visible, array('id' => $moduleid));
+ }
+ }
+ }
+ }
+ rebuild_course_cache($courseid, true);
+
+ // Determine which modules are visible for AJAX update
+ if (!empty($modules)) {
+ list($insql, $params) = $DB->get_in_or_equal($modules);
+ $select = 'id ' . $insql . ' AND visible = ?';
+ array_push($params, $visibility);
+ if (!$visibility) {
+ $select .= ' AND visibleold = 1';
+ }
+ $resourcestotoggle = $DB->get_fieldset_select('course_modules', 'id', $select, $params);
+ }
+ }
+ return $resourcestotoggle;
+}
+
+/**
+ * Obtains shared data that is used in print_section when displaying a
+ * course-module entry.
+ *
+ * Calls format_text or format_string as appropriate, and obtains the correct icon.
+ *
+ * This data is also used in other areas of the code.
+ * @param cm_info $cm Course-module data (must come from get_fast_modinfo)
+ * @param object $course Moodle course object
+ * @return array An array with the following values in this order:
+ * $content (optional extra content for after link),
+ * $instancename (text of link)
+ */
+function get_print_section_cm_text(cm_info $cm, $course) {
+ global $OUTPUT;
+
+ // Get content from modinfo if specified. Content displays either
+ // in addition to the standard link (below), or replaces it if
+ // the link is turned off by setting ->url to null.
+ if (($content = $cm->get_content()) !== '') {
+ // Improve filter performance by preloading filter setttings for all
+ // activities on the course (this does nothing if called multiple
+ // times)
+ filter_preload_activities($cm->get_modinfo());
+
+ // Get module context
+ $modulecontext = context_module::instance($cm->id);
+ $labelformatoptions = new stdClass();
+ $labelformatoptions->noclean = true;
+ $labelformatoptions->overflowdiv = true;
+ $labelformatoptions->context = $modulecontext;
+ $content = format_text($content, FORMAT_HTML, $labelformatoptions);
+ } else {
+ $content = '';
+ }
+
+ // Get course context
+ $coursecontext = context_course::instance($course->id);
+ $stringoptions = new stdClass;
+ $stringoptions->context = $coursecontext;
+ $instancename = format_string($cm->name, true, $stringoptions);
+ return array($content, $instancename);
+}
+
+/**
+ * Prints a section full of activity modules
+ *
+ * @param stdClass $course The course
+ * @param stdClass|section_info $section The section object containing properties id and section
+ * @param array $mods (argument not used)
+ * @param array $modnamesused (argument not used)
+ * @param bool $absolute All links are absolute
+ * @param string $width Width of the container
+ * @param bool $hidecompletion Hide completion status
+ * @param int $sectionreturn The section to return to
+ * @return void
+ */
+function print_section($course, $section, $mods, $modnamesused, $absolute=false, $width="100%", $hidecompletion=false, $sectionreturn=null) {
+ global $CFG, $USER, $DB, $PAGE, $OUTPUT;
+
+ static $initialised;
+
+ static $groupbuttons;
+ static $groupbuttonslink;
+ static $isediting;
+ static $ismoving;
+ static $strmovehere;
+ static $strmovefull;
+ static $strunreadpostsone;
+
+ if (!isset($initialised)) {
+ $groupbuttons = ($course->groupmode or (!$course->groupmodeforce));
+ $groupbuttonslink = (!$course->groupmodeforce);
+ $isediting = $PAGE->user_is_editing();
+ $ismoving = $isediting && ismoving($course->id);
+ if ($ismoving) {
+ $strmovehere = get_string("movehere");
+ $strmovefull = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
+ }
+ $initialised = true;
+ }
+
+ $modinfo = get_fast_modinfo($course);
+ $completioninfo = new completion_info($course);
+
+ //Accessibility: replace table with list <ul>, but don't output empty list.
+ if (!empty($modinfo->sections[$section->section])) {
+
+ // Fix bug #5027, don't want style=\"width:$width\".
+ echo "<ul class=\"section img-text\">\n";
+
+ foreach ($modinfo->sections[$section->section] as $modnumber) {
+ $mod = $modinfo->cms[$modnumber];
+
+ if ($ismoving and $mod->id == $USER->activitycopy) {
+ // do not display moving mod
+ continue;
+ }
+
+ // We can continue (because it will not be displayed at all)
+ // if:
+ // 1) The activity is not visible to users
+ // and
+ // 2a) The 'showavailability' option is not set (if that is set,
+ // we need to display the activity so we can show
+ // availability info)
+ // or
+ // 2b) The 'availableinfo' is empty, i.e. the activity was
+ // hidden in a way that leaves no info, such as using the
+ // eye icon.
+ if (!$mod->uservisible &&
+ (empty($mod->showavailability) ||
+ empty($mod->availableinfo))) {
+ // visibility shortcut
+ continue;
+ }
+
+ // In some cases the activity is visible to user, but it is
+ // dimmed. This is done if viewhiddenactivities is true and if:
+ // 1. the activity is not visible, or
+ // 2. the activity has dates set which do not include current, or
+ // 3. the activity has any other conditions set (regardless of whether
+ // current user meets them)
+ $modcontext = context_module::instance($mod->id);
+ $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext);
+ $accessiblebutdim = false;
+ $conditionalhidden = false;
+ if ($canviewhidden) {
+ $accessiblebutdim = !$mod->visible;
+ if (!empty($CFG->enableavailability)) {
+ $conditionalhidden = $mod->availablefrom > time() ||
+ ($mod->availableuntil && $mod->availableuntil < time()) ||
+ count($mod->conditionsgrade) > 0 ||
+ count($mod->conditionscompletion) > 0;
+ }
+ $accessiblebutdim = $conditionalhidden || $accessiblebutdim;
+ }
+
+ $liclasses = array();
+ $liclasses[] = 'activity';
+ $liclasses[] = $mod->modname;
+ $liclasses[] = 'modtype_'.$mod->modname;
+ $extraclasses = $mod->get_extra_classes();
+ if ($extraclasses) {
+ $liclasses = array_merge($liclasses, explode(' ', $extraclasses));
+ }
+ echo html_writer::start_tag('li', array('class'=>join(' ', $liclasses), 'id'=>'module-'.$modnumber));
+ if ($ismoving) {
+ echo '<a title="'.$strmovefull.'"'.
+ ' href="'.$CFG->wwwroot.'/course/mod.php?moveto='.$mod->id.'&sesskey='.sesskey().'">'.
+ '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
+ ' alt="'.$strmovehere.'" /></a><br />
+ ';
+ }
+
+ $classes = array('mod-indent');
+ if (!empty($mod->indent)) {
+ $classes[] = 'mod-indent-'.$mod->indent;
+ if ($mod->indent > 15) {
+ $classes[] = 'mod-indent-huge';
+ }
+ }
+ echo html_writer::start_tag('div', array('class'=>join(' ', $classes)));
+
+ // Get data about this course-module
+ list($content, $instancename) =
+ get_print_section_cm_text($modinfo->cms[$modnumber], $course);
+
+ //Accessibility: for files get description via icon, this is very ugly hack!
+ $altname = '';
+ $altname = $mod->modfullname;
+ // Avoid unnecessary duplication: if e.g. a forum name already
+ // includes the word forum (or Forum, etc) then it is unhelpful
+ // to include that in the accessible description that is added.
+ if (false !== strpos(textlib::strtolower($instancename),
+ textlib::strtolower($altname))) {
+ $altname = '';
+ }
+ // File type after name, for alphabetic lists (screen reader).
+ if ($altname) {
+ $altname = get_accesshide(' '.$altname);
+ }
+
+ // Start the div for the activity title, excluding the edit icons.
+ echo html_writer::start_tag('div', array('class' => 'activityinstance'));
+
+ // We may be displaying this just in order to show information
+ // about visibility, without the actual link
+ $contentpart = '';
+ if ($mod->uservisible) {
+ // Nope - in this case the link is fully working for user
+ $linkclasses = '';
+ $textclasses = '';
+ if ($accessiblebutdim) {
+ $linkclasses .= ' dimmed';
+ $textclasses .= ' dimmed_text';
+ if ($conditionalhidden) {
+ $linkclasses .= ' conditionalhidden';
+ $textclasses .= ' conditionalhidden';
+ }
+ $accesstext = get_accesshide(get_string('hiddenfromstudents').': ');
+ } else {
+ $accesstext = '';
+ }
+ if ($linkclasses) {
+ $linkcss = trim($linkclasses) . ' ';
+ } else {
+ $linkcss = '';
+ }
+ if ($textclasses) {
+ $textcss = trim($textclasses) . ' ';
+ } else {
+ $textcss = '';
+ }
+
+ // Get on-click attribute value if specified and decode the onclick - it
+ // has already been encoded for display (puke).
+ $onclick = htmlspecialchars_decode($mod->get_on_click(), ENT_QUOTES);
+
+ $groupinglabel = '';
+ if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
+ $groupings = groups_get_all_groupings($course->id);
+ $groupinglabel = html_writer::tag('span', '('.format_string($groupings[$mod->groupingid]->name).')',
+ array('class' => 'groupinglabel'));
+ }
+
+ if ($url = $mod->get_url()) {
+ // Display link itself.
+ $activitylink = html_writer::empty_tag('img', array('src' => $mod->get_icon_url(),
+ 'class' => 'iconlarge activityicon', 'alt' => $mod->modfullname)) . $accesstext .
+ html_writer::tag('span', $instancename . $altname, array('class' => 'instancename'));
+ echo html_writer::link($url, $activitylink, array('class' => $linkcss, 'onclick' => $onclick)) .
+ $groupinglabel;
+
+ // If specified, display extra content after link.
+ if ($content) {
+ $contentpart = html_writer::tag('div', $content, array('class' =>
+ trim('contentafterlink ' . $textclasses)));
+ }
+ } else {
+ // No link, so display only content.
+ $contentpart = html_writer::tag('div', $accesstext . $content, array('class' => $textcss));
+ }
+
+ } else {
+ $textclasses = $extraclasses;
+ $textclasses .= ' dimmed_text';
+ if ($textclasses) {
+ $textcss = 'class="' . trim($textclasses) . '" ';
+ } else {
+ $textcss = '';
+ }
+ $accesstext = '<span class="accesshide">' .
+ get_string('notavailableyet', 'condition') .
+ ': </span>';
+
+ if ($url = $mod->get_url()) {
+ // Display greyed-out text of link
+ echo '<div ' . $textcss . $mod->extra .
+ ' >' . '<img src="' . $mod->get_icon_url() .
+ '" class="activityicon" alt="" /> <span>'. $instancename . $altname .
+ '</span></div>';
+
+ // Do not display content after link when it is greyed out like this.
+ } else {
+ // No link, so display only content (also greyed)
+ $contentpart = '<div ' . $textcss . $mod->extra . '>' .
+ $accesstext . $content . '</div>';
+ }
+ }
+
+ // Module can put text after the link (e.g. forum unread)
+ echo $mod->get_after_link();
+
+ // Closing the tag which contains everything but edit icons. $contentpart should not be part of this.
+ echo html_writer::end_tag('div');
+
+ // If there is content but NO link (eg label), then display the
+ // content here (BEFORE any icons). In this case cons must be
+ // displayed after the content so that it makes more sense visually
+ // and for accessibility reasons, e.g. if you have a one-line label
+ // it should work similarly (at least in terms of ordering) to an
+ // activity.
+ if (empty($url)) {
+ echo $contentpart;
+ }
+
+ if ($isediting) {
+ if ($groupbuttons and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
+ if (! $mod->groupmodelink = $groupbuttonslink) {
+ $mod->groupmode = $course->groupmode;
+ }
+
+ } else {
+ $mod->groupmode = false;
+ }
+ echo make_editing_buttons($mod, $absolute, true, $mod->indent, $sectionreturn);
+ echo $mod->get_after_edit_icons();
+ }
+
+ // Completion
+ $completion = $hidecompletion
+ ? COMPLETION_TRACKING_NONE
+ : $completioninfo->is_enabled($mod);
+ if ($completion!=COMPLETION_TRACKING_NONE && isloggedin() &&
+ !isguestuser() && $mod->uservisible) {
+ $completiondata = $completioninfo->get_data($mod,true);
+ $completionicon = '';
+ if ($isediting) {
+ switch ($completion) {
+ case COMPLETION_TRACKING_MANUAL :
+ $completionicon = 'manual-enabled'; break;
+ case COMPLETION_TRACKING_AUTOMATIC :
+ $completionicon = 'auto-enabled'; break;
+ default: // wtf
+ }
+ } else if ($completion==COMPLETION_TRACKING_MANUAL) {
+ switch($completiondata->completionstate) {
+ case COMPLETION_INCOMPLETE:
+ $completionicon = 'manual-n'; break;
+ case COMPLETION_COMPLETE:
+ $completionicon = 'manual-y'; break;
+ }
+ } else { // Automatic
+ switch($completiondata->completionstate) {
+ case COMPLETION_INCOMPLETE:
+ $completionicon = 'auto-n'; break;
+ case COMPLETION_COMPLETE:
+ $completionicon = 'auto-y'; break;
+ case COMPLETION_COMPLETE_PASS:
+ $completionicon = 'auto-pass'; break;
+ case COMPLETION_COMPLETE_FAIL:
+ $completionicon = 'auto-fail'; break;
+ }
+ }
+ if ($completionicon) {
+ $imgsrc = $OUTPUT->pix_url('i/completion-'.$completionicon);
+ $formattedname = format_string($mod->name, true, array('context' => $modcontext));
+ $imgalt = get_string('completion-alt-' . $completionicon, 'completion', $formattedname);
+ if ($completion == COMPLETION_TRACKING_MANUAL && !$isediting) {
+ $imgtitle = get_string('completion-title-' . $completionicon, 'completion', $formattedname);
+ $newstate =
+ $completiondata->completionstate==COMPLETION_COMPLETE
+ ? COMPLETION_INCOMPLETE
+ : COMPLETION_COMPLETE;
+ // In manual mode the icon is a toggle form...
+
+ // If this completion state is used by the
+ // conditional activities system, we need to turn
+ // off the JS.
+ if (!empty($CFG->enableavailability) &&
+ condition_info::completion_value_used_as_condition($course, $mod)) {
+ $extraclass = ' preventjs';
+ } else {
+ $extraclass = '';
+ }
+ echo html_writer::start_tag('form', array(
+ 'class' => 'togglecompletion' . $extraclass,
+ 'method' => 'post',
+ 'action' => $CFG->wwwroot . '/course/togglecompletion.php'));
+ echo html_writer::start_tag('div');
+ echo html_writer::empty_tag('input', array(
+ 'type' => 'hidden', 'name' => 'id', 'value' => $mod->id));
+ echo html_writer::empty_tag('input', array(
+ 'type' => 'hidden', 'name' => 'modulename',
+ 'value' => $mod->name));
+ echo html_writer::empty_tag('input', array(
+ 'type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
+ echo html_writer::empty_tag('input', array(
+ 'type' => 'hidden', 'name' => 'completionstate',
+ 'value' => $newstate));
+ echo html_writer::empty_tag('input', array(
+ 'type' => 'image', 'src' => $imgsrc, 'alt' => $imgalt, 'title' => $imgtitle));
+ echo html_writer::end_tag('div');
+ echo html_writer::end_tag('form');
+ } else {
+ // In auto mode, or when editing, the icon is just an image
+ echo "<span class='autocompletion'>";
+ echo "<img src='$imgsrc' alt='$imgalt' title='$imgalt' /></span>";
+ }
+ }
+ }
+
+ // If there is content AND a link, then display the content here
+ // (AFTER any icons). Otherwise it was displayed before
+ if (!empty($url)) {
+ echo $contentpart;
+ }
+
+ // Show availability information (for someone who isn't allowed to
+ // see the activity itself, or for staff)
+ if (!$mod->uservisible) {
+ echo '<div class="availabilityinfo">'.$mod->availableinfo.'</div>';
+ } else if ($canviewhidden && !empty($CFG->enableavailability)) {
+ // Don't add availability information if user is not editing and activity is hidden.
+ if ($mod->visible || $PAGE->user_is_editing()) {
+ $hidinfoclass = '';
+ if (!$mod->visible) {
+ $hidinfoclass = 'hide';
+ }
+ $ci = new condition_info($mod);
+ $fullinfo = $ci->get_full_information();
+ if($fullinfo) {
+ echo '<div class="availabilityinfo '.$hidinfoclass.'">'.get_string($mod->showavailability
+ ? 'userrestriction_visible'
+ : 'userrestriction_hidden','condition',
+ $fullinfo).'</div>';
+ }
+ }
+ }
+
+ echo html_writer::end_tag('div');
+ echo html_writer::end_tag('li')."\n";
+ }
+
+ } elseif ($ismoving) {
+ echo "<ul class=\"section\">\n";
+ }
+
+ if ($ismoving) {
+ echo '<li><a title="'.$strmovefull.'"'.
+ ' href="'.$CFG->wwwroot.'/course/mod.php?movetosection='.$section->id.'&sesskey='.sesskey().'">'.
+ '<img class="movetarget" src="'.$OUTPUT->pix_url('movehere') . '" '.
+ ' alt="'.$strmovehere.'" /></a></li>
+ ';
+ }
+ if (!empty($modinfo->sections[$section->section]) || $ismoving) {
+ echo "</ul><!--class='section'-->\n\n";
+ }
+}
+
+/**
+ * Prints the menus to add activities and resources.
+ *
+ * @param stdClass $course The course
+ * @param int $section relative section number (field course_sections.section)
+ * @param null|array $modnames An array containing the list of modules and their names
+ * if omitted will be taken from get_module_types_names()
+ * @param bool $vertical Vertical orientation
+ * @param bool $return Return the menus or send them to output
+ * @param int $sectionreturn The section to link back to
+ * @return void|string depending on $return
+ */
+function print_section_add_menus($course, $section, $modnames = null, $vertical=false, $return=false, $sectionreturn=null) {
+ global $CFG, $OUTPUT;
+
+ if ($modnames === null) {
+ $modnames = get_module_types_names();
+ }
+
+ // check to see if user can add menus and there are modules to add
+ if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id))
+ || empty($modnames)) {
+ if ($return) {
+ return '';
+ } else {
+ return false;
+ }
+ }
+
+ // Retrieve all modules with associated metadata
+ $modules = get_module_metadata($course, $modnames, $sectionreturn);
+
+ // We'll sort resources and activities into two lists
+ $resources = array();
+ $activities = array();
+
+ // We need to add the section section to the link for each module
+ $sectionlink = '§ion=' . $section . '&sr=' . $sectionreturn;
+
+ foreach ($modules as $module) {
+ if (isset($module->types)) {
+ // This module has a subtype
+ // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
+ $subtypes = array();
+ foreach ($module->types as $subtype) {
+ $subtypes[$subtype->link . $sectionlink] = $subtype->title;
+ }
+
+ // Sort module subtypes into the list
+ if (!empty($module->title)) {
+ // This grouping has a name
+ if ($module->archetype == MOD_CLASS_RESOURCE) {
+ $resources[] = array($module->title=>$subtypes);
+ } else {
+ $activities[] = array($module->title=>$subtypes);
+ }
+ } else {
+ // This grouping does not have a name
+ if ($module->archetype == MOD_CLASS_RESOURCE) {
+ $resources = array_merge($resources, $subtypes);
+ } else {
+ $activities = array_merge($activities, $subtypes);
+ }
+ }
+ } else {
+ // This module has no subtypes
+ if ($module->archetype == MOD_ARCHETYPE_RESOURCE) {
+ $resources[$module->link . $sectionlink] = $module->title;
+ } else if ($module->archetype === MOD_ARCHETYPE_SYSTEM) {
+ // System modules cannot be added by user, do not add to dropdown
+ } else {
+ $activities[$module->link . $sectionlink] = $module->title;
+ }
+ }
+ }
+
+ $straddactivity = get_string('addactivity');
+ $straddresource = get_string('addresource');
+ $sectionname = get_section_name($course, $section);
+ $strresourcelabel = get_string('addresourcetosection', null, $sectionname);
+ $stractivitylabel = get_string('addactivitytosection', null, $sectionname);
+
+ $output = html_writer::start_tag('div', array('class' => 'section_add_menus', 'id' => 'add_menus-section-' . $section));
+
+ if (!$vertical) {
+ $output .= html_writer::start_tag('div', array('class' => 'horizontal'));
+ }
+
+ if (!empty($resources)) {
+ $select = new url_select($resources, '', array(''=>$straddresource), "ressection$section");
+ $select->set_help_icon('resources');
+ $select->set_label($strresourcelabel, array('class' => 'accesshide'));
+ $output .= $OUTPUT->render($select);
+ }
+
+ if (!empty($activities)) {
+ $select = new url_select($activities, '', array(''=>$straddactivity), "section$section");
+ $select->set_help_icon('activities');
+ $select->set_label($stractivitylabel, array('class' => 'accesshide'));
+ $output .= $OUTPUT->render($select);
+ }
+
+ if (!$vertical) {
+ $output .= html_writer::end_tag('div');
+ }
+
+ $output .= html_writer::end_tag('div');
+
+ if (course_ajax_enabled($course)) {
+ $straddeither = get_string('addresourceoractivity');
+ // The module chooser link
+ $modchooser = html_writer::start_tag('div', array('class' => 'mdl-right'));
+ $modchooser.= html_writer::start_tag('div', array('class' => 'section-modchooser'));
+ $icon = $OUTPUT->pix_icon('t/add', '');
+ $span = html_writer::tag('span', $straddeither, array('class' => 'section-modchooser-text'));
+ $modchooser .= html_writer::tag('span', $icon . $span, array('class' => 'section-modchooser-link'));
+ $modchooser.= html_writer::end_tag('div');
+ $modchooser.= html_writer::end_tag('div');
+
+ // Wrap the normal output in a noscript div
+ $usemodchooser = get_user_preferences('usemodchooser', $CFG->modchooserdefault);
+ if ($usemodchooser) {
+ $output = html_writer::tag('div', $output, array('class' => 'hiddenifjs addresourcedropdown'));
+ $modchooser = html_writer::tag('div', $modchooser, array('class' => 'visibleifjs addresourcemodchooser'));
+ } else {
+ // If the module chooser is disabled, we need to ensure that the dropdowns are shown even if javascript is disabled
+ $output = html_writer::tag('div', $output, array('class' => 'show addresourcedropdown'));
+ $modchooser = html_writer::tag('div', $modchooser, array('class' => 'hide addresourcemodchooser'));
+ }
+ $output = $modchooser . $output;
+ }
+
+ if ($return) {
+ return $output;
+ } else {
+ echo $output;
+ }
+}
+
+/**
+ * Retrieve all metadata for the requested modules
+ *
+ * @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
+ */
+function get_module_metadata($course, $modnames, $sectionreturn = null) {
+ global $CFG, $OUTPUT;
+
+ // 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 = "/course/mod.php?id=$course->id&sesskey=".sesskey().'&sr='.$sectionreturn.'&add=';
+ foreach($modnames as $modname => $modnamestr) {
+ if (!course_allowed_module($course, $modname)) {
+ continue;
+ }
+ if (isset($modlist[$course->id][$modname])) {
+ // This module is already cached
+ $return[$modname] = $modlist[$course->id][$modname];
+ continue;
+ }
+
+ // Include the module lib
+ $libfile = "$CFG->dirroot/mod/$modname/lib.php";
+ if (!file_exists($libfile)) {
+ continue;
+ }
+ include_once($libfile);
+
+ // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
+ $gettypesfunc = $modname.'_get_types';
+ if (function_exists($gettypesfunc)) {
+ $types = $gettypesfunc();
+ if (is_array($types) && count($types) > 0) {
+ $group = new stdClass();
+ $group->name = $modname;
+ $group->icon = $OUTPUT->pix_icon('icon', '', $modname, array('class' => 'icon'));
+ foreach($types as $type) {
+ if ($type->typestr === '--') {
+ continue;
+ }
+ if (strpos($type->typestr, '--') === 0) {
+ $group->title = str_replace('--', '', $type->typestr);
+ continue;
+ }
+ // Set the Sub Type metadata
+ $subtype = new stdClass();
+ $subtype->title = $type->typestr;
+ $subtype->type = str_replace('&', '&', $type->type);
+ $subtype->name = preg_replace('/.*type=/', '', $subtype->type);
+ $subtype->archetype = $type->modclass;
+
+ // The group archetype should match the subtype archetypes and all subtypes
+ // should have the same archetype
+ $group->archetype = $subtype->archetype;
+
+ if (get_string_manager()->string_exists('help' . $subtype->name, $modname)) {
+ $subtype->help = get_string('help' . $subtype->name, $modname);
+ }
+ $subtype->link = $urlbase . $subtype->type;
+ $group->types[] = $subtype;
+ }
+ $modlist[$course->id][$modname] = $group;
+ }
+ } else {
+ $module = new stdClass();
+ $module->title = get_string('modulename', $modname);
+ $module->name = $modname;
+ $module->link = $urlbase . $modname;
+ $module->icon = $OUTPUT->pix_icon('icon', '', $module->name, array('class' => 'icon'));
+ $sm = get_string_manager();
+ if ($sm->string_exists('modulename_help', $modname)) {
+ $module->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');
+ $module->help .= html_writer::tag('div', $OUTPUT->doc_link($link, $linktext, true), array('class' => 'helpdoclink'));
+ }
+ }
+ $module->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
+ $modlist[$course->id][$modname] = $module;
+ }
+ if (isset($modlist[$course->id][$modname])) {
+ $return[$modname] = $modlist[$course->id][$modname];
+ } else {
+ debugging("Invalid module metadata configuration for {$modname}");
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Return the course category context for the category with id $categoryid, except
+ * that if $categoryid is 0, return the system context.
+ *
+ * @param integer $categoryid a category id or 0.
+ * @return object the corresponding context
+ */
+function get_category_or_system_context($categoryid) {
+ if ($categoryid) {
+ return context_coursecat::instance($categoryid, IGNORE_MISSING);
+ } else {
+ return context_system::instance();
+ }
+}
+
+/**
+ * Gets the child categories of a given courses category. Uses a static cache
+ * to make repeat calls efficient.
+ *
+ * @param int $parentid the id of a course category.
+ * @return array all the child course categories.
+ */
+function get_child_categories($parentid) {
+ static $allcategories = null;
+
+ // only fill in this variable the first time
+ if (null == $allcategories) {
+ $allcategories = array();
+
+ $categories = get_categories();
+ foreach ($categories as $category) {
+ if (empty($allcategories[$category->parent])) {
+ $allcategories[$category->parent] = array();
+ }
+ $allcategories[$category->parent][] = $category;
+ }
+ }
+
+ if (empty($allcategories[$parentid])) {
+ return array();
+ } else {
+ return $allcategories[$parentid];
+ }
+}
+
+/**
+ * This function recursively travels the categories, building up a nice list
+ * for display. It also makes an array that list all the parents for each
+ * category.
+ *
+ * For example, if you have a tree of categories like:
+ * Miscellaneous (id = 1)
+ * Subcategory (id = 2)
+ * Sub-subcategory (id = 4)
+ * Other category (id = 3)
+ * Then after calling this function you will have
+ * $list = array(1 => 'Miscellaneous', 2 => 'Miscellaneous / Subcategory',
+ * 4 => 'Miscellaneous / Subcategory / Sub-subcategory',
+ * 3 => 'Other category');
+ * $parents = array(2 => array(1), 4 => array(1, 2));
+ *
+ * If you specify $requiredcapability, then only categories where the current
+ * user has that capability will be added to $list, although all categories
+ * will still be added to $parents, and if you only have $requiredcapability
+ * in a child category, not the parent, then the child catgegory will still be
+ * included.
+ *
+ * If you specify the option $excluded, then that category, and all its children,
+ * are omitted from the tree. This is useful when you are doing something like
+ * moving categories, where you do not want to allow people to move a category
+ * to be the child of itself.
+ *
+ * @param array $list For output, accumulates an array categoryid => full category path name
+ * @param array $parents For output, accumulates an array categoryid => list of parent category ids.
+ * @param string/array $requiredcapability if given, only categories where the current
+ * user has this capability will be added to $list. Can also be an array of capabilities,
+ * in which case they are all required.
+ * @param integer $excludeid Omit this category and its children from the lists built.
+ * @param object $category Build the tree starting at this category - otherwise starts at the top level.
+ * @param string $path For internal use, as part of recursive calls.
+ */
+function make_categories_list(&$list, &$parents, $requiredcapability = '',
+ $excludeid = 0, $category = NULL, $path = "") {
+
+ // initialize the arrays if needed
+ if (!is_array($list)) {
+ $list = array();
+ }
+ if (!is_array($parents)) {
+ $parents = array();
+ }
+
+ if (empty($category)) {
+ // Start at the top level.
+ $category = new stdClass;
+ $category->id = 0;
+ } else {
+ // This is the excluded category, don't include it.
+ if ($excludeid > 0 && $excludeid == $category->id) {
+ return;
+ }
+
+ $context = context_coursecat::instance($category->id);
+ $categoryname = format_string($category->name, true, array('context' => $context));
+
+ // Update $path.
+ if ($path) {
+ $path = $path.' / '.$categoryname;
+ } else {
+ $path = $categoryname;
+ }
+
+ // Add this category to $list, if the permissions check out.
+ if (empty($requiredcapability)) {
+ $list[$category->id] = $path;
+
+ } else {
+ $requiredcapability = (array)$requiredcapability;
+ if (has_all_capabilities($requiredcapability, $context)) {
+ $list[$category->id] = $path;
+ }
+ }
+ }
+
+ // Add all the children recursively, while updating the parents array.
+ if ($categories = get_child_categories($category->id)) {
+ foreach ($categories as $cat) {
+ if (!empty($category->id)) {
+ if (isset($parents[$category->id])) {
+ $parents[$cat->id] = $parents[$category->id];
+ }
+ $parents[$cat->id][] = $category->id;
+ }
+ make_categories_list($list, $parents, $requiredcapability, $excludeid, $cat, $path);
+ }
+ }
+}
+
+/**
+ * This function generates a structured array of courses and categories.
+ *
+ * The depth of categories is limited by $CFG->maxcategorydepth however there
+ * is no limit on the number of courses!
+ *
+ * Suitable for use with the course renderers course_category_tree method:
+ * $renderer = $PAGE->get_renderer('core','course');
+ * echo $renderer->course_category_tree(get_course_category_tree());
+ *
+ * @global moodle_database $DB
+ * @param int $id
+ * @param int $depth
+ */
+function get_course_category_tree($id = 0, $depth = 0) {
+ global $DB, $CFG;
+ $viewhiddencats = has_capability('moodle/category:viewhiddencategories', context_system::instance());
+ $categories = get_child_categories($id);
+ $categoryids = array();
+ foreach ($categories as $key => &$category) {
+ if (!$category->visible && !$viewhiddencats) {
+ unset($categories[$key]);
+ continue;
+ }
+ $categoryids[$category->id] = $category;
+ if (empty($CFG->maxcategorydepth) || $depth <= $CFG->maxcategorydepth) {
+ list($category->categories, $subcategories) = get_course_category_tree($category->id, $depth+1);
+ foreach ($subcategories as $subid=>$subcat) {
+ $categoryids[$subid] = $subcat;
+ }
+ $category->courses = array();
+ }
+ }
+
+ if ($depth > 0) {
+ // This is a recursive call so return the required array
+ return array($categories, $categoryids);
+ }
+
+ if (empty($categoryids)) {
+ // No categories available (probably all hidden).
+ return array();
+ }
+
+ // The depth is 0 this function has just been called so we can finish it off
+
+ list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
+ list($catsql, $catparams) = $DB->get_in_or_equal(array_keys($categoryids));
+ $sql = "SELECT
+ c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary,c.category
+ $ccselect
+ FROM {course} c
+ $ccjoin
+ WHERE c.category $catsql ORDER BY c.sortorder ASC";
+ if ($courses = $DB->get_records_sql($sql, $catparams)) {
+ // loop throught them
+ foreach ($courses as $course) {
+ if ($course->id == SITEID) {
+ continue;
+ }
+ context_instance_preload($course);
+ if (!empty($course->visible) || has_capability('moodle/course:viewhiddencourses', context_course::instance($course->id))) {
+ $categoryids[$course->category]->courses[$course->id] = $course;
+ }
+ }
+ }
+ return $categories;
+}
+
+/**
+ * Recursive function to print out all the categories in a nice format
+ * with or without courses included
+ */
+function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $showcourses = true) {
+ global $CFG;
+
+ // maxcategorydepth == 0 meant no limit
+ if (!empty($CFG->maxcategorydepth) && $depth >= $CFG->maxcategorydepth) {
+ return;
+ }
+
+ if (!$displaylist) {
+ make_categories_list($displaylist, $parentslist);
+ }
+
+ if ($category) {
+ if ($category->visible or has_capability('moodle/category:viewhiddencategories', context_system::instance())) {
+ print_category_info($category, $depth, $showcourses);
+ } else {
+ return; // Don't bother printing children of invisible categories
+ }
+
+ } else {
+ $category = new stdClass();
+ $category->id = "0";
+ }
+
+ if ($categories = get_child_categories($category->id)) { // Print all the children recursively
+ $countcats = count($categories);
+ $count = 0;
+ $first = true;
+ $last = false;
+ foreach ($categories as $cat) {
+ $count++;
+ if ($count == $countcats) {
+ $last = true;
+ }
+ $up = $first ? false : true;
+ $down = $last ? false : true;
+ $first = false;
+
+ print_whole_category_list($cat, $displaylist, $parentslist, $depth + 1, $showcourses);
+ }
+ }
+}
+
+/**
+ * This function will return $options array for html_writer::select(), with whitespace to denote nesting.
+ */
+function make_categories_options() {
+ make_categories_list($cats,$parents);
+ foreach ($cats as $key => $value) {
+ if (array_key_exists($key,$parents)) {
+ if ($indent = count($parents[$key])) {
+ for ($i = 0; $i < $indent; $i++) {
+ $cats[$key] = ' '.$cats[$key];
+ }
+ }
+ }
+ }
+ return $cats;
+}
+
+/**
+ * Prints the category info in indented fashion
+ * This function is only used by print_whole_category_list() above
+ */
+function print_category_info($category, $depth=0, $showcourses = false) {
+ global $CFG, $DB, $OUTPUT;
+
+ $strsummary = get_string('summary');
+
+ $catlinkcss = null;
+ if (!$category->visible) {
+ $catlinkcss = array('class'=>'dimmed');
+ }
+ static $coursecount = null;
+ if (null === $coursecount) {
+ // only need to check this once
+ $coursecount = $DB->count_records('course') <= FRONTPAGECOURSELIMIT;
+ }
+
+ if ($showcourses and $coursecount) {
+ $catimage = '<img src="'.$OUTPUT->pix_url('i/course') . '" alt="" />';
+ } else {
+ $catimage = " ";
+ }
+
+ $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary');
+ $context = context_coursecat::instance($category->id);
+ $fullname = format_string($category->name, true, array('context' => $context));
+
+ if ($showcourses and $coursecount) {
+ echo '<div class="categorylist clearfix">';
+ $cat = '';
+ $cat .= html_writer::tag('div', $catimage, array('class'=>'image'));
+ $catlink = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
+ $cat .= html_writer::tag('div', $catlink, array('class'=>'name'));
+
+ $html = '';
+ if ($depth > 0) {
+ for ($i=0; $i< $depth; $i++) {
+ $html = html_writer::tag('div', $html . $cat, array('class'=>'indentation'));
+ $cat = '';
+ }
+ } else {
+ $html = $cat;
+ }
+ echo html_writer::tag('div', $html, array('class'=>'category'));
+ echo html_writer::tag('div', '', array('class'=>'clearfloat'));
+
+ // does the depth exceed maxcategorydepth
+ // maxcategorydepth == 0 or unset meant no limit
+ $limit = !(isset($CFG->maxcategorydepth) && ($depth >= $CFG->maxcategorydepth-1));
+ if ($courses && ($limit || $CFG->maxcategorydepth == 0)) {
+ foreach ($courses as $course) {
+ $linkcss = null;
+ if (!$course->visible) {
+ $linkcss = array('class'=>'dimmed');
+ }
+
+ $coursename = get_course_display_name_for_list($course);
+ $courselink = html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($coursename), $linkcss);
+
+ // print enrol info
+ $courseicon = '';
+ if ($icons = enrol_get_course_info_icons($course)) {
+ foreach ($icons as $pix_icon) {
+ $courseicon = $OUTPUT->render($pix_icon);
+ }
+ }
+
+ $coursecontent = html_writer::tag('div', $courseicon.$courselink, array('class'=>'name'));
+
+ if ($course->summary) {
+ $link = new moodle_url('/course/info.php?id='.$course->id);
+ $actionlink = $OUTPUT->action_link($link, '<img alt="'.$strsummary.'" src="'.$OUTPUT->pix_url('i/info') . '" />',
+ new popup_action('click', $link, 'courseinfo', array('height' => 400, 'width' => 500)),
+ array('title'=>$strsummary));
+
+ $coursecontent .= html_writer::tag('div', $actionlink, array('class'=>'info'));
+ }
+
+ $html = '';
+ for ($i=0; $i <= $depth; $i++) {
+ $html = html_writer::tag('div', $html . $coursecontent , array('class'=>'indentation'));
+ $coursecontent = '';
+ }
+ echo html_writer::tag('div', $html, array('class'=>'course clearfloat'));
+ }
+ }
+ echo '</div>';
+ } else {
+ echo '<div class="categorylist">';
+ $html = '';
+ $cat = html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $fullname, $catlinkcss);
+ if (count($courses) > 0) {
+ $cat .= html_writer::tag('span', ' ('.count($courses).')', array('title'=>get_string('numberofcourses'), 'class'=>'numberofcourse'));
+ }
+
+ if ($depth > 0) {
+ for ($i=0; $i< $depth; $i++) {
+ $html = html_writer::tag('div', $html .$cat, array('class'=>'indentation'));
+ $cat = '';
+ }
+ } else {
+ $html = $cat;
+ }
+
+ echo html_writer::tag('div', $html, array('class'=>'category'));
+ echo html_writer::tag('div', '', array('class'=>'clearfloat'));
+ echo '</div>';
+ }
+}
+
+/**
+ * Print the buttons relating to course requests.
+ *
+ * @param object $systemcontext the system context.
+ */
+function print_course_request_buttons($systemcontext) {
+ global $CFG, $DB, $OUTPUT;
+ if (empty($CFG->enablecourserequests)) {
+ return;
+ }
+ if (!has_capability('moodle/course:create', $systemcontext) && has_capability('moodle/course:request', $systemcontext)) {
+ /// Print a button to request a new course
+ echo $OUTPUT->single_button('request.php', get_string('requestcourse'), 'get');
+ }
+ /// Print a button to manage pending requests
+ if (has_capability('moodle/site:approvecourse', $systemcontext)) {
+ $disabled = !$DB->record_exists('course_request', array());
+ echo $OUTPUT->single_button('pending.php', get_string('coursespending'), 'get', array('disabled'=>$disabled));
+ }
+}
+
+/**
+ * Does the user have permission to edit things in this category?
+ *
+ * @param integer $categoryid The id of the category we are showing, or 0 for system context.
+ * @return boolean has_any_capability(array(...), ...); in the appropriate context.
+ */
+function can_edit_in_category($categoryid = 0) {
+ $context = get_category_or_system_context($categoryid);
+ return has_any_capability(array('moodle/category:manage', 'moodle/course:create'), $context);
+}
+
+/**
+ * Prints the turn editing on/off button on course/index.php or course/category.php.
+ *
+ * @param integer $categoryid The id of the category we are showing, or 0 for system context.
+ * @return string HTML of the editing button, or empty string, if this user is not allowed
+ * to see it.
+ */
+function update_category_button($categoryid = 0) {
+ global $CFG, $PAGE, $OUTPUT;
+
+ // Check permissions.
+ if (!can_edit_in_category($categoryid)) {
+ return '';
+ }
+
+ // Work out the appropriate action.
+ if ($PAGE->user_is_editing()) {
+ $label = get_string('turneditingoff');
+ $edit = 'off';
+ } else {
+ $label = get_string('turneditingon');
+ $edit = 'on';
+ }
+
+ // Generate the button HTML.
+ $options = array('categoryedit' => $edit, 'sesskey' => sesskey());
+ if ($categoryid) {
+ $options['id'] = $categoryid;
+ $page = 'category.php';
+ } else {
+ $page = 'index.php';
+ }
+ return $OUTPUT->single_button(new moodle_url('/course/' . $page, $options), $label, 'get');
+}
+
+/**
+ * Print courses in category. If category is 0 then all courses are printed.
+ * @param int|stdClass $category category object or id.
+ * @return bool true if courses found and printed, else false.
+ */
+function print_courses($category) {
+ global $CFG, $OUTPUT;
+
+ if (!is_object($category) && $category==0) {
+ $categories = get_child_categories(0); // Parent = 0 ie top-level categories only
+ if (is_array($categories) && count($categories) == 1) {
+ $category = array_shift($categories);
+ $courses = get_courses_wmanagers($category->id,
+ 'c.sortorder ASC',
+ array('summary','summaryformat'));
+ } else {
+ $courses = get_courses_wmanagers('all',
+ 'c.sortorder ASC',
+ array('summary','summaryformat'));
+ }
+ unset($categories);
+ } else {
+ $courses = get_courses_wmanagers($category->id,
+ 'c.sortorder ASC',
+ array('summary','summaryformat'));
+ }
+
+ if ($courses) {
+ echo html_writer::start_tag('ul', array('class'=>'unlist'));
+ foreach ($courses as $course) {
+ $coursecontext = context_course::instance($course->id);
+ if ($course->visible == 1 || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+ echo html_writer::start_tag('li');
+ print_course($course);
+ echo html_writer::end_tag('li');
+ }
+ }
+ echo html_writer::end_tag('ul');
+ } else {
+ echo $OUTPUT->heading(get_string("nocoursesyet"));
+ $context = context_system::instance();
+ if (has_capability('moodle/course:create', $context)) {
+ $options = array();
+ if (!empty($category->id)) {
+ $options['category'] = $category->id;
+ } else {
+ $options['category'] = $CFG->defaultrequestcategory;
+ }
+ echo html_writer::start_tag('div', array('class'=>'addcoursebutton'));
+ echo $OUTPUT->single_button(new moodle_url('/course/edit.php', $options), get_string("addnewcourse"));
+ echo html_writer::end_tag('div');
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Print a description of a course, suitable for browsing in a list.
+ *
+ * @param object $course the course object.
+ * @param string $highlightterms (optional) some search terms that should be highlighted in the display.
+ */
+function print_course($course, $highlightterms = '') {
+ global $CFG, $USER, $DB, $OUTPUT;
+
+ $context = context_course::instance($course->id);
+
+ // Rewrite file URLs so that they are correct
+ $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', NULL);
+
+ echo html_writer::start_tag('div', array('class'=>'coursebox clearfix'));
+ echo html_writer::start_tag('div', array('class'=>'info'));
+ echo html_writer::start_tag('h3', array('class'=>'name'));
+
+ $linkhref = new moodle_url('/course/view.php', array('id'=>$course->id));
+
+ $coursename = get_course_display_name_for_list($course);
+ $linktext = highlight($highlightterms, format_string($coursename));
+ $linkparams = array('title'=>get_string('entercourse'));
+ if (empty($course->visible)) {
+ $linkparams['class'] = 'dimmed';
+ }
+ echo html_writer::link($linkhref, $linktext, $linkparams);
+ echo html_writer::end_tag('h3');
+
+ /// first find all roles that are supposed to be displayed
+ if (!empty($CFG->coursecontact)) {
+ $managerroles = explode(',', $CFG->coursecontact);
+ $rusers = array();
+
+ if (!isset($course->managers)) {
+ list($sort, $sortparams) = users_order_by_sql('u');
+ $rusers = get_role_users($managerroles, $context, true,
+ 'ra.id AS raid, u.id, u.username, u.firstname, u.lastname, rn.name AS rolecoursealias,
+ r.name AS rolename, r.sortorder, r.id AS roleid, r.shortname AS roleshortname',
+ 'r.sortorder ASC, ' . $sort, null, '', '', '', '', $sortparams);
+ } else {
+ // use the managers array if we have it for perf reasosn
+ // populate the datastructure like output of get_role_users();
+ foreach ($course->managers as $manager) {
+ $user = clone($manager->user);
+ $user->roleid = $manager->roleid;
+ $user->rolename = $manager->rolename;
+ $user->roleshortname = $manager->roleshortname;
+ $user->rolecoursealias = $manager->rolecoursealias;
+ $rusers[$user->id] = $user;
+ }
+ }
+
+ $namesarray = array();
+ $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
+ foreach ($rusers as $ra) {
+ if (isset($namesarray[$ra->id])) {
+ // only display a user once with the higest sortorder role
+ continue;
+ }
+
+ $role = new stdClass();
+ $role->id = $ra->roleid;
+ $role->name = $ra->rolename;
+ $role->shortname = $ra->roleshortname;
+ $role->coursealias = $ra->rolecoursealias;
+ $rolename = role_get_name($role, $context, ROLENAME_ALIAS);
+
+ $fullname = fullname($ra, $canviewfullnames);
+ $namesarray[$ra->id] = $rolename.': '.
+ html_writer::link(new moodle_url('/user/view.php', array('id'=>$ra->id, 'course'=>SITEID)), $fullname);
+ }
+
+ if (!empty($namesarray)) {
+ echo html_writer::start_tag('ul', array('class'=>'teachers'));
+ foreach ($namesarray as $name) {
+ echo html_writer::tag('li', $name);
+ }
+ echo html_writer::end_tag('ul');
+ }
+ }
+ echo html_writer::end_tag('div'); // End of info div
+
+/* CUSTOMICP
+ echo html_writer::start_tag('div', array('class'=>'summary'));
+ $options = new stdClass();
+ $options->noclean = true;
+ $options->para = false;
+ $options->overflowdiv = true;
+ if (!isset($course->summaryformat)) {
+ $course->summaryformat = FORMAT_MOODLE;
+ }
+ echo highlight($highlightterms, format_text($course->summary, $course->summaryformat, $options, $course->id));
+ if ($icons = enrol_get_course_info_icons($course)) {
+ echo html_writer::start_tag('div', array('class'=>'enrolmenticons'));
+ foreach ($icons as $icon) {
+ $icon->attributes["alt"] .= ": ". format_string($coursename, true, array('context'=>$context));
+ echo $OUTPUT->render($icon);
+ }
+ echo html_writer::end_tag('div'); // End of enrolmenticons div
+ }
+ echo html_writer::end_tag('div'); // End of summary div
+///*ENDCUSTOMICP */
+ echo html_writer::end_tag('div'); // End of coursebox div
+}
+
+/**
+ * Prints custom user information on the home page.
+ * Over time this can include all sorts of information
+ */
+function print_my_moodle() {
+ global $USER, $CFG, $DB, $OUTPUT;
+
+ if (!isloggedin() or isguestuser()) {
+ print_error('nopermissions', '', '', 'See My Moodle');
+ }
+
+ $courses = enrol_get_my_courses('summary', 'visible DESC,sortorder ASC');
+ $rhosts = array();
+ $rcourses = array();
+ if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
+ $rcourses = get_my_remotecourses($USER->id);
+ $rhosts = get_my_remotehosts();
+ }
+
+ if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
+
+/* CUSTOMICP désactivation des lignes suivantes
+ if (!empty($courses)) {
+ echo '<ul class="unlist">';
+ foreach ($courses as $course) {
+ if ($course->id == SITEID) {
+ continue;
+ }
+ echo '<li>';
+ print_course($course);
+ echo "</li>\n";
+ }
+ echo "</ul>\n";
+ }
+
+///*ENDCUSTOMICP */
+
+///* CUSTOMICP insertion des lignes suivantes
+if (!empty($courses)) {
+ echo '<ul class="unlist" style="margin-top: 20px;">';
+ $teacher_once=0;
+ $both=0;
+ foreach ($courses as $course) {
+ if ($course->id == SITEID) {
+ continue;
+ }
+
+ $u_context = get_context_instance(CONTEXT_COURSE, $course->id);
+ $u_managerroles = explode(',', $CFG->coursecontact);
+ //CUSTOMICP LIMIT 3
+ $u_rusers = get_role_users($u_managerroles, $u_context, true, '', 'r.sortorder ASC, u.lastname ASC LIMIT 3');
+ $bool_teacher=0;
+ foreach ($u_rusers as $u_teacher) {
+ if ($u_teacher->id == $USER->id) {
+ $bool_teacher=1;
+ $teacher_once++;
+ }
+ }
+ if ( $teacher_once == 1) {
+ ?> <h2 id="h2_teacher" onclick="bascule('div_teacher','img_teacher'); return false;" >En tant qu'enseignant <img id="img_teacher" alt="toggle" src="theme/standard/pix/bas.png"></h2><div id='div_teacher' ><?php
+ $both++;
+ $teacher_once++;
+ }
+ if ( $bool_teacher) {
+ echo '<li>';
+ print_course($course);
+ echo "</li>\n";
+ }
+ }
+ if ( $teacher_once != 0) {
+ echo "</div>";
+ }
+
+ $student_once=0;
+ foreach ($courses as $course) {
+ if ($course->id == SITEID) {
+ continue;
+ }
+
+ $u_context = get_context_instance(CONTEXT_COURSE, $course->id);
+ $u_managerroles = explode(',', $CFG->coursecontact);
+ //CUSTOMICP LIMIT 3
+ $u_rusers = get_role_users($u_managerroles, $u_context, true, '', 'r.sortorder ASC, u.lastname ASC LIMIT 3');
+ $bool_teacher=0;
+ foreach ($u_rusers as $u_teacher) {
+ if ($u_teacher->id == $USER->id) {
+ $bool_teacher=1;
+ }
+ }
+ if ( !$bool_teacher) {
+
+ if ( $student_once == 0) {
+ ?><h2 id="h2_student" onclick="bascule('div_student', 'img_student'); return false;" >En tant qu'étudiant <img id="img_student" alt="toggle" src="theme/standard/pix/bas.png"></h2><div id='div_student' ><?php
+ $both++;
+ $student_once++;
+ }
+ echo '<li>';
+ print_course($course);
+ echo "</li>\n";
+ }
+
+ }
+ if ( $student_once != 0) {
+ echo "</div>";
+ }
+ if ( $both==2 ){
+ ?>
+ <script language="javascript" type="text/javascript">
+ document.getElementById('div_student').style.display="none";
+ document.getElementById('div_teacher').style.display="none";
+ </script>
+ <?php
+ }
+ else {
+ ?>
+ <script language="javascript" type="text/javascript">
+ if ( document.getElementById('h2_teacher') != null )
+ document.getElementById('h2_teacher').style.display="none";
+ if ( document.getElementById('h2_student') !=null )
+ document.getElementById('h2_student').style.display="none";
+ </script>
+ <?php
+ }
+ echo "</ul>\n";
+ ?>
+ <script language="javascript" type="text/javascript">
+ function bascule(elem,img) {
+ etat=document.getElementById(elem).style.display;
+ if(etat=="none"){
+ document.getElementById(elem).style.display="block";
+ document.getElementById(img).src="theme/standard/pix/haut.png";
+ }
+ else{
+ document.getElementById(elem).style.display="none";
+ document.getElementById(img).src="theme/standard/pix/bas.png";
+ }
+ }
+ </script>
+ <style type="text/css">
+ .unlist h2 {
+ cursor:pointer;cursor:hand
+ }
+ </style>
+ <?php
+ }
+///* ENDCUSTOMICP */
+
+ // MNET
+ if (!empty($rcourses)) {
+ // at the IDP, we know of all the remote courses
+ foreach ($rcourses as $course) {
+ print_remote_course($course, "100%");
+ }
+ } elseif (!empty($rhosts)) {
+ // non-IDP, we know of all the remote servers, but not courses
+ foreach ($rhosts as $host) {
+ print_remote_host($host, "100%");
+ }
+ }
+ unset($course);
+ unset($host);
+
+ if ($DB->count_records("course") > (count($courses) + 1) ) { // Some courses not being displayed
+ echo "<table width=\"100%\"><tr><td align=\"center\">";
+ print_course_search("", false, "short");
+ echo "</td><td align=\"center\">";
+ echo $OUTPUT->single_button("$CFG->wwwroot/course/index.php", get_string("fulllistofcourses"), "get");
+ echo "</td></tr></table>\n";
+ }
+
+ } else {
+ if ($DB->count_records("course_categories") > 1) {
+ echo $OUTPUT->box_start("categorybox");
+ print_whole_category_list();
+ echo $OUTPUT->box_end();
+ } else {
+ print_courses(0);
+ }
+ }
+}
+
+
+function print_course_search($value="", $return=false, $format="plain") {
+ global $CFG;
+ static $count = 0;
+
+ $count++;
+
+ $id = 'coursesearch';
+
+ if ($count > 1) {
+ $id .= $count;
+ }
+
+ $strsearchcourses= get_string("searchcourses");
+
+ if ($format == 'plain') {
+ $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
+ $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
+ $output .= '<label for="coursesearchbox">'.$strsearchcourses.': </label>';
+ $output .= '<input type="text" id="coursesearchbox" size="30" name="search" value="'.s($value).'" />';
+ $output .= '<input type="submit" value="'.get_string('go').'" />';
+ $output .= '</fieldset></form>';
+ } else if ($format == 'short') {
+ $output = '<form id="'.$id.'" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
+ $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
+ $output .= '<label for="shortsearchbox">'.$strsearchcourses.': </label>';
+ $output .= '<input type="text" id="shortsearchbox" size="12" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
+ $output .= '<input type="submit" value="'.get_string('go').'" />';
+ $output .= '</fieldset></form>';
+ } else if ($format == 'navbar') {
+ $output = '<form id="coursesearchnavbar" action="'.$CFG->wwwroot.'/course/search.php" method="get">';
+ $output .= '<fieldset class="coursesearchbox invisiblefieldset">';
+ $output .= '<label for="navsearchbox">'.$strsearchcourses.': </label>';
+ $output .= '<input type="text" id="navsearchbox" size="20" name="search" alt="'.s($strsearchcourses).'" value="'.s($value).'" />';
+ $output .= '<input type="submit" value="'.get_string('go').'" />';
+ $output .= '</fieldset></form>';
+ }
+
+ if ($return) {
+ return $output;
+ }
+ echo $output;
+}
+
+function print_remote_course($course, $width="100%") {
+ global $CFG, $USER;
+
+ $linkcss = '';
+
+ $url = "{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&wantsurl=/course/view.php?id={$course->remoteid}";
+
+ echo '<div class="coursebox remotecoursebox clearfix">';
+ echo '<div class="info">';
+ echo '<div class="name"><a title="'.get_string('entercourse').'"'.
+ $linkcss.' href="'.$url.'">'
+ . format_string($course->fullname) .'</a><br />'
+ . format_string($course->hostname) . ' : '
+ . format_string($course->cat_name) . ' : '
+ . format_string($course->shortname). '</div>';
+ echo '</div><div class="summary">';
+ $options = new stdClass();
+ $options->noclean = true;
+ $options->para = false;
+ $options->overflowdiv = true;
+ echo format_text($course->summary, $course->summaryformat, $options);
+ echo '</div>';
+ echo '</div>';
+}
+
+function print_remote_host($host, $width="100%") {
+ global $OUTPUT;
+
+ $linkcss = '';
+
+ echo '<div class="coursebox clearfix">';
+ echo '<div class="info">';
+ echo '<div class="name">';
+ echo '<img src="'.$OUTPUT->pix_url('i/mnethost') . '" class="icon" alt="'.get_string('course').'" />';
+ echo '<a title="'.s($host['name']).'" href="'.s($host['url']).'">'
+ . s($host['name']).'</a> - ';
+ echo $host['count'] . ' ' . get_string('courses');
+ echo '</div>';
+ echo '</div>';
+ echo '</div>';
+}
+
+
+/// MODULE FUNCTIONS /////////////////////////////////////////////////////////////////
+
+function add_course_module($mod) {
+ global $DB;
+
+ $mod->added = time();
+ unset($mod->id);
+
+ $cmid = $DB->insert_record("course_modules", $mod);
+ rebuild_course_cache($mod->course, true);
+ return $cmid;
+}
+
+/**
+ * Creates missing course section(s) and rebuilds course cache
+ *
+ * @param int|stdClass $courseorid course id or course object
+ * @param int|array $sections list of relative section numbers to create
+ * @return bool if there were any sections created
+ */
+function course_create_sections_if_missing($courseorid, $sections) {
+ global $DB;
+ if (!is_array($sections)) {
+ $sections = array($sections);
+ }
+ $existing = array_keys(get_fast_modinfo($courseorid)->get_section_info_all());
+ if (is_object($courseorid)) {
+ $courseorid = $courseorid->id;
+ }
+ $coursechanged = false;
+ foreach ($sections as $sectionnum) {
+ if (!in_array($sectionnum, $existing)) {
+ $cw = new stdClass();
+ $cw->course = $courseorid;
+ $cw->section = $sectionnum;
+ $cw->summary = '';
+ $cw->summaryformat = FORMAT_HTML;
+ $cw->sequence = '';
+ $id = $DB->insert_record("course_sections", $cw);
+ $coursechanged = true;
+ }
+ }
+ if ($coursechanged) {
+ rebuild_course_cache($courseorid, true);
+ }
+ return $coursechanged;
+}
+
+/**
+ * Adds an existing module to the section
+ *
+ * Updates both tables {course_sections} and {course_modules}
+ *
+ * @param int|stdClass $courseorid course id or course object
+ * @param int $cmid id of the module already existing in course_modules table
+ * @param int $sectionnum relative number of the section (field course_sections.section)
+ * If section does not exist it will be created
+ * @param int|stdClass $beforemod id or object with field id corresponding to the module
+ * before which the module needs to be included. Null for inserting in the
+ * end of the section
+ * @return int The course_sections ID where the module is inserted
+ */
+function course_add_cm_to_section($courseorid, $cmid, $sectionnum, $beforemod = null) {
+ global $DB, $COURSE;
+ if (is_object($beforemod)) {
+ $beforemod = $beforemod->id;
+ }
+ if (is_object($courseorid)) {
+ $courseid = $courseorid->id;
+ } else {
+ $courseid = $courseorid;
+ }
+ course_create_sections_if_missing($courseorid, $sectionnum);
+ // Do not try to use modinfo here, there is no guarantee it is valid!
+ $section = $DB->get_record('course_sections', array('course'=>$courseid, 'section'=>$sectionnum), '*', MUST_EXIST);
+ $modarray = explode(",", trim($section->sequence));
+ if (empty($section->sequence)) {
+ $newsequence = "$cmid";
+ } else if ($beforemod && ($key = array_keys($modarray, $beforemod))) {
+ $insertarray = array($cmid, $beforemod);
+ array_splice($modarray, $key[0], 1, $insertarray);
+ $newsequence = implode(",", $modarray);
+ } else {
+ $newsequence = "$section->sequence,$cmid";
+ }
+ $DB->set_field("course_sections", "sequence", $newsequence, array("id" => $section->id));
+ $DB->set_field('course_modules', 'section', $section->id, array('id' => $cmid));
+ if (is_object($courseorid)) {
+ rebuild_course_cache($courseorid->id, true);
+ } else {
+ rebuild_course_cache($courseorid, true);
+ }
+ return $section->id; // Return course_sections ID that was used.
+}
+
+function set_coursemodule_groupmode($id, $groupmode) {
+ global $DB;
+ $cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,groupmode', MUST_EXIST);
+ if ($cm->groupmode != $groupmode) {
+ $DB->set_field('course_modules', 'groupmode', $groupmode, array('id' => $cm->id));
+ rebuild_course_cache($cm->course, true);
+ }
+ return ($cm->groupmode != $groupmode);
+}
+
+function set_coursemodule_idnumber($id, $idnumber) {
+ global $DB;
+ $cm = $DB->get_record('course_modules', array('id' => $id), 'id,course,idnumber', MUST_EXIST);
+ if ($cm->idnumber != $idnumber) {
+ $DB->set_field('course_modules', 'idnumber', $idnumber, array('id' => $cm->id));
+ rebuild_course_cache($cm->course, true);
+ }
+ return ($cm->idnumber != $idnumber);
+}
+
+/**
+ * Set the visibility of a module and inherent properties.
+ *
+ * From 2.4 the parameter $prevstateoverrides has been removed, the logic it triggered
+ * has been moved to {@link set_section_visible()} which was the only place from which
+ * the parameter was used.
+ *
+ * @param int $id of the module
+ * @param int $visible state of the module
+ * @return bool false when the module was not found, true otherwise
+ */
+function set_coursemodule_visible($id, $visible) {
+ global $DB, $CFG;
+ require_once($CFG->libdir.'/gradelib.php');
+
+ // Trigger developer's attention when using the previously removed argument.
+ if (func_num_args() > 2) {
+ debugging('Wrong number of arguments passed to set_coursemodule_visible(), $prevstateoverrides
+ has been removed.', DEBUG_DEVELOPER);
+ }
+
+ if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
+ return false;
+ }
+
+ // Create events and propagate visibility to associated grade items if the value has changed.
+ // Only do this if it's changed to avoid accidently overwriting manual showing/hiding of student grades.
+ if ($cm->visible == $visible) {
+ return true;
+ }
+
+ if (!$modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module))) {
+ return false;
+ }
+ if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
+ foreach($events as $event) {
+ if ($visible) {
+ show_event($event);
+ } else {
+ hide_event($event);
+ }
+ }
+ }
+
+ // Hide the associated grade items so the teacher doesn't also have to go to the gradebook and hide them there.
+ $grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
+ if ($grade_items) {
+ foreach ($grade_items as $grade_item) {
+ $grade_item->set_hidden(!$visible);
+ }
+ }
+
+ // Updating visible and visibleold to keep them in sync. Only changing a section visibility will
+ // affect visibleold to allow for an original visibility restore. See set_section_visible().
+ $cminfo = new stdClass();
+ $cminfo->id = $id;
+ $cminfo->visible = $visible;
+ $cminfo->visibleold = $visible;
+ $DB->update_record('course_modules', $cminfo);
+
+ rebuild_course_cache($cm->course, true);
+ return true;
+}
+
+/**
+ * Delete a course module and any associated data at the course level (events)
+ * Until 1.5 this function simply marked a deleted flag ... now it
+ * deletes it completely.
+ *
+ */
+function delete_course_module($id) {
+ global $CFG, $DB;
+ require_once($CFG->libdir.'/gradelib.php');
+ require_once($CFG->dirroot.'/blog/lib.php');
+
+ if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
+ return true;
+ }
+ $modulename = $DB->get_field('modules', 'name', array('id'=>$cm->module));
+ //delete events from calendar
+ if ($events = $DB->get_records('event', array('instance'=>$cm->instance, 'modulename'=>$modulename))) {
+ foreach($events as $event) {
+ delete_event($event->id);
+ }
+ }
+ //delete grade items, outcome items and grades attached to modules
+ if ($grade_items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
+ 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course))) {
+ foreach ($grade_items as $grade_item) {
+ $grade_item->delete('moddelete');
+ }
+ }
+ // Delete completion and availability data; it is better to do this even if the
+ // features are not turned on, in case they were turned on previously (these will be
+ // very quick on an empty table)
+ $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
+ $DB->delete_records('course_modules_availability', array('coursemoduleid'=> $cm->id));
+ $DB->delete_records('course_modules_avail_fields', array('coursemoduleid' => $cm->id));
+ $DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id,
+ 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
+
+ delete_context(CONTEXT_MODULE, $cm->id);
+ $DB->delete_records('course_modules', array('id'=>$cm->id));
+ rebuild_course_cache($cm->course, true);
+ return true;
+}
+
+function delete_mod_from_section($modid, $sectionid) {
+ global $DB;
+
+ if ($section = $DB->get_record("course_sections", array("id"=>$sectionid)) ) {
+
+ $modarray = explode(",", $section->sequence);
+
+ if ($key = array_keys ($modarray, $modid)) {
+ array_splice($modarray, $key[0], 1);
+ $newsequence = implode(",", $modarray);
+ $DB->set_field("course_sections", "sequence", $newsequence, array("id"=>$section->id));
+ rebuild_course_cache($section->course, true);
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return false;
+}
+
+/**
+ * Moves a section up or down by 1. CANNOT BE USED DIRECTLY BY AJAX!
+ *
+ * @param object $course course object
+ * @param int $section Section number (not id!!!)
+ * @param int $move (-1 or 1)
+ * @return boolean true if section moved successfully
+ * @todo MDL-33379 remove this function in 2.5
+ */
+function move_section($course, $section, $move) {
+ debugging('This function will be removed before 2.5 is released please use move_section_to', DEBUG_DEVELOPER);
+
+/// Moves a whole course section up and down within the course
+ global $USER;
+
+ if (!$move) {
+ return true;
+ }
+
+ $sectiondest = $section + $move;
+
+ // compartibility with course formats using field 'numsections'
+ $courseformatoptions = course_get_format($course)->get_format_options();
+ if (array_key_exists('numsections', $courseformatoptions) &&
+ $sectiondest > $courseformatoptions['numsections'] or $sectiondest < 1) {
+ return false;
+ }
+
+ $retval = move_section_to($course, $section, $sectiondest);
+ return $retval;
+}
+
+/**
+ * Moves a section within a course, from a position to another.
+ * Be very careful: $section and $destination refer to section number,
+ * not id!.
+ *
+ * @param object $course
+ * @param int $section Section number (not id!!!)
+ * @param int $destination
+ * @return boolean Result
+ */
+function move_section_to($course, $section, $destination) {
+/// Moves a whole course section up and down within the course
+ global $USER, $DB;
+
+ if (!$destination && $destination != 0) {
+ return true;
+ }
+
+ // compartibility with course formats using field 'numsections'
+ $courseformatoptions = course_get_format($course)->get_format_options();
+ if ((array_key_exists('numsections', $courseformatoptions) &&
+ ($destination > $courseformatoptions['numsections'])) || ($destination < 1)) {
+ return false;
+ }
+
+ // Get all sections for this course and re-order them (2 of them should now share the same section number)
+ if (!$sections = $DB->get_records_menu('course_sections', array('course' => $course->id),
+ 'section ASC, id ASC', 'id, section')) {
+ return false;
+ }
+
+ $movedsections = reorder_sections($sections, $section, $destination);
+
+ // Update all sections. Do this in 2 steps to avoid breaking database
+ // uniqueness constraint
+ $transaction = $DB->start_delegated_transaction();
+ foreach ($movedsections as $id => $position) {
+ if ($sections[$id] !== $position) {
+ $DB->set_field('course_sections', 'section', -$position, array('id' => $id));
+ }
+ }
+ foreach ($movedsections as $id => $position) {
+ if ($sections[$id] !== $position) {
+ $DB->set_field('course_sections', 'section', $position, array('id' => $id));
+ }
+ }
+
+ // If we move the highlighted section itself, then just highlight the destination.
+ // Adjust the higlighted section location if we move something over it either direction.
+ if ($section == $course->marker) {
+ course_set_marker($course->id, $destination);
+ } elseif ($section > $course->marker && $course->marker >= $destination) {
+ course_set_marker($course->id, $course->marker+1);
+ } elseif ($section < $course->marker && $course->marker <= $destination) {
+ course_set_marker($course->id, $course->marker-1);
+ }
+
+ $transaction->allow_commit();
+ rebuild_course_cache($course->id, true);
+ return true;
+}
+
+/**
+ * Reordering algorithm for course sections. Given an array of section->section indexed by section->id,
+ * an original position number and a target position number, rebuilds the array so that the
+ * move is made without any duplication of section positions.
+ * Note: The target_position is the position AFTER WHICH the moved section will be inserted. If you want to
+ * insert a section before the first one, you must give 0 as the target (section 0 can never be moved).
+ *
+ * @param array $sections
+ * @param int $origin_position
+ * @param int $target_position
+ * @return array
+ */
+function reorder_sections($sections, $origin_position, $target_position) {
+ if (!is_array($sections)) {
+ return false;
+ }
+
+ // We can't move section position 0
+ if ($origin_position < 1) {
+ echo "We can't move section position 0";
+ return false;
+ }
+
+ // Locate origin section in sections array
+ if (!$origin_key = array_search($origin_position, $sections)) {
+ echo "searched position not in sections array";
+ return false; // searched position not in sections array
+ }
+
+ // Extract origin section
+ $origin_section = $sections[$origin_key];
+ unset($sections[$origin_key]);
+
+ // Find offset of target position (stupid PHP's array_splice requires offset instead of key index!)
+ $found = false;
+ $append_array = array();
+ foreach ($sections as $id => $position) {
+ if ($found) {
+ $append_array[$id] = $position;
+ unset($sections[$id]);
+ }
+ if ($position == $target_position) {
+ if ($target_position < $origin_position) {
+ $append_array[$id] = $position;
+ unset($sections[$id]);
+ }
+ $found = true;
+ }
+ }
+
+ // Append moved section
+ $sections[$origin_key] = $origin_section;
+
+ // Append rest of array (if applicable)
+ if (!empty($append_array)) {
+ foreach ($append_array as $id => $position) {
+ $sections[$id] = $position;
+ }
+ }
+
+ // Renumber positions
+ $position = 0;
+ foreach ($sections as $id => $p) {
+ $sections[$id] = $position;
+ $position++;
+ }
+
+ return $sections;
+
+}
+
+/**
+ * Move the module object $mod to the specified $section
+ * If $beforemod exists then that is the module
+ * before which $modid should be inserted
+ * All parameters are objects
+ */
+function moveto_module($mod, $section, $beforemod=NULL) {
+ global $OUTPUT, $DB;
+
+/// Remove original module from original section
+ if (! delete_mod_from_section($mod->id, $mod->section)) {
+ echo $OUTPUT->notification("Could not delete module from existing section");
+ }
+
+ // if moving to a hidden section then hide module
+ if (!$section->visible && $mod->visible) {
+ // Set this in the object because it is sent as a response to ajax calls.
+ $mod->visible = 0;
+ set_coursemodule_visible($mod->id, 0);
+ // Set visibleold to 1 so module will be visible when section is made visible.
+ $DB->set_field('course_modules', 'visibleold', 1, array('id' => $mod->id));
+ }
+ if ($section->visible && !$mod->visible) {
+ set_coursemodule_visible($mod->id, $mod->visibleold);
+ // Set this in the object because it is sent as a response to ajax calls.
+ $mod->visible = $mod->visibleold;
+ }
+
+/// Add the module into the new section
+ course_add_cm_to_section($section->course, $mod->id, $section->section, $beforemod);
+ return true;
+}
+
+/**
+ * Produces the editing buttons for a module
+ *
+ * @global core_renderer $OUTPUT
+ * @staticvar type $str
+ * @param stdClass $mod The module to produce editing buttons for
+ * @param bool $absolute_ignored ignored - all links are absolute
+ * @param bool $moveselect If true a move seleciton process is used (default true)
+ * @param int $indent The current indenting
+ * @param int $section The section to link back to
+ * @return string XHTML for the editing buttons
+ */
+function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $moveselect = true, $indent=-1, $section=null) {
+ global $CFG, $OUTPUT, $COURSE;
+
+ static $str;
+
+ $coursecontext = context_course::instance($mod->course);
+ $modcontext = context_module::instance($mod->id);
+
+ $editcaps = array('moodle/course:manageactivities', 'moodle/course:activityvisibility', 'moodle/role:assign');
+ $dupecaps = array('moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport');
+
+ // no permission to edit anything
+ if (!has_any_capability($editcaps, $modcontext) and !has_all_capabilities($dupecaps, $coursecontext)) {
+ return false;
+ }
+
+ $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
+
+ if (!isset($str)) {
+ $str = new stdClass;
+ $str->assign = get_string("assignroles", 'role');
+ $str->delete = get_string("delete");
+ $str->move = get_string("move");
+ $str->moveup = get_string("moveup");
+ $str->movedown = get_string("movedown");
+ $str->moveright = get_string("moveright");
+ $str->moveleft = get_string("moveleft");
+ $str->update = get_string("update");
+ $str->duplicate = get_string("duplicate");
+ $str->hide = get_string("hide");
+ $str->show = get_string("show");
+ $str->groupsnone = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsnone"));
+ $str->groupsseparate = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsseparate"));
+ $str->groupsvisible = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsvisible"));
+ $str->forcedgroupsnone = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsnone"));
+ $str->forcedgroupsseparate = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsseparate"));
+ $str->forcedgroupsvisible = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsvisible"));
+ $str->edittitle = get_string('edittitle', 'moodle');
+ }
+
+ $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
+
+ if ($section !== null) {
+ $baseurl->param('sr', $section);
+ }
+ $actions = array();
+
+ // AJAX edit title
+ if ($mod->modname !== 'label' && $hasmanageactivities && course_ajax_enabled($COURSE)) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('update' => $mod->id)),
+ new pix_icon('t/editstring', $str->edittitle, 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => '')),
+ null,
+ array('class' => 'editing_title', 'title' => $str->edittitle)
+ );
+ }
+
+ // leftright
+ if ($hasmanageactivities) {
+ if (right_to_left()) { // Exchange arrows on RTL
+ $rightarrow = 't/left';
+ $leftarrow = 't/right';
+ } else {
+ $rightarrow = 't/right';
+ $leftarrow = 't/left';
+ }
+
+ if ($indent > 0) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
+ new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_moveleft', 'title' => $str->moveleft)
+ );
+ }
+ if ($indent >= 0) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
+ new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_moveright', 'title' => $str->moveright)
+ );
+ }
+ }
+
+ // move
+ if ($hasmanageactivities) {
+ if ($moveselect) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('copy' => $mod->id)),
+ new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_move', 'title' => $str->move)
+ );
+ } else {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('id' => $mod->id, 'move' => '-1')),
+ new pix_icon('t/up', $str->moveup, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_moveup', 'title' => $str->moveup)
+ );
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('id' => $mod->id, 'move' => '1')),
+ new pix_icon('t/down', $str->movedown, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_movedown', 'title' => $str->movedown)
+ );
+ }
+ }
+
+ // Update
+ if ($hasmanageactivities) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('update' => $mod->id)),
+ new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_update', 'title' => $str->update)
+ );
+ }
+
+ // Duplicate (require both target import caps to be able to duplicate and backup2 support, see modduplicate.php)
+ if (has_all_capabilities($dupecaps, $coursecontext) && plugin_supports('mod', $mod->modname, FEATURE_BACKUP_MOODLE2)) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('duplicate' => $mod->id)),
+ new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_duplicate', 'title' => $str->duplicate)
+ );
+ }
+
+ // Delete
+ if ($hasmanageactivities) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('delete' => $mod->id)),
+ new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_delete', 'title' => $str->delete)
+ );
+ }
+
+ // hideshow
+ if (has_capability('moodle/course:activityvisibility', $modcontext)) {
+ if ($mod->visible) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('hide' => $mod->id)),
+ new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_hide', 'title' => $str->hide)
+ );
+ } else {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('show' => $mod->id)),
+ new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_show', 'title' => $str->show)
+ );
+ }
+ }
+
+ // groupmode
+ if ($hasmanageactivities and $mod->groupmode !== false) {
+ if ($mod->groupmode == SEPARATEGROUPS) {
+ $groupmode = 0;
+ $grouptitle = $str->groupsseparate;
+ $forcedgrouptitle = $str->forcedgroupsseparate;
+ $groupclass = 'editing_groupsseparate';
+ $groupimage = 't/groups';
+ } else if ($mod->groupmode == VISIBLEGROUPS) {
+ $groupmode = 1;
+ $grouptitle = $str->groupsvisible;
+ $forcedgrouptitle = $str->forcedgroupsvisible;
+ $groupclass = 'editing_groupsvisible';
+ $groupimage = 't/groupv';
+ } else {
+ $groupmode = 2;
+ $grouptitle = $str->groupsnone;
+ $forcedgrouptitle = $str->forcedgroupsnone;
+ $groupclass = 'editing_groupsnone';
+ $groupimage = 't/groupn';
+ }
+ if ($mod->groupmodelink) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $groupmode)),
+ new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => $groupclass, 'title' => $grouptitle)
+ );
+ } else {
+ $actions[] = new pix_icon($groupimage, $forcedgrouptitle, 'moodle', array('title' => $forcedgrouptitle, 'class' => 'iconsmall'));
+ }
+ }
+
+ // Assign
+ if (has_capability('moodle/role:assign', $modcontext)){
+ $actions[] = new action_link(
+ new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid' => $modcontext->id)),
+ new pix_icon('t/assignroles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('class' => 'editing_assign', 'title' => $str->assign)
+ );
+ }
+
+ // The space added before the <span> is a ugly hack but required to set the CSS property white-space: nowrap
+ // and having it to work without attaching the preceding text along with it. Hopefully the refactoring of
+ // the course page HTML will allow this to be removed.
+ $output = ' ' . html_writer::start_tag('span', array('class' => 'commands'));
+ foreach ($actions as $action) {
+ if ($action instanceof renderable) {
+ $output .= $OUTPUT->render($action);
+ } else {
+ $output .= $action;
+ }
+ }
+ $output .= html_writer::end_tag('span');
+ return $output;
+}
+
+/**
+ * given a course object with shortname & fullname, this function will
+ * truncate the the number of chars allowed and add ... if it was too long
+ */
+function course_format_name ($course,$max=100) {
+
+ $context = context_course::instance($course->id);
+ $shortname = format_string($course->shortname, true, array('context' => $context));
+ $fullname = format_string($course->fullname, true, array('context' => context_course::instance($course->id)));
+ $str = $shortname.': '. $fullname;
+ if (textlib::strlen($str) <= $max) {
+ return $str;
+ }
+ else {
+ return textlib::substr($str,0,$max-3).'...';
+ }
+}
+
+/**
+ * Is the user allowed to add this type of module to this course?
+ * @param object $course the course settings. Only $course->id is used.
+ * @param string $modname the module name. E.g. 'forum' or 'quiz'.
+ * @return bool whether the current user is allowed to add this type of module to this course.
+ */
+function course_allowed_module($course, $modname) {
+ if (is_numeric($modname)) {
+ throw new coding_exception('Function course_allowed_module no longer
+ supports numeric module ids. Please update your code to pass the module name.');
+ }
+
+ $capability = 'mod/' . $modname . ':addinstance';
+ if (!get_capability_info($capability)) {
+ // Debug warning that the capability does not exist, but no more than once per page.
+ static $warned = array();
+ $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
+ if (!isset($warned[$modname]) && $archetype !== MOD_ARCHETYPE_SYSTEM) {
+ debugging('The module ' . $modname . ' does not define the standard capability ' .
+ $capability , DEBUG_DEVELOPER);
+ $warned[$modname] = 1;
+ }
+
+ // If the capability does not exist, the module can always be added.
+ return true;
+ }
+
+ $coursecontext = context_course::instance($course->id);
+ return has_capability($capability, $coursecontext);
+}
+
+/**
+ * Recursively delete category including all subcategories and courses.
+ * @param stdClass $category
+ * @param boolean $showfeedback display some notices
+ * @return array return deleted courses
+ */
+function category_delete_full($category, $showfeedback=true) {
+ global $CFG, $DB;
+ require_once($CFG->libdir.'/gradelib.php');
+ require_once($CFG->libdir.'/questionlib.php');
+ require_once($CFG->dirroot.'/cohort/lib.php');
+
+ if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
+ foreach ($children as $childcat) {
+ category_delete_full($childcat, $showfeedback);
+ }
+ }
+
+ $deletedcourses = array();
+ if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC')) {
+ foreach ($courses as $course) {
+ if (!delete_course($course, false)) {
+ throw new moodle_exception('cannotdeletecategorycourse','','',$course->shortname);
+ }
+ $deletedcourses[] = $course;
+ }
+ }
+
+ // move or delete cohorts in this context
+ cohort_delete_category($category);
+
+ // now delete anything that may depend on course category context
+ grade_course_category_delete($category->id, 0, $showfeedback);
+ if (!question_delete_course_category($category, 0, $showfeedback)) {
+ throw new moodle_exception('cannotdeletecategoryquestions','','',$category->name);
+ }
+
+ // finally delete the category and it's context
+ $DB->delete_records('course_categories', array('id'=>$category->id));
+ delete_context(CONTEXT_COURSECAT, $category->id);
+ add_to_log(SITEID, "category", "delete", "index.php", "$category->name (ID $category->id)");
+
+ events_trigger('course_category_deleted', $category);
+
+ return $deletedcourses;
+}
+
+/**
+ * Delete category, but move contents to another category.
+ * @param object $ccategory
+ * @param int $newparentid category id
+ * @return bool status
+ */
+function category_delete_move($category, $newparentid, $showfeedback=true) {
+ global $CFG, $DB, $OUTPUT;
+ require_once($CFG->libdir.'/gradelib.php');
+ require_once($CFG->libdir.'/questionlib.php');
+ require_once($CFG->dirroot.'/cohort/lib.php');
+
+ if (!$newparentcat = $DB->get_record('course_categories', array('id'=>$newparentid))) {
+ return false;
+ }
+
+ if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
+ foreach ($children as $childcat) {
+ move_category($childcat, $newparentcat);
+ }
+ }
+
+ if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC', 'id')) {
+ if (!move_courses(array_keys($courses), $newparentid)) {
+ if ($showfeedback) {
+ echo $OUTPUT->notification("Error moving courses");
+ }
+ return false;
+ }
+ if ($showfeedback) {
+ echo $OUTPUT->notification(get_string('coursesmovedout', '', format_string($category->name)), 'notifysuccess');
+ }
+ }
+
+ // move or delete cohorts in this context
+ cohort_delete_category($category);
+
+ // now delete anything that may depend on course category context
+ grade_course_category_delete($category->id, $newparentid, $showfeedback);
+ if (!question_delete_course_category($category, $newparentcat, $showfeedback)) {
+ if ($showfeedback) {
+ echo $OUTPUT->notification(get_string('errordeletingquestionsfromcategory', 'question', $category), 'notifysuccess');
+ }
+ return false;
+ }
+
+ // finally delete the category and it's context
+ $DB->delete_records('course_categories', array('id'=>$category->id));
+ delete_context(CONTEXT_COURSECAT, $category->id);
+ add_to_log(SITEID, "category", "delete", "index.php", "$category->name (ID $category->id)");
+
+ events_trigger('course_category_deleted', $category);
+
+ if ($showfeedback) {
+ echo $OUTPUT->notification(get_string('coursecategorydeleted', '', format_string($category->name)), 'notifysuccess');
+ }
+ return true;
+}
+
+/**
+ * Efficiently moves many courses around while maintaining
+ * sortorder in order.
+ *
+ * @param array $courseids is an array of course ids
+ * @param int $categoryid
+ * @return bool success
+ */
+function move_courses($courseids, $categoryid) {
+ global $CFG, $DB, $OUTPUT;
+
+ if (empty($courseids)) {
+ // nothing to do
+ return;
+ }
+
+ if (!$category = $DB->get_record('course_categories', array('id'=>$categoryid))) {
+ return false;
+ }
+
+ $courseids = array_reverse($courseids);
+ $newparent = context_coursecat::instance($category->id);
+ $i = 1;
+
+ foreach ($courseids as $courseid) {
+ if ($course = $DB->get_record('course', array('id'=>$courseid), 'id, category')) {
+ $course = new stdClass();
+ $course->id = $courseid;
+ $course->category = $category->id;
+ $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - $i++;
+ if ($category->visible == 0) {
+ // hide the course when moving into hidden category,
+ // do not update the visibleold flag - we want to get to previous state if somebody unhides the category
+ $course->visible = 0;
+ }
+
+ $DB->update_record('course', $course);
+ add_to_log($course->id, "course", "move", "edit.php?id=$course->id", $course->id);
+
+ $context = context_course::instance($course->id);
+ context_moved($context, $newparent);
+ }
+ }
+ fix_course_sortorder();
+
+ return true;
+}
+
+/**
+ * Hide course category and child course and subcategories
+ * @param stdClass $category
+ * @return void
+ */
+function course_category_hide($category) {
+ global $DB;
+
+ $category->visible = 0;
+ $DB->set_field('course_categories', 'visible', 0, array('id'=>$category->id));
+ $DB->set_field('course_categories', 'visibleold', 0, array('id'=>$category->id));
+ $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($category->id)); // store visible flag so that we can return to it if we immediately unhide
+ $DB->set_field('course', 'visible', 0, array('category' => $category->id));
+ // get all child categories and hide too
+ if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
+ foreach ($subcats as $cat) {
+ $DB->set_field('course_categories', 'visibleold', $cat->visible, array('id'=>$cat->id));
+ $DB->set_field('course_categories', 'visible', 0, array('id'=>$cat->id));
+ $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($cat->id));
+ $DB->set_field('course', 'visible', 0, array('category' => $cat->id));
+ }
+ }
+ add_to_log(SITEID, "category", "hide", "editcategory.php?id=$category->id", $category->id);
+}
+
+/**
+ * Show course category and child course and subcategories
+ * @param stdClass $category
+ * @return void
+ */
+function course_category_show($category) {
+ global $DB;
+
+ $category->visible = 1;
+ $DB->set_field('course_categories', 'visible', 1, array('id'=>$category->id));
+ $DB->set_field('course_categories', 'visibleold', 1, array('id'=>$category->id));
+ $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($category->id));
+ // get all child categories and unhide too
+ if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
+ foreach ($subcats as $cat) {
+ if ($cat->visibleold) {
+ $DB->set_field('course_categories', 'visible', 1, array('id'=>$cat->id));
+ }
+ $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($cat->id));
+ }
+ }
+ add_to_log(SITEID, "category", "show", "editcategory.php?id=$category->id", $category->id);
+}
+
+/**
+ * Efficiently moves a category - NOTE that this can have
+ * a huge impact access-control-wise...
+ */
+function move_category($category, $newparentcat) {
+ global $CFG, $DB;
+
+ $context = context_coursecat::instance($category->id);
+
+ $hidecat = false;
+ if (empty($newparentcat->id)) {
+ $DB->set_field('course_categories', 'parent', 0, array('id' => $category->id));
+ $newparent = context_system::instance();
+ } else {
+ $DB->set_field('course_categories', 'parent', $newparentcat->id, array('id' => $category->id));
+ $newparent = context_coursecat::instance($newparentcat->id);
+
+ if (!$newparentcat->visible and $category->visible) {
+ // better hide category when moving into hidden category, teachers may unhide afterwards and the hidden children will be restored properly
+ $hidecat = true;
+ }
+ }
+
+ context_moved($context, $newparent);
+
+ // now make it last in new category
+ $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('id'=>$category->id));
+
+ // Log action.
+ add_to_log(SITEID, "category", "move", "editcategory.php?id=$category->id", $category->id);
+
+ // and fix the sortorders
+ fix_course_sortorder();
+
+ if ($hidecat) {
+ course_category_hide($category);
+ }
+}
+
+/**
+ * Returns the display name of the given section that the course prefers
+ *
+ * Implementation of this function is provided by course format
+ * @see format_base::get_section_name()
+ *
+ * @param int|stdClass $courseorid The course to get the section name for (object or just course id)
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * @return string Display name that the course format prefers, e.g. "Week 2"
+ */
+function get_section_name($courseorid, $section) {
+ return course_get_format($courseorid)->get_section_name($section);
+}
+
+/**
+ * Tells if current course format uses sections
+ *
+ * @param string $format Course format ID e.g. 'weeks' $course->format
+ * @return bool
+ */
+function course_format_uses_sections($format) {
+ $course = new stdClass();
+ $course->format = $format;
+ return course_get_format($course)->uses_sections();
+}
+
+/**
+ * Returns the information about the ajax support in the given source format
+ *
+ * The returned object's property (boolean)capable indicates that
+ * the course format supports Moodle course ajax features.
+ * The property (array)testedbrowsers can be used as a parameter for {@see ajaxenabled()}.
+ *
+ * @param string $format
+ * @return stdClass
+ */
+function course_format_ajax_support($format) {
+ $course = new stdClass();
+ $course->format = $format;
+ return course_get_format($course)->supports_ajax();
+}
+
+/**
+ * Can the current user delete this course?
+ * Course creators have exception,
+ * 1 day after the creation they can sill delete the course.
+ * @param int $courseid
+ * @return boolean
+ */
+function can_delete_course($courseid) {
+ global $USER, $DB;
+
+ $context = context_course::instance($courseid);
+
+ if (has_capability('moodle/course:delete', $context)) {
+ return true;
+ }
+
+ // hack: now try to find out if creator created this course recently (1 day)
+ if (!has_capability('moodle/course:create', $context)) {
+ return false;
+ }
+
+ $since = time() - 60*60*24;
+
+ $params = array('userid'=>$USER->id, 'url'=>"view.php?id=$courseid", 'since'=>$since);
+ $select = "module = 'course' AND action = 'new' AND userid = :userid AND url = :url AND time > :since";
+
+ return $DB->record_exists_select('log', $select, $params);
+}
+
+/**
+ * Save the Your name for 'Some role' strings.
+ *
+ * @param integer $courseid the id of this course.
+ * @param array $data the data that came from the course settings form.
+ */
+function save_local_role_names($courseid, $data) {
+ global $DB;
+ $context = context_course::instance($courseid);
+
+ foreach ($data as $fieldname => $value) {
+ if (strpos($fieldname, 'role_') !== 0) {
+ continue;
+ }
+ list($ignored, $roleid) = explode('_', $fieldname);
+
+ // make up our mind whether we want to delete, update or insert
+ if (!$value) {
+ $DB->delete_records('role_names', array('contextid' => $context->id, 'roleid' => $roleid));
+
+ } else if ($rolename = $DB->get_record('role_names', array('contextid' => $context->id, 'roleid' => $roleid))) {
+ $rolename->name = $value;
+ $DB->update_record('role_names', $rolename);
+
+ } else {
+ $rolename = new stdClass;
+ $rolename->contextid = $context->id;
+ $rolename->roleid = $roleid;
+ $rolename->name = $value;
+ $DB->insert_record('role_names', $rolename);
+ }
+ }
+}
+
+/**
+ * Create a course and either return a $course object
+ *
+ * Please note this functions does not verify any access control,
+ * the calling code is responsible for all validation (usually it is the form definition).
+ *
+ * @param array $editoroptions course description editor options
+ * @param object $data - all the data needed for an entry in the 'course' table
+ * @return object new course instance
+ */
+function create_course($data, $editoroptions = NULL) {
+ global $CFG, $DB;
+
+ //check the categoryid - must be given for all new courses
+ $category = $DB->get_record('course_categories', array('id'=>$data->category), '*', MUST_EXIST);
+
+ //check if the shortname already exist
+ if (!empty($data->shortname)) {
+ if ($DB->record_exists('course', array('shortname' => $data->shortname))) {
+ throw new moodle_exception('shortnametaken');
+ }
+ }
+
+ //check if the id number already exist
+ if (!empty($data->idnumber)) {
+ if ($DB->record_exists('course', array('idnumber' => $data->idnumber))) {
+ throw new moodle_exception('idnumbertaken');
+ }
+ }
+
+ $data->timecreated = time();
+ $data->timemodified = $data->timecreated;
+
+ // place at beginning of any category
+ $data->sortorder = 0;
+
+ if ($editoroptions) {
+ // summary text is updated later, we need context to store the files first
+ $data->summary = '';
+ $data->summary_format = FORMAT_HTML;
+ }
+
+ if (!isset($data->visible)) {
+ // data not from form, add missing visibility info
+ $data->visible = $category->visible;
+ }
+ $data->visibleold = $data->visible;
+
+ $newcourseid = $DB->insert_record('course', $data);
+ $context = context_course::instance($newcourseid, MUST_EXIST);
+
+ if ($editoroptions) {
+ // Save the files used in the summary editor and store
+ $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course', 'summary', 0);
+ $DB->set_field('course', 'summary', $data->summary, array('id'=>$newcourseid));
+ $DB->set_field('course', 'summaryformat', $data->summary_format, array('id'=>$newcourseid));
+ }
+
+ // update course format options
+ course_get_format($newcourseid)->update_course_format_options($data);
+
+ $course = course_get_format($newcourseid)->get_course();
+
+ // Setup the blocks
+ blocks_add_default_course_blocks($course);
+
+ // Create a default section.
+ course_create_sections_if_missing($course, 0);
+
+ fix_course_sortorder();
+
+ // new context created - better mark it as dirty
+ mark_context_dirty($context->path);
+
+ // Save any custom role names.
+ save_local_role_names($course->id, (array)$data);
+
+ // set up enrolments
+ enrol_course_updated(true, $course, $data);
+
+ add_to_log(SITEID, 'course', 'new', 'view.php?id='.$course->id, $data->fullname.' (ID '.$course->id.')');
+
+ // Trigger events
+ events_trigger('course_created', $course);
+
+ return $course;
+}
+
+/**
+ * Create a new course category and marks the context as dirty
+ *
+ * This function does not set the sortorder for the new category and
+ * @see{fix_course_sortorder} should be called after creating a new course
+ * category
+ *
+ * Please note that this function does not verify access control.
+ *
+ * @param object $category All of the data required for an entry in the course_categories table
+ * @return object new course category
+ */
+function create_course_category($category) {
+ global $DB;
+
+ $category->timemodified = time();
+ $category->id = $DB->insert_record('course_categories', $category);
+ $category = $DB->get_record('course_categories', array('id' => $category->id));
+
+ // We should mark the context as dirty
+ $category->context = context_coursecat::instance($category->id);
+ $category->context->mark_dirty();
+
+ return $category;
+}
+
+/**
+ * Update a course.
+ *
+ * Please note this functions does not verify any access control,
+ * the calling code is responsible for all validation (usually it is the form definition).
+ *
+ * @param object $data - all the data needed for an entry in the 'course' table
+ * @param array $editoroptions course description editor options
+ * @return void
+ */
+function update_course($data, $editoroptions = NULL) {
+ global $CFG, $DB;
+
+ $data->timemodified = time();
+
+ $oldcourse = course_get_format($data->id)->get_course();
+ $context = context_course::instance($oldcourse->id);
+
+ if ($editoroptions) {
+ $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course', 'summary', 0);
+ }
+
+ if (!isset($data->category) or empty($data->category)) {
+ // prevent nulls and 0 in category field
+ unset($data->category);
+ }
+ $movecat = (isset($data->category) and $oldcourse->category != $data->category);
+
+ if (!isset($data->visible)) {
+ // data not from form, add missing visibility info
+ $data->visible = $oldcourse->visible;
+ }
+
+ if ($data->visible != $oldcourse->visible) {
+ // reset the visibleold flag when manually hiding/unhiding course
+ $data->visibleold = $data->visible;
+ } else {
+ if ($movecat) {
+ $newcategory = $DB->get_record('course_categories', array('id'=>$data->category));
+ if (empty($newcategory->visible)) {
+ // make sure when moving into hidden category the course is hidden automatically
+ $data->visible = 0;
+ }
+ }
+ }
+
+ // Update with the new data
+ $DB->update_record('course', $data);
+ // make sure the modinfo cache is reset
+ rebuild_course_cache($data->id);
+
+ // update course format options with full course data
+ course_get_format($data->id)->update_course_format_options($data, $oldcourse);
+
+ $course = $DB->get_record('course', array('id'=>$data->id));
+
+ if ($movecat) {
+ $newparent = context_coursecat::instance($course->category);
+ context_moved($context, $newparent);
+ }
+
+ fix_course_sortorder();
+
+ // Test for and remove blocks which aren't appropriate anymore
+ blocks_remove_inappropriate($course);
+
+ // Save any custom role names.
+ save_local_role_names($course->id, $data);
+
+ // update enrol settings
+ enrol_course_updated(false, $course, $data);
+
+ add_to_log($course->id, "course", "update", "edit.php?id=$course->id", $course->id);
+
+ // Trigger events
+ events_trigger('course_updated', $course);
+
+ if ($oldcourse->format !== $course->format) {
+ // Remove all options stored for the previous format
+ // We assume that new course format migrated everything it needed watching trigger
+ // 'course_updated' and in method format_XXX::update_course_format_options()
+ $DB->delete_records('course_format_options',
+ array('courseid' => $course->id, 'format' => $oldcourse->format));
+ }
+}
+
+/**
+ * Average number of participants
+ * @return integer
+ */
+function average_number_of_participants() {
+ global $DB, $SITE;
+
+ //count total of enrolments for visible course (except front page)
+ $sql = 'SELECT COUNT(*) FROM (
+ SELECT DISTINCT ue.userid, e.courseid
+ FROM {user_enrolments} ue, {enrol} e, {course} c
+ WHERE ue.enrolid = e.id
+ AND e.courseid <> :siteid
+ AND c.id = e.courseid
+ AND c.visible = 1) total';
+ $params = array('siteid' => $SITE->id);
+ $enrolmenttotal = $DB->count_records_sql($sql, $params);
+
+
+ //count total of visible courses (minus front page)
+ $coursetotal = $DB->count_records('course', array('visible' => 1));
+ $coursetotal = $coursetotal - 1 ;
+
+ //average of enrolment
+ if (empty($coursetotal)) {
+ $participantaverage = 0;
+ } else {
+ $participantaverage = $enrolmenttotal / $coursetotal;
+ }
+
+ return $participantaverage;
+}
+
+/**
+ * Average number of course modules
+ * @return integer
+ */
+function average_number_of_courses_modules() {
+ global $DB, $SITE;
+
+ //count total of visible course module (except front page)
+ $sql = 'SELECT COUNT(*) FROM (
+ SELECT cm.course, cm.module
+ FROM {course} c, {course_modules} cm
+ WHERE c.id = cm.course
+ AND c.id <> :siteid
+ AND cm.visible = 1
+ AND c.visible = 1) total';
+ $params = array('siteid' => $SITE->id);
+ $moduletotal = $DB->count_records_sql($sql, $params);
+
+
+ //count total of visible courses (minus front page)
+ $coursetotal = $DB->count_records('course', array('visible' => 1));
+ $coursetotal = $coursetotal - 1 ;
+
+ //average of course module
+ if (empty($coursetotal)) {
+ $coursemoduleaverage = 0;
+ } else {
+ $coursemoduleaverage = $moduletotal / $coursetotal;
+ }
+
+ return $coursemoduleaverage;
+}
+
+/**
+ * This class pertains to course requests and contains methods associated with
+ * create, approving, and removing course requests.
+ *
+ * Please note we do not allow embedded images here because there is no context
+ * to store them with proper access control.
+ *
+ * @copyright 2009 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ *
+ * @property-read int $id
+ * @property-read string $fullname
+ * @property-read string $shortname
+ * @property-read string $summary
+ * @property-read int $summaryformat
+ * @property-read int $summarytrust
+ * @property-read string $reason
+ * @property-read int $requester
+ */
+class course_request {
+
+ /**
+ * This is the stdClass that stores the properties for the course request
+ * and is externally accessed through the __get magic method
+ * @var stdClass
+ */
+ protected $properties;
+
+ /**
+ * An array of options for the summary editor used by course request forms.
+ * This is initially set by {@link summary_editor_options()}
+ * @var array
+ * @static
+ */
+ protected static $summaryeditoroptions;
+
+ /**
+ * Static function to prepare the summary editor for working with a course
+ * request.
+ *
+ * @static
+ * @param null|stdClass $data Optional, an object containing the default values
+ * for the form, these may be modified when preparing the
+ * editor so this should be called before creating the form
+ * @return stdClass An object that can be used to set the default values for
+ * an mforms form
+ */
+ public static function prepare($data=null) {
+ if ($data === null) {
+ $data = new stdClass;
+ }
+ $data = file_prepare_standard_editor($data, 'summary', self::summary_editor_options());
+ return $data;
+ }
+
+ /**
+ * Static function to create a new course request when passed an array of properties
+ * for it.
+ *
+ * This function also handles saving any files that may have been used in the editor
+ *
+ * @static
+ * @param stdClass $data
+ * @return course_request The newly created course request
+ */
+ public static function create($data) {
+ global $USER, $DB, $CFG;
+ $data->requester = $USER->id;
+
+ // Setting the default category if none set.
+ if (empty($data->category) || empty($CFG->requestcategoryselection)) {
+ $data->category = $CFG->defaultrequestcategory;
+ }
+
+ // Summary is a required field so copy the text over
+ $data->summary = $data->summary_editor['text'];
+ $data->summaryformat = $data->summary_editor['format'];
+
+ $data->id = $DB->insert_record('course_request', $data);
+
+ // Create a new course_request object and return it
+ $request = new course_request($data);
+
+ // Notify the admin if required.
+ if ($users = get_users_from_config($CFG->courserequestnotify, 'moodle/site:approvecourse')) {
+
+ $a = new stdClass;
+ $a->link = "$CFG->wwwroot/course/pending.php";
+ $a->user = fullname($USER);
+ $subject = get_string('courserequest');
+ $message = get_string('courserequestnotifyemail', 'admin', $a);
+ foreach ($users as $user) {
+ $request->notify($user, $USER, 'courserequested', $subject, $message);
+ }
+ }
+
+ return $request;
+ }
+
+ /**
+ * Returns an array of options to use with a summary editor
+ *
+ * @uses course_request::$summaryeditoroptions
+ * @return array An array of options to use with the editor
+ */
+ public static function summary_editor_options() {
+ global $CFG;
+ if (self::$summaryeditoroptions === null) {
+ self::$summaryeditoroptions = array('maxfiles' => 0, 'maxbytes'=>0);
+ }
+ return self::$summaryeditoroptions;
+ }
+
+ /**
+ * Loads the properties for this course request object. Id is required and if
+ * only id is provided then we load the rest of the properties from the database
+ *
+ * @param stdClass|int $properties Either an object containing properties
+ * or the course_request id to load
+ */
+ public function __construct($properties) {
+ global $DB;
+ if (empty($properties->id)) {
+ if (empty($properties)) {
+ throw new coding_exception('You must provide a course request id when creating a course_request object');
+ }
+ $id = $properties;
+ $properties = new stdClass;
+ $properties->id = (int)$id;
+ unset($id);
+ }
+ if (empty($properties->requester)) {
+ if (!($this->properties = $DB->get_record('course_request', array('id' => $properties->id)))) {
+ print_error('unknowncourserequest');
+ }
+ } else {
+ $this->properties = $properties;
+ }
+ $this->properties->collision = null;
+ }
+
+ /**
+ * Returns the requested property
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key) {
+ return $this->properties->$key;
+ }
+
+ /**
+ * Override this to ensure empty($request->blah) calls return a reliable answer...
+ *
+ * This is required because we define the __get method
+ *
+ * @param mixed $key
+ * @return bool True is it not empty, false otherwise
+ */
+ public function __isset($key) {
+ return (!empty($this->properties->$key));
+ }
+
+ /**
+ * Returns the user who requested this course
+ *
+ * Uses a static var to cache the results and cut down the number of db queries
+ *
+ * @staticvar array $requesters An array of cached users
+ * @return stdClass The user who requested the course
+ */
+ public function get_requester() {
+ global $DB;
+ static $requesters= array();
+ if (!array_key_exists($this->properties->requester, $requesters)) {
+ $requesters[$this->properties->requester] = $DB->get_record('user', array('id'=>$this->properties->requester));
+ }
+ return $requesters[$this->properties->requester];
+ }
+
+ /**
+ * Checks that the shortname used by the course does not conflict with any other
+ * courses that exist
+ *
+ * @param string|null $shortnamemark The string to append to the requests shortname
+ * should a conflict be found
+ * @return bool true is there is a conflict, false otherwise
+ */
+ public function check_shortname_collision($shortnamemark = '[*]') {
+ global $DB;
+
+ if ($this->properties->collision !== null) {
+ return $this->properties->collision;
+ }
+
+ if (empty($this->properties->shortname)) {
+ debugging('Attempting to check a course request shortname before it has been set', DEBUG_DEVELOPER);
+ $this->properties->collision = false;
+ } else if ($DB->record_exists('course', array('shortname' => $this->properties->shortname))) {
+ if (!empty($shortnamemark)) {
+ $this->properties->shortname .= ' '.$shortnamemark;
+ }
+ $this->properties->collision = true;
+ } else {
+ $this->properties->collision = false;
+ }
+ return $this->properties->collision;
+ }
+
+ /**
+ * This function approves the request turning it into a course
+ *
+ * This function converts the course request into a course, at the same time
+ * transferring any files used in the summary to the new course and then removing
+ * the course request and the files associated with it.
+ *
+ * @return int The id of the course that was created from this request
+ */
+ public function approve() {
+ global $CFG, $DB, $USER;
+
+ $user = $DB->get_record('user', array('id' => $this->properties->requester, 'deleted'=>0), '*', MUST_EXIST);
+
+ $courseconfig = get_config('moodlecourse');
+
+ // Transfer appropriate settings
+ $data = clone($this->properties);
+ unset($data->id);
+ unset($data->reason);
+ unset($data->requester);
+
+ // If the category is not set, if the current user does not have the rights to change the category, or if the
+ // category does not exist, we set the default category to the course to be approved.
+ // The system level is used because the capability moodle/site:approvecourse is based on a system level.
+ if (empty($data->category) || !has_capability('moodle/course:changecategory', context_system::instance()) ||
+ (!$category = get_course_category($data->category))) {
+ $category = get_course_category($CFG->defaultrequestcategory);
+ }
+
+ // Set category
+ $data->category = $category->id;
+ $data->sortorder = $category->sortorder; // place as the first in category
+
+ // Set misc settings
+ $data->requested = 1;
+
+ // Apply course default settings
+ $data->format = $courseconfig->format;
+ $data->newsitems = $courseconfig->newsitems;
+ $data->showgrades = $courseconfig->showgrades;
+ $data->showreports = $courseconfig->showreports;
+ $data->maxbytes = $courseconfig->maxbytes;
+ $data->groupmode = $courseconfig->groupmode;
+ $data->groupmodeforce = $courseconfig->groupmodeforce;
+ $data->visible = $courseconfig->visible;
+ $data->visibleold = $data->visible;
+ $data->lang = $courseconfig->lang;
+
+ $course = create_course($data);
+ $context = context_course::instance($course->id, MUST_EXIST);
+
+ // add enrol instances
+ if (!$DB->record_exists('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) {
+ if ($manual = enrol_get_plugin('manual')) {
+ $manual->add_default_instance($course);
+ }
+ }
+
+ // enrol the requester as teacher if necessary
+ if (!empty($CFG->creatornewroleid) and !is_viewing($context, $user, 'moodle/role:assign') and !is_enrolled($context, $user, 'moodle/role:assign')) {
+ enrol_try_internal_enrol($course->id, $user->id, $CFG->creatornewroleid);
+ }
+
+ $this->delete();
+
+ $a = new stdClass();
+ $a->name = format_string($course->fullname, true, array('context' => context_course::instance($course->id)));
+ $a->url = $CFG->wwwroot.'/course/view.php?id=' . $course->id;
+ $this->notify($user, $USER, 'courserequestapproved', get_string('courseapprovedsubject'), get_string('courseapprovedemail2', 'moodle', $a));
+
+ return $course->id;
+ }
+
+ /**
+ * Reject a course request
+ *
+ * This function rejects a course request, emailing the requesting user the
+ * provided notice and then removing the request from the database
+ *
+ * @param string $notice The message to display to the user
+ */
+ public function reject($notice) {
+ global $USER, $DB;
+ $user = $DB->get_record('user', array('id' => $this->properties->requester), '*', MUST_EXIST);
+ $this->notify($user, $USER, 'courserequestrejected', get_string('courserejectsubject'), get_string('courserejectemail', 'moodle', $notice));
+ $this->delete();
+ }
+
+ /**
+ * Deletes the course request and any associated files
+ */
+ public function delete() {
+ global $DB;
+ $DB->delete_records('course_request', array('id' => $this->properties->id));
+ }
+
+ /**
+ * Send a message from one user to another using events_trigger
+ *
+ * @param object $touser
+ * @param object $fromuser
+ * @param string $name
+ * @param string $subject
+ * @param string $message
+ */
+ protected function notify($touser, $fromuser, $name='courserequested', $subject, $message) {
+ $eventdata = new stdClass();
+ $eventdata->component = 'moodle';
+ $eventdata->name = $name;
+ $eventdata->userfrom = $fromuser;
+ $eventdata->userto = $touser;
+ $eventdata->subject = $subject;
+ $eventdata->fullmessage = $message;
+ $eventdata->fullmessageformat = FORMAT_PLAIN;
+ $eventdata->fullmessagehtml = '';
+ $eventdata->smallmessage = '';
+ $eventdata->notification = 1;
+ message_send($eventdata);
+ }
+}
+
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function course_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ // if above course context ,display all course fomats
+ list($currentcontext, $course, $cm) = get_context_info_array($currentcontext->id);
+ if ($course->id == SITEID) {
+ return array('*'=>get_string('page-x', 'pagetype'));
+ } else {
+ return array('*'=>get_string('page-x', 'pagetype'),
+ 'course-*'=>get_string('page-course-x', 'pagetype'),
+ 'course-view-*'=>get_string('page-course-view-x', 'pagetype')
+ );
+ }
+}
+
+/**
+ * Determine whether course ajax should be enabled for the specified course
+ *
+ * @param stdClass $course The course to test against
+ * @return boolean Whether course ajax is enabled or note
+ */
+function course_ajax_enabled($course) {
+ global $CFG, $PAGE, $SITE;
+
+ // Ajax must be enabled globally
+ if (!$CFG->enableajax) {
+ return false;
+ }
+
+ // The user must be editing for AJAX to be included
+ if (!$PAGE->user_is_editing()) {
+ return false;
+ }
+
+ // Check that the theme suports
+ if (!$PAGE->theme->enablecourseajax) {
+ return false;
+ }
+
+ // Check that the course format supports ajax functionality
+ // The site 'format' doesn't have information on course format support
+ if ($SITE->id !== $course->id) {
+ $courseformatajaxsupport = course_format_ajax_support($course->format);
+ if (!$courseformatajaxsupport->capable) {
+ return false;
+ }
+ }
+
+ // All conditions have been met so course ajax should be enabled
+ return true;
+}
+
+/**
+ * Include the relevant javascript and language strings for the resource
+ * toolbox YUI module
+ *
+ * @param integer $id The ID of the course being applied to
+ * @param array $usedmodules An array containing the names of the modules in use on the page
+ * @param array $enabledmodules An array containing the names of the enabled (visible) modules on this site
+ * @param stdClass $config An object containing configuration parameters for ajax modules including:
+ * * resourceurl The URL to post changes to for resource changes
+ * * sectionurl The URL to post changes to for section changes
+ * * pageparams Additional parameters to pass through in the post
+ * @return bool
+ */
+function include_course_ajax($course, $usedmodules = array(), $enabledmodules = null, $config = null) {
+ global $PAGE, $SITE;
+
+ // Ensure that ajax should be included
+ if (!course_ajax_enabled($course)) {
+ return false;
+ }
+
+ if (!$config) {
+ $config = new stdClass();
+ }
+
+ // The URL to use for resource changes
+ if (!isset($config->resourceurl)) {
+ $config->resourceurl = '/course/rest.php';
+ }
+
+ // The URL to use for section changes
+ if (!isset($config->sectionurl)) {
+ $config->sectionurl = '/course/rest.php';
+ }
+
+ // Any additional parameters which need to be included on page submission
+ if (!isset($config->pageparams)) {
+ $config->pageparams = array();
+ }
+
+ // Include toolboxes
+ $PAGE->requires->yui_module('moodle-course-toolboxes',
+ 'M.course.init_resource_toolbox',
+ array(array(
+ 'courseid' => $course->id,
+ 'ajaxurl' => $config->resourceurl,
+ 'config' => $config,
+ ))
+ );
+ $PAGE->requires->yui_module('moodle-course-toolboxes',
+ 'M.course.init_section_toolbox',
+ array(array(
+ 'courseid' => $course->id,
+ 'format' => $course->format,
+ 'ajaxurl' => $config->sectionurl,
+ 'config' => $config,
+ ))
+ );
+
+ // Include course dragdrop
+ if ($course->id != $SITE->id) {
+ $PAGE->requires->yui_module('moodle-course-dragdrop', 'M.course.init_section_dragdrop',
+ array(array(
+ 'courseid' => $course->id,
+ 'ajaxurl' => $config->sectionurl,
+ 'config' => $config,
+ )), null, true);
+
+ $PAGE->requires->yui_module('moodle-course-dragdrop', 'M.course.init_resource_dragdrop',
+ array(array(
+ 'courseid' => $course->id,
+ 'ajaxurl' => $config->resourceurl,
+ 'config' => $config,
+ )), null, true);
+ }
+
+ // Include blocks dragdrop
+ $params = array(
+ 'courseid' => $course->id,
+ 'pagetype' => $PAGE->pagetype,
+ 'pagelayout' => $PAGE->pagelayout,
+ 'subpage' => $PAGE->subpage,
+ 'regions' => $PAGE->blocks->get_regions(),
+ );
+ $PAGE->requires->yui_module('moodle-core-blocks', 'M.core_blocks.init_dragdrop', array($params), null, true);
+
+ // Require various strings for the command toolbox
+ $PAGE->requires->strings_for_js(array(
+ 'moveleft',
+ 'deletechecktype',
+ 'deletechecktypename',
+ 'edittitle',
+ 'edittitleinstructions',
+ 'show',
+ 'hide',
+ 'groupsnone',
+ 'groupsvisible',
+ 'groupsseparate',
+ 'clicktochangeinbrackets',
+ 'markthistopic',
+ 'markedthistopic',
+ 'move',
+ 'movesection',
+ ), 'moodle');
+
+ // Include format-specific strings
+ if ($course->id != $SITE->id) {
+ $PAGE->requires->strings_for_js(array(
+ 'showfromothers',
+ 'hidefromothers',
+ ), 'format_' . $course->format);
+ }
+
+ // For confirming resource deletion we need the name of the module in question
+ foreach ($usedmodules as $module => $modname) {
+ $PAGE->requires->string_for_js('pluginname', $module);
+ }
+
+ // Load drag and drop upload AJAX.
+ dndupload_add_to_course($course, $enabledmodules);
+
+ // Add the module chooser
+ $PAGE->requires->yui_module('moodle-course-modchooser',
+ 'M.course.init_chooser',
+ array(array('courseid' => $course->id, 'closeButtonTitle' => get_string('close', 'editor')))
+ );
+ $PAGE->requires->strings_for_js(array(
+ 'addresourceoractivity',
+ 'modchooserenable',
+ 'modchooserdisable',
+ ), 'moodle');
+
+ return true;
+}
+
+/**
+ * Returns the sorted list of available course formats, filtered by enabled if necessary
+ *
+ * @param bool $enabledonly return only formats that are enabled
+ * @return array array of sorted format names
+ */
+function get_sorted_course_formats($enabledonly = false) {
+ global $CFG;
+ $formats = get_plugin_list('format');
+
+ if (!empty($CFG->format_plugins_sortorder)) {
+ $order = explode(',', $CFG->format_plugins_sortorder);
+ $order = array_merge(array_intersect($order, array_keys($formats)),
+ array_diff(array_keys($formats), $order));
+ } else {
+ $order = array_keys($formats);
+ }
+ if (!$enabledonly) {
+ return $order;
+ }
+ $sortedformats = array();
+ foreach ($order as $formatname) {
+ if (!get_config('format_'.$formatname, 'disabled')) {
+ $sortedformats[] = $formatname;
+ }
+ }
+ return $sortedformats;
+}
+
+/**
+ * The URL to use for the specified course (with section)
+ *
+ * @param int|stdClass $courseorid The course to get the section name for (either object or just course id)
+ * @param int|stdClass $section Section object from database or just field course_sections.section
+ * if omitted the course view page is returned
+ * @param array $options options for view URL. At the moment core uses:
+ * 'navigation' (bool) if true and section has no separate page, the function returns null
+ * 'sr' (int) used by multipage formats to specify to which section to return
+ * @return moodle_url The url of course
+ */
+function course_get_url($courseorid, $section = null, $options = array()) {
+ return course_get_format($courseorid)->get_view_url($section, $options);
+}
--- /dev/null
+<?php
+// Allows a teacher/admin to login as another user (in stealth mode)
+
+require_once('../config.php');
+require_once('lib.php');
+
+$id = optional_param('id', SITEID, PARAM_INT); // course id
+
+/// Reset user back to their real self if needed, for security reasons you need to log out and log in again
+if (session_is_loggedinas()) {
+ require_sesskey();
+ require_logout();
+
+ if ($id and $id != SITEID) {
+ $SESSION->wantsurl = "$CFG->wwwroot/course/view.php?id=".$id;
+ } else {
+ $SESSION->wantsurl = "$CFG->wwwroot/";
+ }
+
+ redirect(get_login_url());
+}
+
+///-------------------------------------
+/// We are trying to log in as this user in the first place
+
+$userid = required_param('user', PARAM_INT); // login as this user
+
+$url = new moodle_url('/course/loginas.php', array('user'=>$userid, 'sesskey'=>sesskey()));
+if ($id !== SITEID) {
+ $url->param('id', $id);
+}
+$PAGE->set_url($url);
+
+require_sesskey();
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+
+/// User must be logged in
+
+$systemcontext = context_system::instance();
+$coursecontext = context_course::instance($course->id);
+
+require_login();
+
+if (has_capability('moodle/user:loginas', $systemcontext)) {
+ if (is_siteadmin($userid)) {
+ print_error('nologinas');
+ }
+ $context = $systemcontext;
+ $PAGE->set_context($context);
+} else {
+ require_login($course);
+ require_capability('moodle/user:loginas', $coursecontext);
+ if (is_siteadmin($userid)) {
+ print_error('nologinas');
+ }
+ if (!is_enrolled($coursecontext, $userid)) {
+ print_error('usernotincourse');
+ }
+ $context = $coursecontext;
+}
+
+/// Login as this user and return to course home page.
+$oldfullname = fullname($USER, true);
+session_loginas($userid, $context);
+$newfullname = fullname($USER, true);
+
+add_to_log($course->id, "course", "loginas", "../user/view.php?id=$course->id&user=$userid", "$oldfullname -> $newfullname");
+
+$strloginas = get_string('loginas');
+$strloggedinas = get_string('loggedinas', '', $newfullname);
+
+$PAGE->set_title($strloggedinas);
+$PAGE->set_heading($course->fullname);
+$PAGE->navbar->add($strloggedinas);
+notice($strloggedinas, "$CFG->wwwroot/course/view.php?id=$course->id");
\ No newline at end of file
--- /dev/null
+<?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/>.
+
+/**
+ * Moves, adds, updates, duplicates or deletes modules in a course
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require("../config.php");
+require_once("lib.php");
+
+$sectionreturn = optional_param('sr', null, PARAM_INT);
+$add = optional_param('add', '', PARAM_ALPHA);
+$type = optional_param('type', '', PARAM_ALPHA);
+$indent = optional_param('indent', 0, PARAM_INT);
+$update = optional_param('update', 0, PARAM_INT);
+$duplicate = optional_param('duplicate', 0, PARAM_INT);
+$hide = optional_param('hide', 0, PARAM_INT);
+$show = optional_param('show', 0, PARAM_INT);
+$copy = optional_param('copy', 0, PARAM_INT);
+$moveto = optional_param('moveto', 0, PARAM_INT);
+$movetosection = optional_param('movetosection', 0, PARAM_INT);
+$delete = optional_param('delete', 0, PARAM_INT);
+$course = optional_param('course', 0, PARAM_INT);
+$groupmode = optional_param('groupmode', -1, PARAM_INT);
+$cancelcopy = optional_param('cancelcopy', 0, PARAM_BOOL);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+// This page should always redirect
+$url = new moodle_url('/course/mod.php');
+foreach (compact('indent','update','hide','show','copy','moveto','movetosection','delete','course','cancelcopy','confirm') as $key=>$value) {
+ if ($value !== 0) {
+ $url->param($key, $value);
+ }
+}
+$url->param('sr', $sectionreturn);
+if ($add !== '') {
+ $url->param('add', $add);
+}
+if ($type !== '') {
+ $url->param('type', $type);
+}
+if ($groupmode !== '') {
+ $url->param('groupmode', $groupmode);
+}
+$PAGE->set_url($url);
+
+require_login();
+
+//check if we are adding / editing a module that has new forms using formslib
+if (!empty($add)) {
+ $id = required_param('id', PARAM_INT);
+ $section = required_param('section', PARAM_INT);
+ $type = optional_param('type', '', PARAM_ALPHA);
+ $returntomod = optional_param('return', 0, PARAM_BOOL);
+
+ redirect("$CFG->wwwroot/course/modedit.php?add=$add&type=$type&course=$id§ion=$section&return=$returntomod&sr=$sectionreturn");
+
+} else if (!empty($update)) {
+ $cm = get_coursemodule_from_id('', $update, 0, true, MUST_EXIST);
+ $returntomod = optional_param('return', 0, PARAM_BOOL);
+ redirect("$CFG->wwwroot/course/modedit.php?update=$update&return=$returntomod&sr=$sectionreturn");
+
+} else if (!empty($duplicate)) {
+ $cm = get_coursemodule_from_id('', $duplicate, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:manageactivities', $coursecontext);
+
+ if (!$confirm or !confirm_sesskey()) {
+ $PAGE->set_title(get_string('duplicate'));
+ $PAGE->set_heading($course->fullname);
+ $PAGE->navbar->add(get_string('duplicatinga', 'core', format_string($cm->name)));
+ $PAGE->set_pagelayout('incourse');
+
+ $a = new stdClass();
+ $a->modtype = get_string('modulename', $cm->modname);
+ $a->modname = format_string($cm->name);
+ $a->modid = $cm->id;
+
+ echo $OUTPUT->header();
+ echo $OUTPUT->confirm(
+ get_string('duplicateconfirm', 'core', $a),
+ new single_button(
+ new moodle_url('/course/modduplicate.php', array(
+ 'cmid' => $cm->id, 'course' => $course->id, 'sr' => $sectionreturn)),
+ get_string('continue'),
+ 'post'),
+ new single_button(
+ course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)),
+ get_string('cancel'),
+ 'get')
+ );
+ echo $OUTPUT->footer();
+ die();
+ }
+
+} else if (!empty($delete)) {
+ $cm = get_coursemodule_from_id('', $delete, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
+
+ $return = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
+
+ if (!$confirm or !confirm_sesskey()) {
+ $fullmodulename = get_string('modulename', $cm->modname);
+
+ $optionsyes = array('confirm'=>1, 'delete'=>$cm->id, 'sesskey'=>sesskey(), 'sr' => $sectionreturn);
+
+ $strdeletecheck = get_string('deletecheck', '', $fullmodulename);
+ $strdeletecheckfull = get_string('deletecheckfull', '', "$fullmodulename '$cm->name'");
+
+ $PAGE->set_pagetype('mod-' . $cm->modname . '-delete');
+ $PAGE->set_title($strdeletecheck);
+ $PAGE->set_heading($course->fullname);
+ $PAGE->navbar->add($strdeletecheck);
+ echo $OUTPUT->header();
+
+ // print_simple_box_start('center', '60%', '#FFAAAA', 20, 'noticebox');
+ echo $OUTPUT->box_start('noticebox');
+ $formcontinue = new single_button(new moodle_url("$CFG->wwwroot/course/mod.php", $optionsyes), get_string('yes'));
+ $formcancel = new single_button($return, get_string('no'), 'get');
+ echo $OUTPUT->confirm($strdeletecheckfull, $formcontinue, $formcancel);
+ echo $OUTPUT->box_end();
+ echo $OUTPUT->footer();
+
+ exit;
+ }
+
+ $modlib = "$CFG->dirroot/mod/$cm->modname/lib.php";
+
+ if (file_exists($modlib)) {
+ require_once($modlib);
+ } else {
+ print_error('modulemissingcode', '', '', $modlib);
+ }
+
+ $deleteinstancefunction = $cm->modname."_delete_instance";
+
+ if (!$deleteinstancefunction($cm->instance)) {
+ echo $OUTPUT->notification("Could not delete the $cm->modname (instance)");
+ }
+
+ // remove all module files in case modules forget to do that
+ $fs = get_file_storage();
+ $fs->delete_area_files($modcontext->id);
+
+ if (!delete_course_module($cm->id)) {
+ echo $OUTPUT->notification("Could not delete the $cm->modname (coursemodule)");
+ }
+ if (!delete_mod_from_section($cm->id, $cm->section)) {
+ echo $OUTPUT->notification("Could not delete the $cm->modname from that section");
+ }
+
+ // Trigger a mod_deleted event with information about this module.
+ $eventdata = new stdClass();
+ $eventdata->modulename = $cm->modname;
+ $eventdata->cmid = $cm->id;
+ $eventdata->courseid = $course->id;
+ $eventdata->userid = $USER->id;
+ events_trigger('mod_deleted', $eventdata);
+
+ add_to_log($course->id, 'course', "delete mod",
+ "view.php?id=$cm->course",
+ "$cm->modname $cm->instance", $cm->id);
+
+ redirect($return);
+}
+
+
+if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
+ $cm = get_coursemodule_from_id('', $USER->activitycopy, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
+
+ if (!empty($movetosection)) {
+ if (!$section = $DB->get_record('course_sections', array('id'=>$movetosection, 'course'=>$cm->course))) {
+ print_error('sectionnotexist');
+ }
+ $beforecm = NULL;
+
+ } else { // normal moveto
+ if (!$beforecm = get_coursemodule_from_id('', $moveto, $cm->course, true)) {
+ print_error('invalidcoursemodule');
+ }
+ if (!$section = $DB->get_record('course_sections', array('id'=>$beforecm->section, 'course'=>$cm->course))) {
+ print_error('sectionnotexist');
+ }
+ }
+
+ if (!ismoving($section->course)) {
+ print_error('needcopy', '', "view.php?id=$section->course");
+ }
+
+ moveto_module($cm, $section, $beforecm);
+
+ $sectionreturn = $USER->activitycopysectionreturn;
+ unset($USER->activitycopy);
+ unset($USER->activitycopycourse);
+ unset($USER->activitycopyname);
+ unset($USER->activitycopysectionreturn);
+
+ redirect(course_get_url($course, $section->section, array('sr' => $sectionreturn)));
+
+} else if (!empty($indent) and confirm_sesskey()) {
+ $id = required_param('id', PARAM_INT);
+
+ $cm = get_coursemodule_from_id('', $id, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
+
+ $cm->indent += $indent;
+
+ if ($cm->indent < 0) {
+ $cm->indent = 0;
+ }
+
+ $DB->set_field('course_modules', 'indent', $cm->indent, array('id'=>$cm->id));
+
+ rebuild_course_cache($cm->course);
+
+ redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
+
+} else if (!empty($hide) and confirm_sesskey()) {
+ $cm = get_coursemodule_from_id('', $hide, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:activityvisibility', $modcontext);
+
+ set_coursemodule_visible($cm->id, 0);
+
+ redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
+
+} else if (!empty($show) and confirm_sesskey()) {
+ $cm = get_coursemodule_from_id('', $show, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:activityvisibility', $modcontext);
+
+ $section = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST);
+
+ $module = $DB->get_record('modules', array('id'=>$cm->module), '*', MUST_EXIST);
+
+ if ($module->visible and ($section->visible or (SITEID == $cm->course))) {
+ set_coursemodule_visible($cm->id, 1);
+ }
+
+ redirect(course_get_url($course, $section->section, array('sr' => $sectionreturn)));
+
+} else if ($groupmode > -1 and confirm_sesskey()) {
+ $id = required_param('id', PARAM_INT);
+
+ $cm = get_coursemodule_from_id('', $id, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
+
+ set_coursemodule_groupmode($cm->id, $groupmode);
+
+ redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
+
+} else if (!empty($copy) and confirm_sesskey()) { // value = course module
+ $cm = get_coursemodule_from_id('', $copy, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = context_course::instance($course->id);
+ $modcontext = context_module::instance($cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
+
+ $section = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST);
+
+ $USER->activitycopy = $copy;
+ $USER->activitycopycourse = $cm->course;
+ $USER->activitycopyname = $cm->name;
+ $USER->activitycopysectionreturn = $sectionreturn;
+
+ redirect(course_get_url($course, $section->section, array('sr' => $sectionreturn)));
+
+} else if (!empty($cancelcopy) and confirm_sesskey()) { // value = course module
+
+ $courseid = $USER->activitycopycourse;
+ $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
+
+ $cm = get_coursemodule_from_id('', $USER->activitycopy, 0, true, IGNORE_MISSING);
+ $sectionreturn = $USER->activitycopysectionreturn;
+ unset($USER->activitycopy);
+ unset($USER->activitycopycourse);
+ unset($USER->activitycopyname);
+ unset($USER->activitycopysectionreturn);
+ redirect(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
+} else {
+ print_error('unknowaction');
+}
+
+
--- /dev/null
+<?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/>.
+
+/**
+ * Duplicates a given course module
+ *
+ * The script backups and restores a single activity as if it was imported
+ * from the same course, using the default import settings. The newly created
+ * copy of the activity is then moved right below the original one.
+ *
+ * @package core
+ * @subpackage course
+ * @copyright 2011 David Mudrak <david@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/config.php');
+require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+require_once($CFG->libdir . '/filelib.php');
+
+$cmid = required_param('cmid', PARAM_INT);
+$courseid = required_param('course', PARAM_INT);
+$sectionreturn = optional_param('sr', null, PARAM_INT);
+
+$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
+$cm = get_coursemodule_from_id('', $cmid, $course->id, true, MUST_EXIST);
+$cmcontext = context_module::instance($cm->id);
+$context = context_course::instance($courseid);
+$section = $DB->get_record('course_sections', array('id' => $cm->section, 'course' => $cm->course));
+
+require_login($course);
+require_sesskey();
+require_capability('moodle/course:manageactivities', $context);
+// Require both target import caps to be able to duplicate, see make_editing_buttons()
+require_capability('moodle/backup:backuptargetimport', $context);
+require_capability('moodle/restore:restoretargetimport', $context);
+
+$PAGE->set_title(get_string('duplicate'));
+$PAGE->set_heading($course->fullname);
+$PAGE->set_url(new moodle_url('/course/modduplicate.php', array('cmid' => $cm->id, 'courseid' => $course->id)));
+$PAGE->set_pagelayout('incourse');
+
+$output = $PAGE->get_renderer('core', 'backup');
+
+$a = new stdClass();
+$a->modtype = get_string('modulename', $cm->modname);
+$a->modname = format_string($cm->name);
+
+if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) {
+ $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
+ print_error('duplicatenosupport', 'error', $url, $a);
+}
+
+// backup the activity
+
+$bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
+
+$backupid = $bc->get_backupid();
+$backupbasepath = $bc->get_plan()->get_basepath();
+
+$bc->execute_plan();
+
+$bc->destroy();
+
+// restore the backup immediately
+
+$rc = new restore_controller($backupid, $courseid,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
+
+if (!$rc->execute_precheck()) {
+ $precheckresults = $rc->get_precheck_results();
+ if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+ }
+
+ echo $output->header();
+ echo $output->precheck_notices($precheckresults);
+ $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
+ echo $output->continue_button($url);
+ echo $output->footer();
+ die();
+ }
+}
+
+$rc->execute_plan();
+
+// now a bit hacky part follows - we try to get the cmid of the newly
+// restored copy of the module
+$newcmid = null;
+$tasks = $rc->get_plan()->get_tasks();
+foreach ($tasks as $task) {
+ if (is_subclass_of($task, 'restore_activity_task')) {
+ if ($task->get_old_contextid() == $cmcontext->id) {
+ $newcmid = $task->get_moduleid();
+ break;
+ }
+ }
+}
+
+// if we know the cmid of the new course module, let us move it
+// right below the original one. otherwise it will stay at the
+// end of the section
+if ($newcmid) {
+ $newcm = get_coursemodule_from_id('', $newcmid, $course->id, true, MUST_EXIST);
+ moveto_module($newcm, $section, $cm);
+ moveto_module($cm, $section, $newcm);
+}
+
+$rc->destroy();
+
+if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+}
+
+echo $output->header();
+
+if ($newcmid) {
+ echo $output->confirm(
+ get_string('duplicatesuccess', 'core', $a),
+ new single_button(
+ new moodle_url('/course/modedit.php', array('update' => $newcmid, 'sr' => $sectionreturn)),
+ get_string('duplicatecontedit'),
+ 'get'),
+ new single_button(
+ course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)),
+ get_string('duplicatecontcourse'),
+ 'get')
+ );
+
+} else {
+ echo $output->notification(get_string('duplicatesuccess', 'core', $a), 'notifysuccess');
+ echo $output->continue_button(course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)));
+}
+
+echo $output->footer();
--- /dev/null
+<?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/>.
+
+/**
+* Adds or updates modules in a course using new formslib
+*
+* @package moodlecore
+* @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
+* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+*/
+
+require_once("../config.php");
+require_once("lib.php");
+require_once($CFG->libdir.'/filelib.php');
+require_once($CFG->libdir.'/gradelib.php');
+require_once($CFG->libdir.'/completionlib.php');
+require_once($CFG->libdir.'/conditionlib.php');
+require_once($CFG->libdir.'/plagiarismlib.php');
+
+$add = optional_param('add', '', PARAM_ALPHA); // module name
+$update = optional_param('update', 0, PARAM_INT);
+$return = optional_param('return', 0, PARAM_BOOL); //return to course/view.php if false or mod/modname/view.php if true
+$type = optional_param('type', '', PARAM_ALPHANUM); //TODO: hopefully will be removed in 2.0
+$sectionreturn = optional_param('sr', null, PARAM_INT);
+
+$url = new moodle_url('/course/modedit.php');
+$url->param('sr', $sectionreturn);
+if (!empty($return)) {
+ $url->param('return', $return);
+}
+
+if (!empty($add)) {
+ $section = required_param('section', PARAM_INT);
+ $course = required_param('course', PARAM_INT);
+
+ $url->param('add', $add);
+ $url->param('section', $section);
+ $url->param('course', $course);
+ $PAGE->set_url($url);
+
+ $course = $DB->get_record('course', array('id'=>$course), '*', MUST_EXIST);
+ $module = $DB->get_record('modules', array('name'=>$add), '*', MUST_EXIST);
+
+ require_login($course);
+ $context = context_course::instance($course->id);
+ require_capability('moodle/course:manageactivities', $context);
+
+ course_create_sections_if_missing($course, $section);
+ $cw = get_fast_modinfo($course)->get_section_info($section);
+
+ if (!course_allowed_module($course, $module->name)) {
+ print_error('moduledisable');
+ }
+
+ $cm = null;
+
+ $data = new stdClass();
+ $data->section = $section; // The section number itself - relative!!! (section column in course_sections)
+ $data->visible = $cw->visible;
+ $data->course = $course->id;
+ $data->module = $module->id;
+ $data->modulename = $module->name;
+ $data->groupmode = $course->groupmode;
+ $data->groupingid = $course->defaultgroupingid;
+ $data->groupmembersonly = 0;
+ $data->id = '';
+ $data->instance = '';
+ $data->coursemodule = '';
+ $data->add = $add;
+ $data->return = 0; //must be false if this is an add, go back to course view on cancel
+ $data->sr = $sectionreturn;
+
+ if (plugin_supports('mod', $data->modulename, FEATURE_MOD_INTRO, true)) {
+ $draftid_editor = file_get_submitted_draft_itemid('introeditor');
+ file_prepare_draft_area($draftid_editor, null, null, null, null);
+ $data->introeditor = array('text'=>'', 'format'=>FORMAT_HTML, 'itemid'=>$draftid_editor); // TODO: add better default
+ }
+
+ if (plugin_supports('mod', $data->modulename, FEATURE_ADVANCED_GRADING, false)
+ and has_capability('moodle/grade:managegradingforms', $context)) {
+ require_once($CFG->dirroot.'/grade/grading/lib.php');
+
+ $data->_advancedgradingdata['methods'] = grading_manager::available_methods();
+ $areas = grading_manager::available_areas('mod_'.$module->name);
+
+ foreach ($areas as $areaname => $areatitle) {
+ $data->_advancedgradingdata['areas'][$areaname] = array(
+ 'title' => $areatitle,
+ 'method' => '',
+ );
+ $formfield = 'advancedgradingmethod_'.$areaname;
+ $data->{$formfield} = '';
+ }
+ }
+
+ if (!empty($type)) { //TODO: hopefully will be removed in 2.0
+ $data->type = $type;
+ }
+
+ $sectionname = get_section_name($course, $cw);
+ $fullmodulename = get_string('modulename', $module->name);
+
+ if ($data->section && $course->format != 'site') {
+ $heading = new stdClass();
+ $heading->what = $fullmodulename;
+ $heading->to = $sectionname;
+ $pageheading = get_string('addinganewto', 'moodle', $heading);
+ } else {
+ $pageheading = get_string('addinganew', 'moodle', $fullmodulename);
+ }
+
+} else if (!empty($update)) {
+
+ $url->param('update', $update);
+ $PAGE->set_url($url);
+
+ $cm = get_coursemodule_from_id('', $update, 0, false, MUST_EXIST);
+ $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm); // needed to setup proper $COURSE
+ $context = context_module::instance($cm->id);
+ require_capability('moodle/course:manageactivities', $context);
+
+ $module = $DB->get_record('modules', array('id'=>$cm->module), '*', MUST_EXIST);
+ $data = $data = $DB->get_record($module->name, array('id'=>$cm->instance), '*', MUST_EXIST);
+ $cw = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST);
+
+ $data->coursemodule = $cm->id;
+ $data->section = $cw->section; // The section number itself - relative!!! (section column in course_sections)
+ $data->visible = $cm->visible; //?? $cw->visible ? $cm->visible : 0; // section hiding overrides
+ $data->cmidnumber = $cm->idnumber; // The cm IDnumber
+ $data->groupmode = groups_get_activity_groupmode($cm); // locked later if forced
+ $data->groupingid = $cm->groupingid;
+ $data->groupmembersonly = $cm->groupmembersonly;
+ $data->course = $course->id;
+ $data->module = $module->id;
+ $data->modulename = $module->name;
+ $data->instance = $cm->instance;
+ $data->return = $return;
+ $data->sr = $sectionreturn;
+ $data->update = $update;
+ $data->completion = $cm->completion;
+ $data->completionview = $cm->completionview;
+ $data->completionexpected = $cm->completionexpected;
+ $data->completionusegrade = is_null($cm->completiongradeitemnumber) ? 0 : 1;
+ $data->showdescription = $cm->showdescription;
+ if (!empty($CFG->enableavailability)) {
+ $data->availablefrom = $cm->availablefrom;
+ $data->availableuntil = $cm->availableuntil;
+ $data->showavailability = $cm->showavailability;
+ }
+
+ if (plugin_supports('mod', $data->modulename, FEATURE_MOD_INTRO, true)) {
+ $draftid_editor = file_get_submitted_draft_itemid('introeditor');
+ $currentintro = file_prepare_draft_area($draftid_editor, $context->id, 'mod_'.$data->modulename, 'intro', 0, array('subdirs'=>true), $data->intro);
+ $data->introeditor = array('text'=>$currentintro, 'format'=>$data->introformat, 'itemid'=>$draftid_editor);
+ }
+
+ if (plugin_supports('mod', $data->modulename, FEATURE_ADVANCED_GRADING, false)
+ and has_capability('moodle/grade:managegradingforms', $context)) {
+ require_once($CFG->dirroot.'/grade/grading/lib.php');
+ $gradingman = get_grading_manager($context, 'mod_'.$data->modulename);
+ $data->_advancedgradingdata['methods'] = $gradingman->get_available_methods();
+ $areas = $gradingman->get_available_areas();
+
+ foreach ($areas as $areaname => $areatitle) {
+ $gradingman->set_area($areaname);
+ $method = $gradingman->get_active_method();
+ $data->_advancedgradingdata['areas'][$areaname] = array(
+ 'title' => $areatitle,
+ 'method' => $method,
+ );
+ $formfield = 'advancedgradingmethod_'.$areaname;
+ $data->{$formfield} = $method;
+ }
+ }
+
+ if ($items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$data->modulename,
+ 'iteminstance'=>$data->instance, 'courseid'=>$course->id))) {
+ // add existing outcomes
+ foreach ($items as $item) {
+ if (!empty($item->outcomeid)) {
+ $data->{'outcome_'.$item->outcomeid} = 1;
+ }
+ }
+
+ // set category if present
+ $gradecat = false;
+ foreach ($items as $item) {
+ if ($gradecat === false) {
+ $gradecat = $item->categoryid;
+ continue;
+ }
+ if ($gradecat != $item->categoryid) {
+ //mixed categories
+ $gradecat = false;
+ break;
+ }
+ }
+ if ($gradecat !== false) {
+ // do not set if mixed categories present
+ $data->gradecat = $gradecat;
+ }
+ }
+
+ $sectionname = get_section_name($course, $cw);
+ $fullmodulename = get_string('modulename', $module->name);
+
+ if ($data->section && $course->format != 'site') {
+ $heading = new stdClass();
+ $heading->what = $fullmodulename;
+ $heading->in = $sectionname;
+ $pageheading = get_string('updatingain', 'moodle', $heading);
+ } else {
+ $pageheading = get_string('updatinga', 'moodle', $fullmodulename);
+ }
+
+} else {
+ require_login();
+ print_error('invalidaction');
+}
+
+$pagepath = 'mod-' . $module->name . '-';
+if (!empty($type)) { //TODO: hopefully will be removed in 2.0
+ $pagepath .= $type;
+} else {
+ $pagepath .= 'mod';
+}
+$PAGE->set_pagetype($pagepath);
+$PAGE->set_pagelayout('admin');
+
+$modmoodleform = "$CFG->dirroot/mod/$module->name/mod_form.php";
+if (file_exists($modmoodleform)) {
+ require_once($modmoodleform);
+} else {
+ print_error('noformdesc');
+}
+
+$modlib = "$CFG->dirroot/mod/$module->name/lib.php";
+if (file_exists($modlib)) {
+ include_once($modlib);
+} else {
+ print_error('modulemissingcode', '', '', $modlib);
+}
+
+$mformclassname = 'mod_'.$module->name.'_mod_form';
+$mform = new $mformclassname($data, $cw->section, $cm, $course);
+$mform->set_data($data);
+
+if ($mform->is_cancelled()) {
+ if ($return && !empty($cm->id)) {
+ redirect("$CFG->wwwroot/mod/$module->name/view.php?id=$cm->id");
+ } else {
+ redirect(course_get_url($course, $cw->section, array('sr' => $sectionreturn)));
+ }
+} else if ($fromform = $mform->get_data()) {
+ if (empty($fromform->coursemodule)) {
+ // Add
+ $cm = null;
+ $course = $DB->get_record('course', array('id'=>$fromform->course), '*', MUST_EXIST);
+ $fromform->instance = '';
+ $fromform->coursemodule = '';
+ } else {
+ // Update
+ $cm = get_coursemodule_from_id('', $fromform->coursemodule, 0, false, MUST_EXIST);
+ $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
+ $fromform->instance = $cm->instance;
+ $fromform->coursemodule = $cm->id;
+ }
+
+ if (!empty($fromform->coursemodule)) {
+ $context = context_module::instance($fromform->coursemodule);
+ } else {
+ $context = context_course::instance($course->id);
+ }
+
+ $fromform->course = $course->id;
+ $fromform->modulename = clean_param($fromform->modulename, PARAM_PLUGIN); // For safety
+
+ $addinstancefunction = $fromform->modulename."_add_instance";
+ $updateinstancefunction = $fromform->modulename."_update_instance";
+
+ if (!isset($fromform->groupingid)) {
+ $fromform->groupingid = 0;
+ }
+
+ if (!isset($fromform->groupmembersonly)) {
+ $fromform->groupmembersonly = 0;
+ }
+
+ if (!isset($fromform->name)) { //label
+ $fromform->name = $fromform->modulename;
+ }
+
+ if (!isset($fromform->completion)) {
+ $fromform->completion = COMPLETION_DISABLED;
+ }
+ if (!isset($fromform->completionview)) {
+ $fromform->completionview = COMPLETION_VIEW_NOT_REQUIRED;
+ }
+
+ // Convert the 'use grade' checkbox into a grade-item number: 0 if
+ // checked, null if not
+ if (isset($fromform->completionusegrade) && $fromform->completionusegrade) {
+ $fromform->completiongradeitemnumber = 0;
+ } else {
+ $fromform->completiongradeitemnumber = null;
+ }
+
+ // the type of event to trigger (mod_created/mod_updated)
+ $eventname = '';
+
+ if (!empty($fromform->update)) {
+
+ if (!empty($course->groupmodeforce) or !isset($fromform->groupmode)) {
+ $fromform->groupmode = $cm->groupmode; // keep original
+ }
+
+ // update course module first
+ $cm->groupmode = $fromform->groupmode;
+ $cm->groupingid = $fromform->groupingid;
+ $cm->groupmembersonly = $fromform->groupmembersonly;
+
+ $completion = new completion_info($course);
+ if ($completion->is_enabled()) {
+ // Update completion settings
+ $cm->completion = $fromform->completion;
+ $cm->completiongradeitemnumber = $fromform->completiongradeitemnumber;
+ $cm->completionview = $fromform->completionview;
+ $cm->completionexpected = $fromform->completionexpected;
+ }
+ if (!empty($CFG->enableavailability)) {
+ $cm->availablefrom = $fromform->availablefrom;
+ $cm->availableuntil = $fromform->availableuntil;
+ $cm->showavailability = $fromform->showavailability;
+ condition_info::update_cm_from_form($cm,$fromform,true);
+ }
+ if (isset($fromform->showdescription)) {
+ $cm->showdescription = $fromform->showdescription;
+ } else {
+ $cm->showdescription = 0;
+ }
+
+ $DB->update_record('course_modules', $cm);
+
+ $modcontext = context_module::instance($fromform->coursemodule);
+
+ // update embedded links and save files
+ if (plugin_supports('mod', $fromform->modulename, FEATURE_MOD_INTRO, true)) {
+ $fromform->intro = file_save_draft_area_files($fromform->introeditor['itemid'], $modcontext->id,
+ 'mod_'.$fromform->modulename, 'intro', 0,
+ array('subdirs'=>true), $fromform->introeditor['text']);
+ $fromform->introformat = $fromform->introeditor['format'];
+ unset($fromform->introeditor);
+ }
+
+ if (!$updateinstancefunction($fromform, $mform)) {
+ print_error('cannotupdatemod', '', course_get_url($course, $cw->section), $fromform->modulename);
+ }
+
+ // make sure visibility is set correctly (in particular in calendar)
+ if (has_capability('moodle/course:activityvisibility', $modcontext)) {
+ set_coursemodule_visible($fromform->coursemodule, $fromform->visible);
+ }
+
+ if (isset($fromform->cmidnumber)) { //label
+ // set cm idnumber - uniqueness is already verified by form validation
+ set_coursemodule_idnumber($fromform->coursemodule, $fromform->cmidnumber);
+ }
+
+ // Now that module is fully updated, also update completion data if
+ // required (this will wipe all user completion data and recalculate it)
+ if ($completion->is_enabled() && !empty($fromform->completionunlocked)) {
+ $completion->reset_all_state($cm);
+ }
+
+ $eventname = 'mod_updated';
+
+ add_to_log($course->id, "course", "update mod",
+ "../mod/$fromform->modulename/view.php?id=$fromform->coursemodule",
+ "$fromform->modulename $fromform->instance");
+ add_to_log($course->id, $fromform->modulename, "update",
+ "view.php?id=$fromform->coursemodule",
+ "$fromform->instance", $fromform->coursemodule);
+
+ } else if (!empty($fromform->add)) {
+
+ if (!empty($course->groupmodeforce) or !isset($fromform->groupmode)) {
+ $fromform->groupmode = 0; // do not set groupmode
+ }
+
+ if (!course_allowed_module($course, $fromform->modulename)) {
+ print_error('moduledisable', '', '', $fromform->modulename);
+ }
+
+ // first add course_module record because we need the context
+ $newcm = new stdClass();
+ $newcm->course = $course->id;
+ $newcm->module = $fromform->module;
+ $newcm->instance = 0; // not known yet, will be updated later (this is similar to restore code)
+ $newcm->visible = $fromform->visible;
+ $newcm->groupmode = $fromform->groupmode;
+ $newcm->groupingid = $fromform->groupingid;
+ $newcm->groupmembersonly = $fromform->groupmembersonly;
+ $completion = new completion_info($course);
+ if ($completion->is_enabled()) {
+ $newcm->completion = $fromform->completion;
+ $newcm->completiongradeitemnumber = $fromform->completiongradeitemnumber;
+ $newcm->completionview = $fromform->completionview;
+ $newcm->completionexpected = $fromform->completionexpected;
+ }
+ if(!empty($CFG->enableavailability)) {
+ $newcm->availablefrom = $fromform->availablefrom;
+ $newcm->availableuntil = $fromform->availableuntil;
+ $newcm->showavailability = $fromform->showavailability;
+ }
+ if (isset($fromform->showdescription)) {
+ $newcm->showdescription = $fromform->showdescription;
+ } else {
+ $newcm->showdescription = 0;
+ }
+
+ if (!$fromform->coursemodule = add_course_module($newcm)) {
+ print_error('cannotaddcoursemodule');
+ }
+
+ if (plugin_supports('mod', $fromform->modulename, FEATURE_MOD_INTRO, true)) {
+ $introeditor = $fromform->introeditor;
+ unset($fromform->introeditor);
+ $fromform->intro = $introeditor['text'];
+ $fromform->introformat = $introeditor['format'];
+ }
+
+ $returnfromfunc = $addinstancefunction($fromform, $mform);
+
+ if (!$returnfromfunc or !is_number($returnfromfunc)) {
+ // undo everything we can
+ $modcontext = context_module::instance($fromform->coursemodule);
+ delete_context(CONTEXT_MODULE, $fromform->coursemodule);
+ $DB->delete_records('course_modules', array('id'=>$fromform->coursemodule));
+
+ if (!is_number($returnfromfunc)) {
+ print_error('invalidfunction', '', course_get_url($course, $cw->section));
+ } else {
+ print_error('cannotaddnewmodule', '', course_get_url($course, $cw->section), $fromform->modulename);
+ }
+ }
+
+ $fromform->instance = $returnfromfunc;
+
+ $DB->set_field('course_modules', 'instance', $returnfromfunc, array('id'=>$fromform->coursemodule));
+
+ // update embedded links and save files
+ $modcontext = context_module::instance($fromform->coursemodule);
+ if (!empty($introeditor)) {
+ $fromform->intro = file_save_draft_area_files($introeditor['itemid'], $modcontext->id,
+ 'mod_'.$fromform->modulename, 'intro', 0,
+ array('subdirs'=>true), $introeditor['text']);
+ $DB->set_field($fromform->modulename, 'intro', $fromform->intro, array('id'=>$fromform->instance));
+ }
+
+ // course_modules and course_sections each contain a reference
+ // to each other, so we have to update one of them twice.
+ $sectionid = course_add_cm_to_section($course, $fromform->coursemodule, $fromform->section);
+
+ // make sure visibility is set correctly (in particular in calendar)
+ // note: allow them to set it even without moodle/course:activityvisibility
+ set_coursemodule_visible($fromform->coursemodule, $fromform->visible);
+ $DB->set_field('course_modules', 'visibleold', 1, array('id' => $fromform->coursemodule));
+
+ if (isset($fromform->cmidnumber)) { //label
+ // set cm idnumber - uniqueness is already verified by form validation
+ set_coursemodule_idnumber($fromform->coursemodule, $fromform->cmidnumber);
+ }
+
+ // Set up conditions
+ if ($CFG->enableavailability) {
+ condition_info::update_cm_from_form((object)array('id'=>$fromform->coursemodule), $fromform, false);
+ }
+
+ $eventname = 'mod_created';
+
+ add_to_log($course->id, "course", "add mod",
+ "../mod/$fromform->modulename/view.php?id=$fromform->coursemodule",
+ "$fromform->modulename $fromform->instance");
+ add_to_log($course->id, $fromform->modulename, "add",
+ "view.php?id=$fromform->coursemodule",
+ "$fromform->instance", $fromform->coursemodule);
+ } else {
+ print_error('invaliddata');
+ }
+
+ // Trigger mod_created/mod_updated event with information about this module.
+ $eventdata = new stdClass();
+ $eventdata->modulename = $fromform->modulename;
+ $eventdata->name = $fromform->name;
+ $eventdata->cmid = $fromform->coursemodule;
+ $eventdata->courseid = $course->id;
+ $eventdata->userid = $USER->id;
+ events_trigger($eventname, $eventdata);
+
+ // sync idnumber with grade_item
+ if ($grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$fromform->modulename,
+ 'iteminstance'=>$fromform->instance, 'itemnumber'=>0, 'courseid'=>$course->id))) {
+ if ($grade_item->idnumber != $fromform->cmidnumber) {
+ $grade_item->idnumber = $fromform->cmidnumber;
+ $grade_item->update();
+ }
+ }
+
+ $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$fromform->modulename,
+ 'iteminstance'=>$fromform->instance, 'courseid'=>$course->id));
+
+ // create parent category if requested and move to correct parent category
+ if ($items and isset($fromform->gradecat)) {
+ if ($fromform->gradecat == -1) {
+ $grade_category = new grade_category();
+ $grade_category->courseid = $course->id;
+ $grade_category->fullname = $fromform->name;
+ $grade_category->insert();
+ if ($grade_item) {
+ $parent = $grade_item->get_parent_category();
+ $grade_category->set_parent($parent->id);
+ }
+ $fromform->gradecat = $grade_category->id;
+ }
+ foreach ($items as $itemid=>$unused) {
+ $items[$itemid]->set_parent($fromform->gradecat);
+ if ($itemid == $grade_item->id) {
+ // use updated grade_item
+ $grade_item = $items[$itemid];
+ }
+ }
+ }
+
+ // add outcomes if requested
+ if ($outcomes = grade_outcome::fetch_all_available($course->id)) {
+ $grade_items = array();
+
+ // Outcome grade_item.itemnumber start at 1000, there is nothing above outcomes
+ $max_itemnumber = 999;
+ if ($items) {
+ foreach($items as $item) {
+ if ($item->itemnumber > $max_itemnumber) {
+ $max_itemnumber = $item->itemnumber;
+ }
+ }
+ }
+
+ foreach($outcomes as $outcome) {
+ $elname = 'outcome_'.$outcome->id;
+
+ if (property_exists($fromform, $elname) and $fromform->$elname) {
+ // so we have a request for new outcome grade item?
+ if ($items) {
+ foreach($items as $item) {
+ if ($item->outcomeid == $outcome->id) {
+ //outcome aready exists
+ continue 2;
+ }
+ }
+ }
+
+ $max_itemnumber++;
+
+ $outcome_item = new grade_item();
+ $outcome_item->courseid = $course->id;
+ $outcome_item->itemtype = 'mod';
+ $outcome_item->itemmodule = $fromform->modulename;
+ $outcome_item->iteminstance = $fromform->instance;
+ $outcome_item->itemnumber = $max_itemnumber;
+ $outcome_item->itemname = $outcome->fullname;
+ $outcome_item->outcomeid = $outcome->id;
+ $outcome_item->gradetype = GRADE_TYPE_SCALE;
+ $outcome_item->scaleid = $outcome->scaleid;
+ $outcome_item->insert();
+
+ // move the new outcome into correct category and fix sortorder if needed
+ if ($grade_item) {
+ $outcome_item->set_parent($grade_item->categoryid);
+ $outcome_item->move_after_sortorder($grade_item->sortorder);
+
+ } else if (isset($fromform->gradecat)) {
+ $outcome_item->set_parent($fromform->gradecat);
+ }
+ }
+ }
+ }
+
+ if (plugin_supports('mod', $fromform->modulename, FEATURE_ADVANCED_GRADING, false)
+ and has_capability('moodle/grade:managegradingforms', $modcontext)) {
+ require_once($CFG->dirroot.'/grade/grading/lib.php');
+ $gradingman = get_grading_manager($modcontext, 'mod_'.$fromform->modulename);
+ $showgradingmanagement = false;
+ foreach ($gradingman->get_available_areas() as $areaname => $aretitle) {
+ $formfield = 'advancedgradingmethod_'.$areaname;
+ if (isset($fromform->{$formfield})) {
+ $gradingman->set_area($areaname);
+ $methodchanged = $gradingman->set_active_method($fromform->{$formfield});
+ if (empty($fromform->{$formfield})) {
+ // going back to the simple direct grading is not a reason
+ // to open the management screen
+ $methodchanged = false;
+ }
+ $showgradingmanagement = $showgradingmanagement || $methodchanged;
+ }
+ }
+ }
+
+ rebuild_course_cache($course->id);
+ grade_regrade_final_grades($course->id);
+ plagiarism_save_form_elements($fromform); //save plagiarism settings
+
+ if (isset($fromform->submitbutton)) {
+ if (empty($showgradingmanagement)) {
+ redirect("$CFG->wwwroot/mod/$module->name/view.php?id=$fromform->coursemodule");
+ } else {
+ $returnurl = new moodle_url("/mod/$module->name/view.php", array('id' => $fromform->coursemodule));
+ redirect($gradingman->get_management_url($returnurl));
+ }
+ } else {
+ redirect(course_get_url($course, $cw->section, array('sr' => $sectionreturn)));
+ }
+ exit;
+
+} else {
+
+ $streditinga = get_string('editinga', 'moodle', $fullmodulename);
+ $strmodulenameplural = get_string('modulenameplural', $module->name);
+
+ if (!empty($cm->id)) {
+ $context = context_module::instance($cm->id);
+ } else {
+ $context = context_course::instance($course->id);
+ }
+
+ $PAGE->set_heading($course->fullname);
+ $PAGE->set_title($streditinga);
+ $PAGE->set_cacheable(false);
+ echo $OUTPUT->header();
+
+ if (get_string_manager()->string_exists('modulename_help', $module->name)) {
+ echo $OUTPUT->heading_with_help($pageheading, 'modulename', $module->name, 'icon');
+ } else {
+ echo $OUTPUT->heading_with_help($pageheading, '', $module->name, 'icon');
+ }
+
+ $mform->display();
+
+ echo $OUTPUT->footer();
+}
--- /dev/null
+<?php
+require_once ($CFG->libdir.'/formslib.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+/**
+ * This class adds extra methods to form wrapper specific to be used for module
+ * add / update forms mod/{modname}/mod_form.php replaced deprecated mod/{modname}/mod.html
+ */
+abstract class moodleform_mod extends moodleform {
+ /** Current data */
+ protected $current;
+ /**
+ * Instance of the module that is being updated. This is the id of the {prefix}{modulename}
+ * record. Can be used in form definition. Will be "" if this is an 'add' form and not an
+ * update one.
+ *
+ * @var mixed
+ */
+ protected $_instance;
+ /**
+ * Section of course that module instance will be put in or is in.
+ * This is always the section number itself (column 'section' from 'course_sections' table).
+ *
+ * @var mixed
+ */
+ protected $_section;
+ /**
+ * Course module record of the module that is being updated. Will be null if this is an 'add' form and not an
+ * update one.
+ *
+ * @var mixed
+ */
+ protected $_cm;
+ /**
+ * List of modform features
+ */
+ protected $_features;
+ /**
+ * @var array Custom completion-rule elements, if enabled
+ */
+ protected $_customcompletionelements;
+ /**
+ * @var string name of module
+ */
+ protected $_modname;
+ /** current context, course or module depends if already exists*/
+ protected $context;
+
+ /** a flag indicating whether outcomes are being used*/
+ protected $_outcomesused;
+
+ function moodleform_mod($current, $section, $cm, $course) {
+ $this->current = $current;
+ $this->_instance = $current->instance;
+ $this->_section = $section;
+ $this->_cm = $cm;
+ if ($this->_cm) {
+ $this->context = context_module::instance($this->_cm->id);
+ } else {
+ $this->context = context_course::instance($course->id);
+ }
+
+ // Guess module name
+ $matches = array();
+ if (!preg_match('/^mod_([^_]+)_mod_form$/', get_class($this), $matches)) {
+ debugging('Use $modname parameter or rename form to mod_xx_mod_form, where xx is name of your module');
+ print_error('unknownmodulename');
+ }
+ $this->_modname = $matches[1];
+ $this->init_features();
+ parent::moodleform('modedit.php');
+ }
+
+ protected function init_features() {
+ global $CFG;
+
+ $this->_features = new stdClass();
+ $this->_features->groups = plugin_supports('mod', $this->_modname, FEATURE_GROUPS, true);
+ $this->_features->groupings = plugin_supports('mod', $this->_modname, FEATURE_GROUPINGS, false);
+ $this->_features->groupmembersonly = (!empty($CFG->enablegroupmembersonly) and plugin_supports('mod', $this->_modname, FEATURE_GROUPMEMBERSONLY, false));
+ $this->_features->outcomes = (!empty($CFG->enableoutcomes) and plugin_supports('mod', $this->_modname, FEATURE_GRADE_OUTCOMES, true));
+ $this->_features->hasgrades = plugin_supports('mod', $this->_modname, FEATURE_GRADE_HAS_GRADE, false);
+ $this->_features->idnumber = plugin_supports('mod', $this->_modname, FEATURE_IDNUMBER, true);
+ $this->_features->introeditor = plugin_supports('mod', $this->_modname, FEATURE_MOD_INTRO, true);
+ $this->_features->defaultcompletion = plugin_supports('mod', $this->_modname, FEATURE_MODEDIT_DEFAULT_COMPLETION, true);
+ $this->_features->rating = plugin_supports('mod', $this->_modname, FEATURE_RATE, false);
+ $this->_features->showdescription = plugin_supports('mod', $this->_modname, FEATURE_SHOW_DESCRIPTION, false);
+
+ $this->_features->gradecat = ($this->_features->outcomes or $this->_features->hasgrades);
+ $this->_features->advancedgrading = plugin_supports('mod', $this->_modname, FEATURE_ADVANCED_GRADING, false);
+ }
+
+ /**
+ * Only available on moodleform_mod.
+ *
+ * @param array $default_values passed by reference
+ */
+ function data_preprocessing(&$default_values){
+ if (empty($default_values['scale'])) {
+ $default_values['assessed'] = 0;
+ }
+
+ if (empty($default_values['assessed'])){
+ $default_values['ratingtime'] = 0;
+ } else {
+ $default_values['ratingtime']=
+ ($default_values['assesstimestart'] && $default_values['assesstimefinish']) ? 1 : 0;
+ }
+ }
+
+ /**
+ * Each module which defines definition_after_data() must call this method using parent::definition_after_data();
+ */
+ function definition_after_data() {
+ global $CFG, $COURSE;
+ $mform =& $this->_form;
+
+ if ($id = $mform->getElementValue('update')) {
+ $modulename = $mform->getElementValue('modulename');
+ $instance = $mform->getElementValue('instance');
+
+ if ($this->_features->gradecat) {
+ $gradecat = false;
+ if (!empty($CFG->enableoutcomes) and $this->_features->outcomes) {
+ $outcomes = grade_outcome::fetch_all_available($COURSE->id);
+ if (!empty($outcomes)) {
+ $gradecat = true;
+ }
+ }
+
+ $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$modulename,'iteminstance'=>$instance, 'courseid'=>$COURSE->id));
+ //will be no items if, for example, this activity supports ratings but rating aggregate type == no ratings
+ if (!empty($items)) {
+ foreach ($items as $item) {
+ if (!empty($item->outcomeid)) {
+ $elname = 'outcome_'.$item->outcomeid;
+ if ($mform->elementExists($elname)) {
+ $mform->hardFreeze($elname); // prevent removing of existing outcomes
+ }
+ }
+ }
+
+ foreach ($items as $item) {
+ if (is_bool($gradecat)) {
+ $gradecat = $item->categoryid;
+ continue;
+ }
+ if ($gradecat != $item->categoryid) {
+ //mixed categories
+ $gradecat = false;
+ break;
+ }
+ }
+ }
+
+ if ($gradecat === false) {
+ // items and outcomes in different categories - remove the option
+ // TODO: add a "Mixed categories" text instead of removing elements with no explanation
+ if ($mform->elementExists('gradecat')) {
+ $mform->removeElement('gradecat');
+ if ($this->_features->rating) {
+ //if supports ratings then the max grade dropdown wasnt added so the grade box can be removed entirely
+ $mform->removeElement('modstandardgrade');
+ }
+ }
+ }
+ }
+ }
+
+ if ($COURSE->groupmodeforce) {
+ if ($mform->elementExists('groupmode')) {
+ $mform->hardFreeze('groupmode'); // groupmode can not be changed if forced from course settings
+ }
+ }
+
+ // Don't disable/remove groupingid if it is currently set to something,
+ // otherwise you cannot turn it off at same time as turning off other
+ // option (MDL-30764)
+ if (empty($this->_cm) || !$this->_cm->groupingid) {
+ if ($mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly') and empty($COURSE->groupmodeforce)) {
+ $mform->disabledIf('groupingid', 'groupmode', 'eq', NOGROUPS);
+
+ } else if (!$mform->elementExists('groupmode') and $mform->elementExists('groupmembersonly')) {
+ $mform->disabledIf('groupingid', 'groupmembersonly', 'notchecked');
+
+ } else if (!$mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly')) {
+ // groupings have no use without groupmode or groupmembersonly
+ if ($mform->elementExists('groupingid')) {
+ $mform->removeElement('groupingid');
+ }
+ }
+ }
+
+ // Completion: If necessary, freeze fields
+ $completion = new completion_info($COURSE);
+ if ($completion->is_enabled()) {
+ // If anybody has completed the activity, these options will be 'locked'
+ $completedcount = empty($this->_cm)
+ ? 0
+ : $completion->count_user_data($this->_cm);
+
+ $freeze = false;
+ if (!$completedcount) {
+ if ($mform->elementExists('unlockcompletion')) {
+ $mform->removeElement('unlockcompletion');
+ }
+ // Automatically set to unlocked (note: this is necessary
+ // in order to make it recalculate completion once the option
+ // is changed, maybe someone has completed it now)
+ $mform->getElement('completionunlocked')->setValue(1);
+ } else {
+ // Has the element been unlocked?
+ if ($mform->exportValue('unlockcompletion')) {
+ // Yes, add in warning text and set the hidden variable
+ $mform->insertElementBefore(
+ $mform->createElement('static', 'completedunlocked',
+ get_string('completedunlocked', 'completion'),
+ get_string('completedunlockedtext', 'completion')),
+ 'unlockcompletion');
+ $mform->removeElement('unlockcompletion');
+ $mform->getElement('completionunlocked')->setValue(1);
+ } else {
+ // No, add in the warning text with the count (now we know
+ // it) before the unlock button
+ $mform->insertElementBefore(
+ $mform->createElement('static', 'completedwarning',
+ get_string('completedwarning', 'completion'),
+ get_string('completedwarningtext', 'completion', $completedcount)),
+ 'unlockcompletion');
+ $freeze = true;
+ }
+ }
+
+ if ($freeze) {
+ $mform->freeze('completion');
+ if ($mform->elementExists('completionview')) {
+ $mform->freeze('completionview'); // don't use hardFreeze or checkbox value gets lost
+ }
+ if ($mform->elementExists('completionusegrade')) {
+ $mform->freeze('completionusegrade');
+ }
+ $mform->freeze($this->_customcompletionelements);
+ }
+ }
+
+ // Availability conditions
+ if (!empty($CFG->enableavailability) && $this->_cm) {
+ $ci = new condition_info($this->_cm);
+ $fullcm=$ci->get_full_course_module();
+
+ $num=0;
+ foreach($fullcm->conditionsgrade as $gradeitemid=>$minmax) {
+ $groupelements=$mform->getElement('conditiongradegroup['.$num.']')->getElements();
+ $groupelements[0]->setValue($gradeitemid);
+ $groupelements[2]->setValue(is_null($minmax->min) ? '' :
+ format_float($minmax->min, 5, true, true));
+ $groupelements[4]->setValue(is_null($minmax->max) ? '' :
+ format_float($minmax->max, 5, true, true));
+ $num++;
+ }
+
+ $num = 0;
+ foreach($fullcm->conditionsfield as $field => $details) {
+ $groupelements = $mform->getElement('conditionfieldgroup['.$num.']')->getElements();
+ $groupelements[0]->setValue($field);
+ $groupelements[1]->setValue(is_null($details->operator) ? '' : $details->operator);
+ $groupelements[2]->setValue(is_null($details->value) ? '' : $details->value);
+ $num++;
+ }
+
+ if ($completion->is_enabled()) {
+ $num=0;
+ foreach($fullcm->conditionscompletion as $othercmid=>$state) {
+ $groupelements=$mform->getElement('conditioncompletiongroup['.$num.']')->getElements();
+ $groupelements[0]->setValue($othercmid);
+ $groupelements[1]->setValue($state);
+ $num++;
+ }
+ }
+ }
+ }
+
+ // form verification
+ function validation($data, $files) {
+ global $COURSE, $DB;
+ $errors = parent::validation($data, $files);
+
+ $mform =& $this->_form;
+
+ $errors = array();
+
+ if ($mform->elementExists('name')) {
+ $name = trim($data['name']);
+ if ($name == '') {
+ $errors['name'] = get_string('required');
+ }
+ }
+
+ $grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$data['modulename'],
+ 'iteminstance'=>$data['instance'], 'itemnumber'=>0, 'courseid'=>$COURSE->id));
+ if ($data['coursemodule']) {
+ $cm = $DB->get_record('course_modules', array('id'=>$data['coursemodule']));
+ } else {
+ $cm = null;
+ }
+
+ if ($mform->elementExists('cmidnumber')) {
+ // verify the idnumber
+ if (!grade_verify_idnumber($data['cmidnumber'], $COURSE->id, $grade_item, $cm)) {
+ $errors['cmidnumber'] = get_string('idnumbertaken');
+ }
+ }
+
+ // Completion: Don't let them choose automatic completion without turning
+ // on some conditions
+ if (array_key_exists('completion', $data) && $data['completion']==COMPLETION_TRACKING_AUTOMATIC) {
+ if (empty($data['completionview']) && empty($data['completionusegrade']) &&
+ !$this->completion_rule_enabled($data)) {
+ $errors['completion'] = get_string('badautocompletion', 'completion');
+ }
+ }
+
+ // Conditions: Don't let them set dates which make no sense
+ if (array_key_exists('availablefrom', $data) &&
+ $data['availablefrom'] && $data['availableuntil'] &&
+ $data['availablefrom'] >= $data['availableuntil']) {
+ $errors['availablefrom'] = get_string('badavailabledates', 'condition');
+ }
+
+ // Conditions: Verify that the grade conditions are numbers, and make sense.
+ if (array_key_exists('conditiongradegroup', $data)) {
+ foreach ($data['conditiongradegroup'] as $i => $gradedata) {
+ if ($gradedata['conditiongrademin'] !== '' &&
+ !is_numeric(unformat_float($gradedata['conditiongrademin']))) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradesmustbenumeric', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademax'] !== '' &&
+ !is_numeric(unformat_float($gradedata['conditiongrademax']))) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradesmustbenumeric', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademin'] !== '' && $gradedata['conditiongrademax'] !== '' &&
+ unformat_float($gradedata['conditiongrademax']) <= unformat_float($gradedata['conditiongrademin'])) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('badgradelimits', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademin'] === '' && $gradedata['conditiongrademax'] === '' &&
+ $gradedata['conditiongradeitemid']) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradeitembutnolimits', 'condition');
+ continue;
+ }
+ if (($gradedata['conditiongrademin'] !== '' || $gradedata['conditiongrademax'] !== '') &&
+ !$gradedata['conditiongradeitemid']) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradelimitsbutnoitem', 'condition');
+ continue;
+ }
+ }
+ }
+
+ // Conditions: Verify that the user profile field has not been declared more than once
+ if (array_key_exists('conditionfieldgroup', $data)) {
+ // Array to store the existing fields
+ $arrcurrentfields = array();
+ // Error message displayed if any condition is declared more than once. We use lang string because
+ // this way we don't actually generate the string unless there is an error.
+ $stralreadydeclaredwarning = new lang_string('fielddeclaredmultipletimes', 'condition');
+ foreach ($data['conditionfieldgroup'] as $i => $fielddata) {
+ if ($fielddata['conditionfield'] == 0) { // Don't need to bother if none is selected
+ continue;
+ }
+ if (in_array($fielddata['conditionfield'], $arrcurrentfields)) {
+ $errors["conditionfieldgroup[{$i}]"] = $stralreadydeclaredwarning->out();
+ }
+ // Add the field to the array
+ $arrcurrentfields[] = $fielddata['conditionfield'];
+ }
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Load in existing data as form defaults. Usually new entry defaults are stored directly in
+ * form definition (new entry form); this function is used to load in data where values
+ * already exist and data is being edited (edit entry form).
+ *
+ * @param mixed $default_values object or array of default values
+ */
+ function set_data($default_values) {
+ if (is_object($default_values)) {
+ $default_values = (array)$default_values;
+ }
+
+ $this->data_preprocessing($default_values);
+ parent::set_data($default_values);
+ }
+
+ /**
+ * Adds all the standard elements to a form to edit the settings for an activity module.
+ */
+ function standard_coursemodule_elements(){
+ global $COURSE, $CFG, $DB;
+ $mform =& $this->_form;
+
+ $this->_outcomesused = false;
+ if ($this->_features->outcomes) {
+ if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) {
+ $this->_outcomesused = true;
+ $mform->addElement('header', 'modoutcomes', get_string('outcomes', 'grades'));
+ foreach($outcomes as $outcome) {
+ $mform->addElement('advcheckbox', 'outcome_'.$outcome->id, $outcome->get_name());
+ }
+ }
+ }
+
+
+ if ($this->_features->rating) {
+ require_once($CFG->dirroot.'/rating/lib.php');
+ $rm = new rating_manager();
+
+ $mform->addElement('header', 'modstandardratings', get_string('ratings', 'rating'));
+
+ $permission=CAP_ALLOW;
+ $rolenamestring = null;
+ if (!empty($this->_cm)) {
+ $context = context_module::instance($this->_cm->id);
+
+ $rolenames = get_role_names_with_caps_in_context($context, array('moodle/rating:rate', 'mod/'.$this->_cm->modname.':rate'));
+ $rolenamestring = implode(', ', $rolenames);
+ } else {
+ $rolenamestring = get_string('capabilitychecknotavailable','rating');
+ }
+ $mform->addElement('static', 'rolewarning', get_string('rolewarning','rating'), $rolenamestring);
+ $mform->addHelpButton('rolewarning', 'rolewarning', 'rating');
+
+ $mform->addElement('select', 'assessed', get_string('aggregatetype', 'rating') , $rm->get_aggregate_types());
+ $mform->setDefault('assessed', 0);
+ $mform->addHelpButton('assessed', 'aggregatetype', 'rating');
+
+ $mform->addElement('modgrade', 'scale', get_string('scale'), false);
+ $mform->disabledIf('scale', 'assessed', 'eq', 0);
+
+ $mform->addElement('checkbox', 'ratingtime', get_string('ratingtime', 'rating'));
+ $mform->disabledIf('ratingtime', 'assessed', 'eq', 0);
+
+ $mform->addElement('date_time_selector', 'assesstimestart', get_string('from'));
+ $mform->disabledIf('assesstimestart', 'assessed', 'eq', 0);
+ $mform->disabledIf('assesstimestart', 'ratingtime');
+
+ $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to'));
+ $mform->disabledIf('assesstimefinish', 'assessed', 'eq', 0);
+ $mform->disabledIf('assesstimefinish', 'ratingtime');
+ }
+
+ //doing this here means splitting up the grade related settings on the lesson settings page
+ //$this->standard_grading_coursemodule_elements();
+
+ $mform->addElement('header', 'modstandardelshdr', get_string('modstandardels', 'form'));
+ if ($this->_features->groups) {
+ $options = array(NOGROUPS => get_string('groupsnone'),
+ SEPARATEGROUPS => get_string('groupsseparate'),
+ VISIBLEGROUPS => get_string('groupsvisible'));
+ $mform->addElement('select', 'groupmode', get_string('groupmode', 'group'), $options, NOGROUPS);
+ $mform->addHelpButton('groupmode', 'groupmode', 'group');
+ }
+
+ if ($this->_features->groupings or $this->_features->groupmembersonly) {
+ //groupings selector - used for normal grouping mode or also when restricting access with groupmembersonly
+ $options = array();
+ $options[0] = get_string('none');
+ if ($groupings = $DB->get_records('groupings', array('courseid'=>$COURSE->id))) {
+ foreach ($groupings as $grouping) {
+ $options[$grouping->id] = format_string($grouping->name);
+ }
+ }
+ $mform->addElement('select', 'groupingid', get_string('grouping', 'group'), $options);
+ $mform->addHelpButton('groupingid', 'grouping', 'group');
+ $mform->setAdvanced('groupingid');
+ }
+
+ if ($this->_features->groupmembersonly) {
+ $mform->addElement('checkbox', 'groupmembersonly', get_string('groupmembersonly', 'group'));
+ $mform->addHelpButton('groupmembersonly', 'groupmembersonly', 'group');
+ $mform->setAdvanced('groupmembersonly');
+ }
+
+ $mform->addElement('modvisible', 'visible', get_string('visible'));
+ if (!empty($this->_cm)) {
+ $context = context_module::instance($this->_cm->id);
+ if (!has_capability('moodle/course:activityvisibility', $context)) {
+ $mform->hardFreeze('visible');
+ }
+ }
+
+ if ($this->_features->idnumber) {
+ $mform->addElement('text', 'cmidnumber', get_string('idnumbermod'));
+ $mform->addHelpButton('cmidnumber', 'idnumbermod');
+ }
+
+ if (!empty($CFG->enableavailability)) {
+ // String used by conditions
+ $strnone = get_string('none','condition');
+ // Conditional availability
+
+ // Available from/to defaults to midnight because then the display
+ // will be nicer where it tells users when they can access it (it
+ // shows only the date and not time).
+ $date = usergetdate(time());
+ $midnight = make_timestamp($date['year'], $date['mon'], $date['mday']);
+
+ // From/until controls
+ $mform->addElement('header', 'availabilityconditionsheader',
+ get_string('availabilityconditions', 'condition'));
+ $mform->addElement('date_time_selector', 'availablefrom',
+ get_string('availablefrom', 'condition'),
+ array('optional' => true, 'defaulttime' => $midnight));
+ $mform->addHelpButton('availablefrom', 'availablefrom', 'condition');
+ $mform->addElement('date_time_selector', 'availableuntil',
+ get_string('availableuntil', 'condition'),
+ array('optional' => true, 'defaulttime' => $midnight));
+
+ // Conditions based on grades
+ $gradeoptions = array();
+ $items = grade_item::fetch_all(array('courseid'=>$COURSE->id));
+ $items = $items ? $items : array();
+ foreach($items as $id=>$item) {
+ // Do not include grades for current item
+ if (!empty($this->_cm) && $this->_cm->instance == $item->iteminstance
+ && $this->_cm->modname == $item->itemmodule
+ && $item->itemtype == 'mod') {
+ continue;
+ }
+ $gradeoptions[$id] = $item->get_name();
+ }
+ asort($gradeoptions);
+ $gradeoptions = array(0 => $strnone) + $gradeoptions;
+
+ $grouparray = array();
+ $grouparray[] =& $mform->createElement('select','conditiongradeitemid','',$gradeoptions);
+ $grouparray[] =& $mform->createElement('static', '', '',' '.get_string('grade_atleast','condition').' ');
+ $grouparray[] =& $mform->createElement('text', 'conditiongrademin','',array('size'=>3));
+ $grouparray[] =& $mform->createElement('static', '', '','% '.get_string('grade_upto','condition').' ');
+ $grouparray[] =& $mform->createElement('text', 'conditiongrademax','',array('size'=>3));
+ $grouparray[] =& $mform->createElement('static', '', '','%');
+ $group = $mform->createElement('group','conditiongradegroup',
+ get_string('gradecondition', 'condition'),$grouparray);
+
+ // Get version with condition info and store it so we don't ask
+ // twice
+ if(!empty($this->_cm)) {
+ $ci = new condition_info($this->_cm, CONDITION_MISSING_EXTRATABLE);
+ $this->_cm = $ci->get_full_course_module();
+ $count = count($this->_cm->conditionsgrade)+1;
+ $fieldcount = count($this->_cm->conditionsfield) + 1;
+ } else {
+ $count = 1;
+ $fieldcount = 1;
+ }
+
+ $this->repeat_elements(array($group), $count, array(), 'conditiongraderepeats', 'conditiongradeadds', 2,
+ get_string('addgrades', 'condition'), true);
+ $mform->addHelpButton('conditiongradegroup[0]', 'gradecondition', 'condition');
+
+ // Conditions based on user fields
+ $operators = condition_info::get_condition_user_field_operators();
+ $useroptions = condition_info::get_condition_user_fields();
+ asort($useroptions);
+
+ $useroptions = array(0 => $strnone) + $useroptions;
+ $grouparray = array();
+ $grouparray[] =& $mform->createElement('select', 'conditionfield', '', $useroptions);
+ $grouparray[] =& $mform->createElement('select', 'conditionfieldoperator', '', $operators);
+ $grouparray[] =& $mform->createElement('text', 'conditionfieldvalue');
+ $mform->setType('conditionfieldvalue', PARAM_RAW);
+ $group = $mform->createElement('group', 'conditionfieldgroup', get_string('userfield', 'condition'), $grouparray);
+
+ $this->repeat_elements(array($group), $fieldcount, array(), 'conditionfieldrepeats', 'conditionfieldadds', 2,
+ get_string('adduserfields', 'condition'), true);
+ $mform->addHelpButton('conditionfieldgroup[0]', 'userfield', 'condition');
+
+ // Conditions based on completion
+ $completion = new completion_info($COURSE);
+ if ($completion->is_enabled()) {
+ $completionoptions = array();
+ $modinfo = get_fast_modinfo($COURSE);
+ foreach($modinfo->cms as $id=>$cm) {
+ // Add each course-module if it:
+ // (a) has completion turned on
+ // (b) is not the same as current course-module
+ if ($cm->completion && (empty($this->_cm) || $this->_cm->id != $id)) {
+ $completionoptions[$id]=$cm->name;
+ }
+ }
+ asort($completionoptions);
+ $completionoptions = array(0 => $strnone) + $completionoptions;
+
+ $completionvalues=array(
+ COMPLETION_COMPLETE=>get_string('completion_complete','condition'),
+ COMPLETION_INCOMPLETE=>get_string('completion_incomplete','condition'),
+ COMPLETION_COMPLETE_PASS=>get_string('completion_pass','condition'),
+ COMPLETION_COMPLETE_FAIL=>get_string('completion_fail','condition'));
+
+ $grouparray = array();
+ $grouparray[] =& $mform->createElement('select','conditionsourcecmid','',$completionoptions);
+ $grouparray[] =& $mform->createElement('select','conditionrequiredcompletion','',$completionvalues);
+ $group = $mform->createElement('group','conditioncompletiongroup',
+ get_string('completioncondition', 'condition'),$grouparray);
+
+ $count = empty($this->_cm) ? 1 : count($this->_cm->conditionscompletion)+1;
+ $this->repeat_elements(array($group),$count,array(),
+ 'conditioncompletionrepeats','conditioncompletionadds',2,
+ get_string('addcompletions','condition'),true);
+ $mform->addHelpButton('conditioncompletiongroup[0]', 'completioncondition', 'condition');
+ }
+
+ // Do we display availability info to students?
+ $mform->addElement('select', 'showavailability', get_string('showavailability', 'condition'),
+ array(CONDITION_STUDENTVIEW_SHOW=>get_string('showavailability_show', 'condition'),
+ CONDITION_STUDENTVIEW_HIDE=>get_string('showavailability_hide', 'condition')));
+ $mform->setDefault('showavailability', CONDITION_STUDENTVIEW_SHOW);
+ }
+
+ // Conditional activities: completion tracking section
+ if(!isset($completion)) {
+ $completion = new completion_info($COURSE);
+ }
+ if ($completion->is_enabled()) {
+ $mform->addElement('header', 'activitycompletionheader', get_string('activitycompletion', 'completion'));
+
+ // Unlock button for if people have completed it (will
+ // be removed in definition_after_data if they haven't)
+ $mform->addElement('submit', 'unlockcompletion', get_string('unlockcompletion', 'completion'));
+ $mform->registerNoSubmitButton('unlockcompletion');
+ $mform->addElement('hidden', 'completionunlocked', 0);
+ $mform->setType('completionunlocked', PARAM_INT);
+
+ $mform->addElement('select', 'completion', get_string('completion', 'completion'),
+ array(COMPLETION_TRACKING_NONE=>get_string('completion_none', 'completion'),
+ COMPLETION_TRACKING_MANUAL=>get_string('completion_manual', 'completion')));
+ $mform->setDefault('completion', $this->_features->defaultcompletion
+ ? COMPLETION_TRACKING_MANUAL
+ : COMPLETION_TRACKING_NONE);
+ $mform->addHelpButton('completion', 'completion', 'completion');
+
+ // Automatic completion once you view it
+ $gotcompletionoptions = false;
+ if (plugin_supports('mod', $this->_modname, FEATURE_COMPLETION_TRACKS_VIEWS, false)) {
+ $mform->addElement('checkbox', 'completionview', get_string('completionview', 'completion'),
+ get_string('completionview_desc', 'completion'));
+ $mform->disabledIf('completionview', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ $gotcompletionoptions = true;
+ }
+
+ // Automatic completion once it's graded
+ if (plugin_supports('mod', $this->_modname, FEATURE_GRADE_HAS_GRADE, false)) {
+ $mform->addElement('checkbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
+ get_string('completionusegrade_desc', 'completion'));
+ $mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ $mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
+ $gotcompletionoptions = true;
+ }
+
+ // Automatic completion according to module-specific rules
+ $this->_customcompletionelements = $this->add_completion_rules();
+ foreach ($this->_customcompletionelements as $element) {
+ $mform->disabledIf($element, 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ }
+
+ $gotcompletionoptions = $gotcompletionoptions ||
+ count($this->_customcompletionelements)>0;
+
+ // Automatic option only appears if possible
+ if ($gotcompletionoptions) {
+ $mform->getElement('completion')->addOption(
+ get_string('completion_automatic', 'completion'),
+ COMPLETION_TRACKING_AUTOMATIC);
+ }
+
+ // Completion expected at particular date? (For progress tracking)
+ $mform->addElement('date_selector', 'completionexpected', get_string('completionexpected', 'completion'), array('optional'=>true));
+ $mform->addHelpButton('completionexpected', 'completionexpected', 'completion');
+ $mform->disabledIf('completionexpected', 'completion', 'eq', COMPLETION_TRACKING_NONE);
+ }
+
+ $this->standard_hidden_coursemodule_elements();
+ }
+
+ /**
+ * Can be overridden to add custom completion rules if the module wishes
+ * them. If overriding this, you should also override completion_rule_enabled.
+ * <p>
+ * Just add elements to the form as needed and return the list of IDs. The
+ * system will call disabledIf and handle other behaviour for each returned
+ * ID.
+ * @return array Array of string IDs of added items, empty array if none
+ */
+ function add_completion_rules() {
+ return array();
+ }
+
+ /**
+ * Called during validation. Override to indicate, based on the data, whether
+ * a custom completion rule is enabled (selected).
+ *
+ * @param array $data Input data (not yet validated)
+ * @return bool True if one or more rules is enabled, false if none are;
+ * default returns false
+ */
+ function completion_rule_enabled($data) {
+ return false;
+ }
+
+ function standard_hidden_coursemodule_elements(){
+ $mform =& $this->_form;
+ $mform->addElement('hidden', 'course', 0);
+ $mform->setType('course', PARAM_INT);
+
+ $mform->addElement('hidden', 'coursemodule', 0);
+ $mform->setType('coursemodule', PARAM_INT);
+
+ $mform->addElement('hidden', 'section', 0);
+ $mform->setType('section', PARAM_INT);
+
+ $mform->addElement('hidden', 'module', 0);
+ $mform->setType('module', PARAM_INT);
+
+ $mform->addElement('hidden', 'modulename', '');
+ $mform->setType('modulename', PARAM_PLUGIN);
+
+ $mform->addElement('hidden', 'instance', 0);
+ $mform->setType('instance', PARAM_INT);
+
+ $mform->addElement('hidden', 'add', 0);
+ $mform->setType('add', PARAM_ALPHA);
+
+ $mform->addElement('hidden', 'update', 0);
+ $mform->setType('update', PARAM_INT);
+
+ $mform->addElement('hidden', 'return', 0);
+ $mform->setType('return', PARAM_BOOL);
+
+ $mform->addElement('hidden', 'sr', 0);
+ $mform->setType('sr', PARAM_INT);
+ }
+
+ public function standard_grading_coursemodule_elements() {
+ global $COURSE, $CFG;
+ $mform =& $this->_form;
+
+ if ($this->_features->hasgrades) {
+
+ if (!$this->_features->rating || $this->_features->gradecat) {
+ $mform->addElement('header', 'modstandardgrade', get_string('grade'));
+ }
+
+ //if supports grades and grades arent being handled via ratings
+ if (!$this->_features->rating) {
+ $mform->addElement('modgrade', 'grade', get_string('grade'));
+ $mform->setDefault('grade', 100);
+ }
+
+ if ($this->_features->advancedgrading
+ and !empty($this->current->_advancedgradingdata['methods'])
+ and !empty($this->current->_advancedgradingdata['areas'])) {
+
+ if (count($this->current->_advancedgradingdata['areas']) == 1) {
+ // if there is just one gradable area (most cases), display just the selector
+ // without its name to make UI simplier
+ $areadata = reset($this->current->_advancedgradingdata['areas']);
+ $areaname = key($this->current->_advancedgradingdata['areas']);
+ $mform->addElement('select', 'advancedgradingmethod_'.$areaname,
+ get_string('gradingmethod', 'core_grading'), $this->current->_advancedgradingdata['methods']);
+ $mform->addHelpButton('advancedgradingmethod_'.$areaname, 'gradingmethod', 'core_grading');
+
+ } else {
+ // the module defines multiple gradable areas, display a selector
+ // for each of them together with a name of the area
+ $areasgroup = array();
+ foreach ($this->current->_advancedgradingdata['areas'] as $areaname => $areadata) {
+ $areasgroup[] = $mform->createElement('select', 'advancedgradingmethod_'.$areaname,
+ $areadata['title'], $this->current->_advancedgradingdata['methods']);
+ $areasgroup[] = $mform->createElement('static', 'advancedgradingareaname_'.$areaname, '', $areadata['title']);
+ }
+ $mform->addGroup($areasgroup, 'advancedgradingmethodsgroup', get_string('gradingmethods', 'core_grading'),
+ array(' ', '<br />'), false);
+ }
+ }
+
+ if ($this->_features->gradecat) {
+ $mform->addElement('select', 'gradecat',
+ get_string('gradecategoryonmodform', 'grades'),
+ grade_get_categories_menu($COURSE->id, $this->_outcomesused));
+ $mform->addHelpButton('gradecat', 'gradecategoryonmodform', 'grades');
+ }
+ }
+ }
+
+ function add_intro_editor($required=false, $customlabel=null) {
+ if (!$this->_features->introeditor) {
+ // intro editor not supported in this module
+ return;
+ }
+
+ $mform = $this->_form;
+ $label = is_null($customlabel) ? get_string('moduleintro') : $customlabel;
+
+ $mform->addElement('editor', 'introeditor', $label, null, array('maxfiles'=>EDITOR_UNLIMITED_FILES, 'noclean'=>true, 'context'=>$this->context));
+ $mform->setType('introeditor', PARAM_RAW); // no XSS prevention here, users must be trusted
+ if ($required) {
+ $mform->addRule('introeditor', get_string('required'), 'required', null, 'client');
+ }
+
+ // If the 'show description' feature is enabled, this checkbox appears
+ // below the intro.
+ if ($this->_features->showdescription) {
+ $mform->addElement('checkbox', 'showdescription', get_string('showdescription'));
+ $mform->addHelpButton('showdescription', 'showdescription');
+ }
+ }
+
+ /**
+ * Overriding formslib's add_action_buttons() method, to add an extra submit "save changes and return" button.
+ *
+ * @param bool $cancel show cancel button
+ * @param string $submitlabel null means default, false means none, string is label text
+ * @param string $submit2label null means default, false means none, string is label text
+ * @return void
+ */
+ function add_action_buttons($cancel=true, $submitlabel=null, $submit2label=null) {
+ if (is_null($submitlabel)) {
+ $submitlabel = get_string('savechangesanddisplay');
+ }
+
+ if (is_null($submit2label)) {
+ $submit2label = get_string('savechangesandreturntocourse');
+ }
+
+ $mform = $this->_form;
+
+ // elements in a row need a group
+ $buttonarray = array();
+
+ if ($submit2label !== false) {
+ $buttonarray[] = &$mform->createElement('submit', 'submitbutton2', $submit2label);
+ }
+
+ if ($submitlabel !== false) {
+ $buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel);
+ }
+
+ if ($cancel) {
+ $buttonarray[] = &$mform->createElement('cancel');
+ }
+
+ $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
+ $mform->setType('buttonar', PARAM_RAW);
+ $mform->closeHeaderBefore('buttonar');
+ }
+}
+
+
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.org //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * Allow the administrator to look through a list of course requests and approve or reject them.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package course
+ */
+
+require_once(dirname(__FILE__) . '/../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/course/lib.php');
+require_once($CFG->dirroot . '/course/request_form.php');
+
+require_login();
+require_capability('moodle/site:approvecourse', context_system::instance());
+
+$approve = optional_param('approve', 0, PARAM_INT);
+$reject = optional_param('reject', 0, PARAM_INT);
+
+$baseurl = $CFG->wwwroot . '/course/pending.php';
+admin_externalpage_setup('coursespending');
+
+/// Process approval of a course.
+if (!empty($approve) and confirm_sesskey()) {
+ /// Load the request.
+ $course = new course_request($approve);
+ $courseid = $course->approve();
+
+ if ($courseid !== false) {
+ redirect($CFG->wwwroot.'/course/edit.php?id=' . $courseid);
+ } else {
+ print_error('courseapprovedfailed');
+ }
+}
+
+/// Process rejection of a course.
+if (!empty($reject)) {
+ // Load the request.
+ $course = new course_request($reject);
+
+ // Prepare the form.
+ $rejectform = new reject_request_form($baseurl);
+ $default = new stdClass();
+ $default->reject = $course->id;
+ $rejectform->set_data($default);
+
+/// Standard form processing if statement.
+ if ($rejectform->is_cancelled()){
+ redirect($baseurl);
+
+ } else if ($data = $rejectform->get_data()) {
+
+ /// Reject the request
+ $course->reject($data->rejectnotice);
+
+ /// Redirect back to the course listing.
+ redirect($baseurl, get_string('courserejected'));
+ }
+
+/// Display the form for giving a reason for rejecting the request.
+ echo $OUTPUT->header($rejectform->focus());
+ $rejectform->display();
+ echo $OUTPUT->footer();
+ exit;
+}
+
+/// Print a list of all the pending requests.
+echo $OUTPUT->header();
+
+$pending = $DB->get_records('course_request');
+if (empty($pending)) {
+ echo $OUTPUT->heading(get_string('nopendingcourses'));
+} else {
+ echo $OUTPUT->heading(get_string('coursespending'));
+
+/// Build a table of all the requests.
+ $table = new html_table();
+ $table->attributes['class'] = 'pendingcourserequests generaltable';
+ $table->align = array('center', 'center', 'center', 'center', 'center', 'center');
+ $table->head = array(get_string('shortnamecourse'), get_string('fullnamecourse'), get_string('requestedby'),
+ get_string('summary'), get_string('category'), get_string('requestreason'), get_string('action'));
+
+ foreach ($pending as $course) {
+ $course = new course_request($course);
+
+ // Check here for shortname collisions and warn about them.
+ $course->check_shortname_collision();
+
+ // Retreiving category name.
+ // If the category was not set (can happen after upgrade) or if the user does not have the capability
+ // to change the category, we fallback on the default one.
+ // Else, the category proposed is fetched, but we fallback on the default one if we can't find it.
+ // It is just a matter of displaying the right information because the logic when approving the category
+ // proceeds the same way. The system context level is used as moodle/site:approvecourse uses it.
+ if (empty($course->category) || !has_capability('moodle/course:changecategory', context_system::instance()) ||
+ (!$category = get_course_category($course->category))) {
+ $category = get_course_category($CFG->defaultrequestcategory);
+ }
+
+ $row = array();
+ $row[] = format_string($course->shortname);
+ $row[] = format_string($course->fullname);
+ $row[] = fullname($course->get_requester());
+ $row[] = $course->summary;
+ $row[] = format_string($category->name);
+ $row[] = format_string($course->reason);
+ $row[] = $OUTPUT->single_button(new moodle_url($baseurl, array('approve' => $course->id, 'sesskey' => sesskey())), get_string('approve'), 'get') .
+ $OUTPUT->single_button(new moodle_url($baseurl, array('reject' => $course->id)), get_string('rejectdots'), 'get');
+
+ /// Add the row to the table.
+ $table->data[] = $row;
+ }
+
+/// Display the table.
+ echo html_writer::table($table);
+
+/// Message about name collisions, if necessary.
+ if (!empty($collision)) {
+ print_string('shortnamecollisionwarning');
+ }
+}
+
+/// Finish off the page.
+echo $OUTPUT->single_button($CFG->wwwroot . '/course/index.php', get_string('backtocourselisting'));
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// This file is part of Moodle - http://moodle.org/ //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// //
+// 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/>. //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/*
+ * @package course
+ * @subpackage publish
+ * @author Jerome Mouneyrac <jerome@mouneyrac.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
+ *
+ * This page display the publication backup form
+ */
+
+require_once('../../config.php');
+require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php');
+require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
+require_once($CFG->dirroot . '/course/publish/lib.php');
+require_once($CFG->libdir . '/filelib.php');
+
+
+//retrieve initial page parameters
+$id = required_param('id', PARAM_INT);
+$hubcourseid = required_param('hubcourseid', PARAM_INT);
+$huburl = required_param('huburl', PARAM_URL);
+$hubname = optional_param('hubname', '', PARAM_TEXT);
+
+//some permissions and parameters checking
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+require_login($course);
+if (!has_capability('moodle/course:publish', context_course::instance($id))
+ or !confirm_sesskey()) {
+ throw new moodle_exception('nopermission');
+}
+
+//page settings
+$PAGE->set_url('/course/publish/backup.php');
+$PAGE->set_pagelayout('course');
+$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+$PAGE->set_heading($course->fullname);
+
+//BEGIN backup processing
+$backupid = optional_param('backup', false, PARAM_ALPHANUM);
+if (!($bc = backup_ui::load_controller($backupid))) {
+ $bc = new backup_controller(backup::TYPE_1COURSE, $id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_YES, backup::MODE_HUB, $USER->id);
+}
+$backup = new backup_ui($bc,
+ array('id' => $id, 'hubcourseid' => $hubcourseid, 'huburl' => $huburl, 'hubname' => $hubname));
+$backup->process();
+if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
+ $backup->execute();
+} else {
+ $backup->save_controller();
+}
+
+if ($backup->get_stage() !== backup_ui::STAGE_COMPLETE) {
+ $renderer = $PAGE->get_renderer('core', 'backup');
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('publishcourseon', 'hub', !empty($hubname)?$hubname:$huburl), 3, 'main');
+ if ($backup->enforce_changed_dependencies()) {
+ debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER);
+ }
+ echo $renderer->progress_bar($backup->get_progress_bar());
+ echo $backup->display($renderer);
+ echo $OUTPUT->footer();
+ die();
+}
+
+//$backupfile = $backup->get_stage_results();
+$backupfile = $bc->get_results();
+$backupfile = $backupfile['backup_destination'];
+//END backup processing
+
+//retrieve the token to call the hub
+$registrationmanager = new registration_manager();
+$registeredhub = $registrationmanager->get_registeredhub($huburl);
+
+//display the sending file page
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('sendingcourse', 'hub'), 3, 'main');
+$renderer = $PAGE->get_renderer('core', 'publish');
+echo $renderer->sendingbackupinfo($backupfile);
+if (ob_get_level()) {
+ ob_flush();
+}
+flush();
+
+//send backup file to the hub
+$curl = new curl();
+$params = array();
+$params['filetype'] = HUB_BACKUP_FILE_TYPE;
+$params['courseid'] = $hubcourseid;
+$params['file'] = $backupfile;
+$params['token'] = $registeredhub->token;
+$curl->post($huburl . "/local/hub/webservice/upload.php", $params);
+
+//delete the temp backup file from user_tohub aera
+$backupfile->delete();
+$bc->destroy();
+
+//Output sending success
+echo $renderer->sentbackupinfo($id, $huburl, $hubname);
+
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// This file is part of Moodle - http://moodle.org/ //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// //
+// 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/>. //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/*
+ * @package course
+ * @subpackage publish
+ * @author Jerome Mouneyrac <jerome@mouneyrac.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
+ *
+ * The forms used for course publication
+ */
+
+
+require_once($CFG->libdir . '/formslib.php');
+require_once($CFG->dirroot . "/" . $CFG->admin . "/registration/lib.php");
+require_once($CFG->dirroot . "/course/publish/lib.php");
+
+/*
+ * Hub selector to choose on which hub we want to publish.
+ */
+
+class hub_publish_selector_form extends moodleform {
+
+ public function definition() {
+ global $CFG;
+ $mform = & $this->_form;
+ $share = $this->_customdata['share'];
+
+ $mform->addElement('header', 'site', get_string('selecthub', 'hub'));
+
+ $mform->addElement('static', 'info', '', get_string('selecthubinfo', 'hub') . html_writer::empty_tag('br'));
+
+ $registrationmanager = new registration_manager();
+ $registeredhubs = $registrationmanager->get_registered_on_hubs();
+
+ //Public hub list
+ $options = array();
+ foreach ($registeredhubs as $hub) {
+
+ $hubname = $hub->hubname;
+ $mform->addElement('hidden', clean_param($hub->huburl, PARAM_ALPHANUMEXT), $hubname);
+ if (empty($hubname)) {
+ $hubname = $hub->huburl;
+ }
+ $mform->addElement('radio', 'huburl', null, ' ' . $hubname, $hub->huburl);
+ if ($hub->huburl == HUB_MOODLEORGHUBURL) {
+ $mform->setDefault('huburl', $hub->huburl);
+ }
+ }
+
+ $mform->addElement('hidden', 'id', $this->_customdata['id']);
+
+ if ($share) {
+ $buttonlabel = get_string('shareonhub', 'hub');
+ $mform->addElement('hidden', 'share', true);
+ } else {
+ $buttonlabel = get_string('advertiseonhub', 'hub');
+ $mform->addElement('hidden', 'advertise', true);
+ }
+
+ $this->add_action_buttons(false, $buttonlabel);
+ }
+
+}
+
+/*
+ * Course publication form
+ */
+
+class course_publication_form extends moodleform {
+
+ public function definition() {
+ global $CFG, $DB, $USER, $OUTPUT;
+
+ $strrequired = get_string('required');
+ $mform = & $this->_form;
+ $huburl = $this->_customdata['huburl'];
+ $hubname = $this->_customdata['hubname'];
+ $course = $this->_customdata['course'];
+ $advertise = $this->_customdata['advertise'];
+ $share = $this->_customdata['share'];
+ $page = $this->_customdata['page'];
+ $site = get_site();
+
+ //hidden parameters
+ $mform->addElement('hidden', 'huburl', $huburl);
+ $mform->addElement('hidden', 'hubname', $hubname);
+
+ //check on the hub if the course has already been published
+ $registrationmanager = new registration_manager();
+ $registeredhub = $registrationmanager->get_registeredhub($huburl);
+ $publicationmanager = new course_publish_manager();
+ $publications = $publicationmanager->get_publications($registeredhub->huburl, $course->id, $advertise);
+
+ if (!empty($publications)) {
+ //get the last publication of this course
+ $publication = array_pop($publications);
+
+ $function = 'hub_get_courses';
+ $options = new stdClass();
+ $options->ids = array($publication->hubcourseid);
+ $options->allsitecourses = 1;
+ $params = array('search' => '', 'downloadable' => $share,
+ 'enrollable' => !$share, 'options' => $options);
+ $serverurl = $huburl . "/local/hub/webservice/webservices.php";
+ require_once($CFG->dirroot . "/webservice/xmlrpc/lib.php");
+ $xmlrpcclient = new webservice_xmlrpc_client($serverurl, $registeredhub->token);
+ try {
+ $result = $xmlrpcclient->call($function, $params);
+ $publishedcourses = $result['courses'];
+ } catch (Exception $e) {
+ $error = $OUTPUT->notification(get_string('errorcourseinfo', 'hub', $e->getMessage()));
+ $mform->addElement('static', 'errorhub', '', $error);
+ }
+ }
+
+ if (!empty($publishedcourses)) {
+ $publishedcourse = $publishedcourses[0];
+ $hubcourseid = $publishedcourse['id'];
+ $defaultfullname = $publishedcourse['fullname'];
+ $defaultshortname = $publishedcourse['shortname'];
+ $defaultsummary = $publishedcourse['description'];
+ $defaultlanguage = $publishedcourse['language'];
+ $defaultpublishername = $publishedcourse['publishername'];
+ $defaultpublisheremail = $publishedcourse['publisheremail'];
+ $defaultcontributornames = $publishedcourse['contributornames'];
+ $defaultcoverage = $publishedcourse['coverage'];
+ $defaultcreatorname = $publishedcourse['creatorname'];
+ $defaultlicenceshortname = $publishedcourse['licenceshortname'];
+ $defaultsubject = $publishedcourse['subject'];
+ $defaultaudience = $publishedcourse['audience'];
+ $defaulteducationallevel = $publishedcourse['educationallevel'];
+ $defaultcreatornotes = $publishedcourse['creatornotes'];
+ $defaultcreatornotesformat = $publishedcourse['creatornotesformat'];
+ $screenshotsnumber = $publishedcourse['screenshots'];
+ $privacy = $publishedcourse['privacy'];
+ if (($screenshotsnumber > 0) and !empty($privacy)) {
+ $page->requires->yui_module('moodle-block_community-imagegallery',
+ 'M.blocks_community.init_imagegallery',
+ array(array('imageids' => array($hubcourseid),
+ 'imagenumbers' => array($screenshotsnumber),
+ 'huburl' => $huburl)));
+ }
+ } else {
+ $defaultfullname = $course->fullname;
+ $defaultshortname = $course->shortname;
+ $defaultsummary = clean_param($course->summary, PARAM_TEXT);
+ if (empty($course->lang)) {
+ $language = get_site()->lang;
+ if (empty($language)) {
+ $defaultlanguage = current_language();
+ } else {
+ $defaultlanguage = $language;
+ }
+ } else {
+ $defaultlanguage = $course->lang;
+ }
+ $defaultpublishername = $USER->firstname . ' ' . $USER->lastname;
+ $defaultpublisheremail = $USER->email;
+ $defaultcontributornames = '';
+ $defaultcoverage = '';
+ $defaultcreatorname = $USER->firstname . ' ' . $USER->lastname;
+ $defaultlicenceshortname = 'cc';
+ $defaultsubject = 'none';
+ $defaultaudience = HUB_AUDIENCE_STUDENTS;
+ $defaulteducationallevel = HUB_EDULEVEL_TERTIARY;
+ $defaultcreatornotes = '';
+ $defaultcreatornotesformat = FORMAT_HTML;
+ $screenshotsnumber = 0;
+ }
+
+ //the input parameters
+ $mform->addElement('header', 'moodle', get_string('publicationinfo', 'hub'));
+
+ $mform->addElement('text', 'name', get_string('coursename', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->addRule('name', $strrequired, 'required', null, 'client');
+ $mform->setType('name', PARAM_TEXT);
+ $mform->setDefault('name', $defaultfullname);
+ $mform->addHelpButton('name', 'name', 'hub');
+
+ $mform->addElement('hidden', 'id', $this->_customdata['id']);
+
+ if ($share) {
+ $buttonlabel = get_string('shareon', 'hub', !empty($hubname) ? $hubname : $huburl);
+
+ $mform->addElement('hidden', 'share', $share);
+
+ $mform->addElement('text', 'demourl', get_string('demourl', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->setType('demourl', PARAM_URL);
+ $mform->setDefault('demourl', new moodle_url("/course/view.php?id=" . $course->id));
+ $mform->addHelpButton('demourl', 'demourl', 'hub');
+ }
+
+ if ($advertise) {
+ if (empty($publishedcourses)) {
+ $buttonlabel = get_string('advertiseon', 'hub', !empty($hubname) ? $hubname : $huburl);
+ } else {
+ $buttonlabel = get_string('readvertiseon', 'hub', !empty($hubname) ? $hubname : $huburl);
+ }
+ $mform->addElement('hidden', 'advertise', $advertise);
+ $mform->addElement('hidden', 'courseurl', $CFG->wwwroot . "/course/view.php?id=" . $course->id);
+ $mform->addElement('static', 'courseurlstring', get_string('courseurl', 'hub'));
+ $mform->setDefault('courseurlstring', new moodle_url("/course/view.php?id=" . $course->id));
+ $mform->addHelpButton('courseurlstring', 'courseurl', 'hub');
+ }
+
+ $mform->addElement('text', 'courseshortname', get_string('courseshortname', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->setDefault('courseshortname', $defaultshortname);
+ $mform->addHelpButton('courseshortname', 'courseshortname', 'hub');
+
+ $mform->addElement('textarea', 'description', get_string('description'), array('rows' => 10,
+ 'cols' => 57));
+ $mform->addRule('description', $strrequired, 'required', null, 'client');
+ $mform->setDefault('description', $defaultsummary);
+ $mform->setType('description', PARAM_TEXT);
+ $mform->addHelpButton('description', 'description', 'hub');
+
+ $languages = get_string_manager()->get_list_of_languages();
+ collatorlib::asort($languages);
+ $mform->addElement('select', 'language', get_string('language'), $languages);
+ $mform->setDefault('language', $defaultlanguage);
+ $mform->addHelpButton('language', 'language', 'hub');
+
+
+ $mform->addElement('text', 'publishername', get_string('publishername', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->setDefault('publishername', $defaultpublishername);
+ $mform->addRule('publishername', $strrequired, 'required', null, 'client');
+ $mform->addHelpButton('publishername', 'publishername', 'hub');
+
+ $mform->addElement('text', 'publisheremail', get_string('publisheremail', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->setDefault('publisheremail', $defaultpublisheremail);
+ $mform->addRule('publisheremail', $strrequired, 'required', null, 'client');
+ $mform->addHelpButton('publisheremail', 'publisheremail', 'hub');
+
+ $mform->addElement('text', 'creatorname', get_string('creatorname', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->addRule('creatorname', $strrequired, 'required', null, 'client');
+ $mform->setType('creatorname', PARAM_TEXT);
+ $mform->setDefault('creatorname', $defaultcreatorname);
+ $mform->addHelpButton('creatorname', 'creatorname', 'hub');
+
+ $mform->addElement('text', 'contributornames', get_string('contributornames', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->setDefault('contributornames', $defaultcontributornames);
+ $mform->addHelpButton('contributornames', 'contributornames', 'hub');
+
+ $mform->addElement('text', 'coverage', get_string('tags', 'hub'),
+ array('class' => 'metadatatext'));
+ $mform->setType('coverage', PARAM_TEXT);
+ $mform->setDefault('coverage', $defaultcoverage);
+ $mform->addHelpButton('coverage', 'tags', 'hub');
+
+
+
+ require_once($CFG->libdir . "/licenselib.php");
+ $licensemanager = new license_manager();
+ $licences = $licensemanager->get_licenses();
+ $options = array();
+ foreach ($licences as $license) {
+ $options[$license->shortname] = get_string($license->shortname, 'license');
+ }
+ $mform->addElement('select', 'licence', get_string('license'), $options);
+ $mform->setDefault('licence', $defaultlicenceshortname);
+ unset($options);
+ $mform->addHelpButton('licence', 'licence', 'hub');
+
+ $options = $publicationmanager->get_sorted_subjects();
+
+ //prepare data for the smartselect
+ foreach ($options as $key => &$option) {
+ $keylength = strlen($key);
+ if ($keylength == 10) {
+ $option = " " . $option;
+ } else if ($keylength == 12) {
+ $option = " " . $option;
+ }
+ }
+
+ $options = array('none' => get_string('none', 'hub')) + $options;
+ $mform->addElement('select', 'subject', get_string('subject', 'hub'), $options);
+ unset($options);
+ $mform->addHelpButton('subject', 'subject', 'hub');
+ $mform->setDefault('subject', $defaultsubject);
+ $mform->addRule('subject', $strrequired, 'required', null, 'client');
+ $this->init_javascript_enhancement('subject', 'smartselect', array('selectablecategories' => false, 'mode' => 'compact'));
+
+ $options = array();
+ $options[HUB_AUDIENCE_EDUCATORS] = get_string('audienceeducators', 'hub');
+ $options[HUB_AUDIENCE_STUDENTS] = get_string('audiencestudents', 'hub');
+ $options[HUB_AUDIENCE_ADMINS] = get_string('audienceadmins', 'hub');
+ $mform->addElement('select', 'audience', get_string('audience', 'hub'), $options);
+ $mform->setDefault('audience', $defaultaudience);
+ unset($options);
+ $mform->addHelpButton('audience', 'audience', 'hub');
+
+ $options = array();
+ $options[HUB_EDULEVEL_PRIMARY] = get_string('edulevelprimary', 'hub');
+ $options[HUB_EDULEVEL_SECONDARY] = get_string('edulevelsecondary', 'hub');
+ $options[HUB_EDULEVEL_TERTIARY] = get_string('eduleveltertiary', 'hub');
+ $options[HUB_EDULEVEL_GOVERNMENT] = get_string('edulevelgovernment', 'hub');
+ $options[HUB_EDULEVEL_ASSOCIATION] = get_string('edulevelassociation', 'hub');
+ $options[HUB_EDULEVEL_CORPORATE] = get_string('edulevelcorporate', 'hub');
+ $options[HUB_EDULEVEL_OTHER] = get_string('edulevelother', 'hub');
+ $mform->addElement('select', 'educationallevel', get_string('educationallevel', 'hub'), $options);
+ $mform->setDefault('educationallevel', $defaulteducationallevel);
+ unset($options);
+ $mform->addHelpButton('educationallevel', 'educationallevel', 'hub');
+
+ $editoroptions = array('maxfiles' => 0, 'maxbytes' => 0, 'trusttext' => false, 'forcehttps' => false);
+ $mform->addElement('editor', 'creatornotes', get_string('creatornotes', 'hub'), '', $editoroptions);
+ $mform->addRule('creatornotes', $strrequired, 'required', null, 'client');
+ $mform->setType('creatornotes', PARAM_CLEANHTML);
+ $mform->addHelpButton('creatornotes', 'creatornotes', 'hub');
+
+ if ($advertise) {
+ if (!empty($screenshotsnumber)) {
+
+ if (!empty($privacy)) {
+ $baseurl = new moodle_url($huburl . '/local/hub/webservice/download.php',
+ array('courseid' => $hubcourseid, 'filetype' => HUB_SCREENSHOT_FILE_TYPE));
+ $screenshothtml = html_writer::empty_tag('img',
+ array('src' => $baseurl, 'alt' => $defaultfullname));
+ $screenshothtml = html_writer::tag('div', $screenshothtml,
+ array('class' => 'coursescreenshot',
+ 'id' => 'image-' . $hubcourseid));
+ } else {
+ $screenshothtml = get_string('existingscreenshotnumber', 'hub', $screenshotsnumber);
+ }
+ $mform->addElement('static', 'existingscreenshots', get_string('existingscreenshots', 'hub'), $screenshothtml);
+ $mform->addHelpButton('existingscreenshots', 'deletescreenshots', 'hub');
+ $mform->addElement('checkbox', 'deletescreenshots', '', ' ' . get_string('deletescreenshots', 'hub'));
+ }
+
+ $mform->addElement('hidden', 'existingscreenshotnumber', $screenshotsnumber);
+ }
+
+ $mform->addElement('filemanager', 'screenshots', get_string('addscreenshots', 'hub'), null,
+ array('subdirs' => 0,
+ 'maxbytes' => 1000000,
+ 'maxfiles' => 3
+ ));
+ $mform->addHelpButton('screenshots', 'screenshots', 'hub');
+
+ $this->add_action_buttons(false, $buttonlabel);
+
+ //set default value for creatornotes editor
+ $data = new stdClass();
+ $data->creatornotes = array();
+ $data->creatornotes['text'] = $defaultcreatornotes;
+ $data->creatornotes['format'] = $defaultcreatornotesformat;
+ $this->set_data($data);
+ }
+
+ function validation($data, $files) {
+ global $CFG;
+
+ $errors = array();
+
+ if ($this->_form->_submitValues['subject'] == 'none') {
+ $errors['subject'] = get_string('mustselectsubject', 'hub');
+ }
+
+ return $errors;
+ }
+
+}
+
--- /dev/null
+<?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/>.
+
+/*
+ * @package course
+ * @subpackage publish
+ * @author Jerome Mouneyrac <jerome@mouneyrac.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
+ *
+ * On this page the user selects where he wants to publish the course
+*/
+
+require('../../config.php');
+
+require_once($CFG->dirroot.'/' . $CFG->admin . '/registration/lib.php');
+require_once($CFG->dirroot.'/course/publish/forms.php');
+
+$id = required_param('id', PARAM_INT);
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+require_login($course);
+
+$PAGE->set_url('/course/publish/hubselector.php', array('id' => $course->id));
+$PAGE->set_pagelayout('course');
+$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+$PAGE->set_heading($course->fullname);
+
+$registrationmanager = new registration_manager();
+$registeredhubs = $registrationmanager->get_registered_on_hubs();
+if (empty($registeredhubs)) {
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('publishon', 'hub'), 3, 'main');
+ echo $OUTPUT->box(get_string('notregisteredonhub', 'hub'));
+ echo $OUTPUT->footer();
+ die();
+}
+
+
+$share = optional_param('share', false, PARAM_BOOL);
+$advertise = optional_param('advertise', false, PARAM_BOOL);
+$hubselectorform = new hub_publish_selector_form('',
+ array('id' => $id, 'share' => $share, 'advertise' => $advertise));
+$fromform = $hubselectorform->get_data();
+
+//// Redirect to the registration form if an URL has been chosen ////
+$huburl = optional_param('huburl', false, PARAM_URL);
+
+//redirect
+if (!empty($huburl) and confirm_sesskey()) {
+ $hubname = optional_param(clean_param($huburl, PARAM_ALPHANUMEXT), '', PARAM_TEXT);
+ $params = array('sesskey' => sesskey(), 'id' => $id,
+ 'huburl' => $huburl, 'hubname' => $hubname, 'share' => $share, 'advertise' => $advertise);
+ redirect(new moodle_url($CFG->wwwroot."/course/publish/metadata.php",
+ $params));
+}
+
+
+//// OUTPUT ////
+
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('publishon', 'hub'), 3, 'main');
+$hubselectorform->display();
+echo $OUTPUT->footer();
\ No newline at end of file
--- /dev/null
+<?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/>.
+
+/*
+ * @package course
+ * @subpackage publish
+ * @author Jerome Mouneyrac <jerome@mouneyrac.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
+ *
+ * The user selects if he wants to publish the course on Moodle.org hub or
+ * on a specific hub. The site must be registered on a hub to be able to
+ * publish a course on it.
+*/
+
+require('../../config.php');
+require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
+require_once($CFG->dirroot . '/course/publish/lib.php');
+
+$id = required_param('id', PARAM_INT);
+$hubname = optional_param('hubname', 0, PARAM_TEXT);
+$huburl = optional_param('huburl', 0, PARAM_URL);
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+
+require_login($course);
+$context = context_course::instance($course->id);
+$shortname = format_string($course->shortname, true, array('context' => $context));
+
+$PAGE->set_url('/course/publish/index.php', array('id' => $course->id));
+$PAGE->set_pagelayout('course');
+$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+$PAGE->set_heading($course->fullname);
+
+//check that the PHP xmlrpc extension is enabled
+if (!extension_loaded('xmlrpc')) {
+ $notificationerror = $OUTPUT->doc_link('admin/environment/php_extension/xmlrpc', '');
+ $notificationerror .= get_string('xmlrpcdisabledpublish', 'hub');
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('publishcourse', 'hub', $shortname), 3, 'main');
+ echo $OUTPUT->notification($notificationerror);
+ echo $OUTPUT->footer();
+ die();
+}
+
+if (has_capability('moodle/course:publish', context_course::instance($id))) {
+
+ $publicationmanager = new course_publish_manager();
+ $confirmmessage = '';
+
+ //update the courses status
+ $updatestatusid = optional_param('updatestatusid', false, PARAM_INT);
+ if (!empty($updatestatusid) and confirm_sesskey()) {
+ //get the communication token from the publication
+ $hub = $publicationmanager->get_registeredhub_by_publication($updatestatusid);
+ if (empty($hub)) {
+ $confirmmessage = $OUTPUT->notification(get_string('nocheckstatusfromunreghub', 'hub'));
+ } else {
+ //get all site courses registered on this hub
+ $function = 'hub_get_courses';
+ $params = array('search' => '', 'downloadable' => 1,
+ 'enrollable' => 1, 'options' => array( 'allsitecourses' => 1));
+ $serverurl = $hub->huburl."/local/hub/webservice/webservices.php";
+ require_once($CFG->dirroot."/webservice/xmlrpc/lib.php");
+ $xmlrpcclient = new webservice_xmlrpc_client($serverurl, $hub->token);
+ $result = $xmlrpcclient->call($function, $params);
+ $sitecourses = $result['courses'];
+
+ //update status for all these course
+ foreach ($sitecourses as $sitecourse) {
+ //get the publication from the hub course id
+ $publication = $publicationmanager->get_publication($sitecourse['id'], $hub->huburl);
+ if (!empty($publication)) {
+ $publication->status = $sitecourse['privacy'];
+ $publication->timechecked = time();
+ $publicationmanager->update_publication($publication);
+ } else {
+ $msgparams = new stdClass();
+ $msgparams->id = $sitecourse['id'];
+ $msgparams->hubname = html_writer::tag('a', $hub->hubname, array('href' => $hub->huburl));
+ $confirmmessage .= $OUTPUT->notification(
+ get_string('detectednotexistingpublication', 'hub', $msgparams));
+ }
+ }
+ }
+ }
+
+ //if the site os registered on no hub display an error page
+ $registrationmanager = new registration_manager();
+ $registeredhubs = $registrationmanager->get_registered_on_hubs();
+ if (empty($registeredhubs)) {
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('publishon', 'hub'), 3, 'main');
+ echo $OUTPUT->box(get_string('notregisteredonhub', 'hub'));
+ echo $OUTPUT->footer();
+ die();
+ }
+
+ $renderer = $PAGE->get_renderer('core', 'publish');
+
+ /// UNPUBLISH
+ $cancel = optional_param('cancel', 0, PARAM_BOOL);
+ if (!empty($cancel) and confirm_sesskey()) {
+ $confirm = optional_param('confirm', 0, PARAM_BOOL);
+ $hubcourseid = optional_param('hubcourseid', 0, PARAM_INT);
+ $publicationid = optional_param('publicationid', 0, PARAM_INT);
+ $timepublished = optional_param('timepublished', 0, PARAM_INT);
+ $publication->courseshortname = $course->shortname;
+ $publication->courseid = $course->id;
+ $publication->hubname = $hubname;
+ $publication->huburl = $huburl;
+ $publication->hubcourseid = $hubcourseid;
+ $publication->timepublished = $timepublished;
+ if (empty($publication->hubname)) {
+ $publication->hubname = $huburl;
+ }
+ $publication->id = $publicationid;
+ if($confirm) {
+ //unpublish the publication by web service
+ $registeredhub = $registrationmanager->get_registeredhub($huburl);
+ $function = 'hub_unregister_courses';
+ $params = array('courseids' => array( $publication->hubcourseid));
+ $serverurl = $huburl."/local/hub/webservice/webservices.php";
+ require_once($CFG->dirroot."/webservice/xmlrpc/lib.php");
+ $xmlrpcclient = new webservice_xmlrpc_client($serverurl, $registeredhub->token);
+ $result = $xmlrpcclient->call($function, $params);
+
+ //delete the publication from the database
+ $publicationmanager->delete_publication($publicationid);
+
+ //display confirmation message
+ $confirmmessage = $OUTPUT->notification(get_string('courseunpublished', 'hub', $publication), 'notifysuccess');
+
+ } else {
+ //display confirmation page for unpublishing
+
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('unpublishcourse', 'hub', $shortname), 3, 'main');
+ echo $renderer->confirmunpublishing($publication);
+ echo $OUTPUT->footer();
+ die();
+ }
+ }
+
+ //check if a course was published
+ if (optional_param('published', 0, PARAM_TEXT)) {
+ $confirmmessage = $OUTPUT->notification(get_string('coursepublished', 'hub',
+ empty($hubname)?$huburl:$hubname), 'notifysuccess');
+ }
+
+
+ /// OUTPUT
+ echo $OUTPUT->header();
+ echo $confirmmessage;
+
+ echo $OUTPUT->heading(get_string('publishcourse', 'hub', $shortname), 3, 'main');
+ echo $renderer->publicationselector($course->id);
+
+ $publications = $publicationmanager->get_course_publications($course->id);
+ if (!empty($publications)) {
+ echo $OUTPUT->heading(get_string('publishedon', 'hub'), 3, 'main');
+ echo $renderer->registeredonhublisting($course->id, $publications);
+ }
+
+ echo $OUTPUT->footer();
+
+}
--- /dev/null
+<?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/>.
+/// TIME PERIOD ///
+
+define('HUB_LASTMODIFIED_WEEK', 7);
+
+define('HUB_LASTMODIFIED_FORTEENNIGHT', 14);
+
+define('HUB_LASTMODIFIED_MONTH', 30);
+
+
+
+//// AUDIENCE ////
+
+/**
+ * Audience: educators
+ */
+define('HUB_AUDIENCE_EDUCATORS', 'educators');
+
+/**
+ * Audience: students
+ */
+define('HUB_AUDIENCE_STUDENTS', 'students');
+
+/**
+ * Audience: admins
+ */
+define('HUB_AUDIENCE_ADMINS', 'admins');
+
+
+
+///// EDUCATIONAL LEVEL /////
+
+/**
+ * Educational level: primary
+ */
+define('HUB_EDULEVEL_PRIMARY', 'primary');
+
+/**
+ * Educational level: secondary
+ */
+define('HUB_EDULEVEL_SECONDARY', 'secondary');
+
+/**
+ * Educational level: tertiary
+ */
+define('HUB_EDULEVEL_TERTIARY', 'tertiary');
+
+/**
+ * Educational level: government
+ */
+define('HUB_EDULEVEL_GOVERNMENT', 'government');
+
+/**
+ * Educational level: association
+ */
+define('HUB_EDULEVEL_ASSOCIATION', 'association');
+
+/**
+ * Educational level: corporate
+ */
+define('HUB_EDULEVEL_CORPORATE', 'corporate');
+
+/**
+ * Educational level: other
+ */
+define('HUB_EDULEVEL_OTHER', 'other');
+
+
+
+///// FILE TYPES /////
+
+/**
+ * FILE TYPE: COURSE SCREENSHOT
+ */
+define('HUB_SCREENSHOT_FILE_TYPE', 'screenshot');
+
+/**
+ * FILE TYPE: HUB SCREENSHOT
+ */
+define('HUB_HUBSCREENSHOT_FILE_TYPE', 'hubscreenshot');
+
+/**
+ * FILE TYPE: BACKUP
+ */
+define('HUB_BACKUP_FILE_TYPE', 'backup');
+
+/**
+ *
+ * Course publication library
+ *
+ * @package course
+ * @copyright 2010 Moodle Pty Ltd (http://moodle.com)
+ * @author Jerome Mouneyrac
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class course_publish_manager {
+
+ /**
+ * Record a course publication
+ * @param int $hubid the hub id from the 'registered on hub' table
+ * @param int $courseid the course id from site point of view
+ * @param int $enrollable if the course is enrollable = 1, if downloadable = 0
+ * @param int $hubcourseid the course id from the hub point of view
+ */
+ public function add_course_publication($huburl, $courseid, $enrollable, $hubcourseid) {
+ global $DB;
+ $publication = new stdClass();
+ $publication->huburl = $huburl;
+ $publication->courseid = $courseid;
+ $publication->hubcourseid = $hubcourseid;
+ $publication->enrollable = (int) $enrollable;
+ $publication->timepublished = time();
+ $DB->insert_record('course_published', $publication);
+ }
+
+ /**
+ * Update a enrollable course publication
+ * @param int $publicationid
+ */
+ public function update_enrollable_course_publication($publicationid) {
+ global $DB;
+ $publication = new stdClass();
+ $publication->id = $publicationid;
+ $publication->timepublished = time();
+ $DB->update_record('course_published', $publication);
+ }
+
+ /**
+ * Update a course publication
+ * @param object $publication
+ */
+ public function update_publication($publication) {
+ global $DB;
+ $DB->update_record('course_published', $publication);
+ }
+
+ /**
+ * Get courses publications
+ * @param int $hubid specify a hub
+ * @param int $courseid specify a course
+ * @param int $enrollable specify type of publication (enrollable or downloadable)
+ * @return array of publications
+ */
+ public function get_publications($huburl = null, $courseid = null, $enrollable = -1) {
+ global $DB;
+ $params = array();
+
+ if (!empty($huburl)) {
+ $params['huburl'] = $huburl;
+ }
+
+ if (!empty($courseid)) {
+ $params['courseid'] = $courseid;
+ }
+
+ if ($enrollable != -1) {
+ $params['enrollable'] = (int) $enrollable;
+ }
+
+ return $DB->get_records('course_published', $params);
+ }
+
+ /**
+ * Get a publication for a course id on a hub
+ * (which is either the id of the unique possible enrollable publication of a course,
+ * either an id of one of the downloadable publication)
+ * @param int $hubcourseid
+ * @param string $huburl
+ * @return object publication
+ */
+ public function get_publication($hubcourseid, $huburl) {
+ global $DB;
+ return $DB->get_record('course_published',
+ array('hubcourseid' => $hubcourseid, 'huburl' => $huburl));
+ }
+
+ /**
+ * Get all publication for a course
+ * @param int $courseid
+ * @return array of publication
+ */
+ public function get_course_publications($courseid) {
+ global $DB;
+ $sql = 'SELECT cp.id, cp.status, cp.timechecked, cp.timepublished, rh.hubname,
+ rh.huburl, cp.courseid, cp.enrollable, cp.hubcourseid
+ FROM {course_published} cp, {registration_hubs} rh
+ WHERE cp.huburl = rh.huburl and cp.courseid = :courseid
+ ORDER BY cp.enrollable DESC, rh.hubname, cp.timepublished';
+ $params = array('courseid' => $courseid);
+ return $DB->get_records_sql($sql, $params);
+ }
+
+ /**
+ * Get the hub concerned by a publication
+ * @param int $publicationid
+ * @return object the hub (id, name, url, token)
+ */
+ public function get_registeredhub_by_publication($publicationid) {
+ global $DB;
+ $sql = 'SELECT rh.huburl, rh.hubname, rh.token
+ FROM {course_published} cp, {registration_hubs} rh
+ WHERE cp.huburl = rh.huburl and cp.id = :publicationid';
+ $params = array('publicationid' => $publicationid);
+ return $DB->get_record_sql($sql, $params);
+ }
+
+ /**
+ * Delete a publication
+ * @param int $publicationid
+ */
+ public function delete_publication($publicationid) {
+ global $DB;
+ $DB->delete_records('course_published', array('id' => $publicationid));
+ }
+
+ /**
+ * Delete publications for a hub
+ * @param string $huburl
+ * @param int $enrollable
+ */
+ public function delete_hub_publications($huburl, $enrollable = -1) {
+ global $DB;
+
+ $params = array();
+
+ if (!empty($huburl)) {
+ $params['huburl'] = $huburl;
+ }
+
+ if ($enrollable != -1) {
+ $params['enrollable'] = (int) $enrollable;
+ }
+
+ $DB->delete_records('course_published', $params);
+ }
+
+ /**
+ * Get an array of all block instances for a given context
+ * @param int $contextid a context id
+ * @return array of block instances.
+ */
+ public function get_block_instances_by_context($contextid, $sort = '') {
+ global $DB;
+ return $DB->get_records('block_instances', array('parentcontextid' => $contextid), $sort);
+ }
+
+ /**
+ * Retrieve all the sorted course subjects
+ * @return array $subjects
+ */
+ public function get_sorted_subjects() {
+ $subjects = get_string_manager()->load_component_strings('edufields', current_language());
+
+ //sort the subjects
+ asort($subjects);
+ foreach ($subjects as $key => $option) {
+ $keylength = strlen($key);
+ if ($keylength == 8) {
+ $toplevel[$key] = $option;
+ } else if ($keylength == 10) {
+ $sublevel[substr($key, 0, 8)][$key] = $option;
+ } else if ($keylength == 12) {
+ $subsublevel[substr($key, 0, 8)][substr($key, 0, 10)][$key] = $option;
+ }
+ }
+
+ //recreate the initial structure returned by get_string_manager()
+ $subjects = array();
+ foreach ($toplevel as $key => $name) {
+ $subjects[$key] = $name;
+ foreach ($sublevel[$key] as $subkey => $subname) {
+ $subjects[$subkey] = $subname;
+ foreach ($subsublevel[$key][$subkey] as $subsubkey => $subsubname) {
+ $subjects[$subsubkey] = $subsubname;
+ }
+ }
+ }
+
+ return $subjects;
+ }
+
+}
+?>
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// This file is part of Moodle - http://moodle.org/ //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// //
+// 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/>. //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/*
+ * @package course
+ * @subpackage publish
+ * @author Jerome Mouneyrac <jerome@mouneyrac.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
+ *
+ * This page display the publication metadata form
+ */
+
+require_once('../../config.php');
+require_once($CFG->dirroot . '/course/publish/forms.php');
+require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
+require_once($CFG->dirroot . '/course/publish/lib.php');
+require_once($CFG->libdir . '/filelib.php');
+
+
+//check user access capability to this page
+$id = required_param('id', PARAM_INT);
+
+$course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST);
+require_login($course);
+
+//page settings
+$PAGE->set_url('/course/publish/metadata.php', array('id' => $course->id));
+$PAGE->set_pagelayout('course');
+$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+$PAGE->set_heading($course->fullname);
+
+//check that the PHP xmlrpc extension is enabled
+if (!extension_loaded('xmlrpc')) {
+ $errornotification = $OUTPUT->doc_link('admin/environment/php_extension/xmlrpc', '');
+ $errornotification .= get_string('xmlrpcdisabledpublish', 'hub');
+ $context = context_course::instance($course->id);
+ $shortname = format_string($course->shortname, true, array('context' => $context));
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('publishcourse', 'hub', $shortname), 3, 'main');
+ echo $OUTPUT->notification($errornotification);
+ echo $OUTPUT->footer();
+ die();
+}
+
+if (has_capability('moodle/course:publish', context_course::instance($id))) {
+
+ //retrieve hub name and hub url
+ $huburl = optional_param('huburl', '', PARAM_URL);
+ $hubname = optional_param('hubname', '', PARAM_TEXT);
+ if (empty($huburl) or !confirm_sesskey()) {
+ throw new moodle_exception('missingparameter');
+ }
+
+ //set the publication form
+ $advertise = optional_param('advertise', false, PARAM_BOOL);
+ $share = optional_param('share', false, PARAM_BOOL);
+ $coursepublicationform = new course_publication_form('',
+ array('huburl' => $huburl, 'hubname' => $hubname, 'sesskey' => sesskey(),
+ 'course' => $course, 'advertise' => $advertise, 'share' => $share,
+ 'id' => $id, 'page' => $PAGE));
+ $fromform = $coursepublicationform->get_data();
+
+ //retrieve the token to call the hub
+ $registrationmanager = new registration_manager();
+ $registeredhub = $registrationmanager->get_registeredhub($huburl);
+
+ //setup web service xml-rpc client
+ $serverurl = $huburl . "/local/hub/webservice/webservices.php";
+ require_once($CFG->dirroot . "/webservice/xmlrpc/lib.php");
+ $xmlrpcclient = new webservice_xmlrpc_client($serverurl, $registeredhub->token);
+
+ if (!empty($fromform)) {
+
+ $publicationmanager = new course_publish_manager();
+
+ //retrieve the course information
+ $courseinfo = new stdClass();
+ $courseinfo->fullname = $fromform->name;
+ $courseinfo->shortname = $fromform->courseshortname;
+ $courseinfo->description = $fromform->description;
+ $courseinfo->language = $fromform->language;
+ $courseinfo->publishername = $fromform->publishername;
+ $courseinfo->publisheremail = $fromform->publisheremail;
+ $courseinfo->contributornames = $fromform->contributornames;
+ $courseinfo->coverage = $fromform->coverage;
+ $courseinfo->creatorname = $fromform->creatorname;
+ $courseinfo->licenceshortname = $fromform->licence;
+ $courseinfo->subject = $fromform->subject;
+ $courseinfo->audience = $fromform->audience;
+ $courseinfo->educationallevel = $fromform->educationallevel;
+ $creatornotes = $fromform->creatornotes;
+ $courseinfo->creatornotes = $creatornotes['text'];
+ $courseinfo->creatornotesformat = $creatornotes['format'];
+ $courseinfo->sitecourseid = $id;
+ if (!empty($fromform->deletescreenshots)) {
+ $courseinfo->deletescreenshots = $fromform->deletescreenshots;
+ }
+ if ($share) {
+ $courseinfo->demourl = $fromform->demourl;
+ $courseinfo->enrollable = false;
+ } else {
+ $courseinfo->courseurl = $fromform->courseurl;
+ $courseinfo->enrollable = true;
+ }
+
+ //retrieve the outcomes of this course
+ require_once($CFG->libdir . '/grade/grade_outcome.php');
+ $outcomes = grade_outcome::fetch_all_available($id);
+ if (!empty($outcomes)) {
+ foreach ($outcomes as $outcome) {
+ $sentoutcome = new stdClass();
+ $sentoutcome->fullname = $outcome->fullname;
+ $courseinfo->outcomes[] = $sentoutcome;
+ }
+ }
+
+ //retrieve the content information from the course
+ $coursecontext = context_course::instance($course->id);
+ $courseblocks = $publicationmanager->get_block_instances_by_context($coursecontext->id, 'blockname');
+
+ if (!empty($courseblocks)) {
+ $blockname = '';
+ foreach ($courseblocks as $courseblock) {
+ if ($courseblock->blockname != $blockname) {
+ if (!empty($blockname)) {
+ $courseinfo->contents[] = $content;
+ }
+
+ $blockname = $courseblock->blockname;
+ $content = new stdClass();
+ $content->moduletype = 'block';
+ $content->modulename = $courseblock->blockname;
+ $content->contentcount = 1;
+ } else {
+ $content->contentcount = $content->contentcount + 1;
+ }
+ }
+ $courseinfo->contents[] = $content;
+ }
+
+ $activities = get_fast_modinfo($course, $USER->id);
+ foreach ($activities->instances as $activityname => $activitydetails) {
+ $content = new stdClass();
+ $content->moduletype = 'activity';
+ $content->modulename = $activityname;
+ $content->contentcount = count($activities->instances[$activityname]);
+ $courseinfo->contents[] = $content;
+ }
+
+ //save into screenshots field the references to the screenshot content hash
+ //(it will be like a unique id from the hub perspective)
+ if (!empty($fromform->deletescreenshots) or $share) {
+ $courseinfo->screenshots = 0;
+ } else {
+ $courseinfo->screenshots = $fromform->existingscreenshotnumber;
+ }
+ if (!empty($fromform->screenshots)) {
+ $screenshots = $fromform->screenshots;
+ $fs = get_file_storage();
+ $files = $fs->get_area_files(context_user::instance($USER->id)->id, 'user', 'draft', $screenshots);
+ if (!empty($files)) {
+ $courseinfo->screenshots = $courseinfo->screenshots + count($files) - 1; //minus the ./ directory
+ }
+ }
+
+ // PUBLISH ACTION
+
+ //publish the course information
+ $function = 'hub_register_courses';
+ $params = array('courses' => array($courseinfo));
+ try {
+ $courseids = $xmlrpcclient->call($function, $params);
+ } catch (Exception $e) {
+ throw new moodle_exception('errorcoursepublish', 'hub',
+ new moodle_url('/course/view.php', array('id' => $id)), $e->getMessage());
+ }
+
+ if (count($courseids) != 1) {
+ throw new moodle_exception('errorcoursewronglypublished', 'hub');
+ }
+
+ //save the record into the published course table
+ $publication = $publicationmanager->get_publication($courseids[0], $huburl);
+ if (empty($publication)) {
+ //if never been published or if we share, we need to save this new publication record
+ $publicationmanager->add_course_publication($registeredhub->huburl, $course->id, !$share, $courseids[0]);
+ } else {
+ //if we update the enrollable course publication we update the publication record
+ $publicationmanager->update_enrollable_course_publication($publication->id);
+ }
+
+ // SEND FILES
+ $curl = new curl();
+
+ // send screenshots
+ if (!empty($fromform->screenshots)) {
+
+ if (!empty($fromform->deletescreenshots) or $share) {
+ $screenshotnumber = 0;
+ } else {
+ $screenshotnumber = $fromform->existingscreenshotnumber;
+ }
+ foreach ($files as $file) {
+ if ($file->is_valid_image()) {
+ $screenshotnumber = $screenshotnumber + 1;
+ $params = array();
+ $params['filetype'] = HUB_SCREENSHOT_FILE_TYPE;
+ $params['file'] = $file;
+ $params['courseid'] = $courseids[0];
+ $params['filename'] = $file->get_filename();
+ $params['screenshotnumber'] = $screenshotnumber;
+ $params['token'] = $registeredhub->token;
+ $curl->post($huburl . "/local/hub/webservice/upload.php", $params);
+ }
+ }
+ }
+
+ //redirect to the backup process page
+ if ($share) {
+ $params = array('sesskey' => sesskey(), 'id' => $id, 'hubcourseid' => $courseids[0],
+ 'huburl' => $huburl, 'hubname' => $hubname);
+ $backupprocessurl = new moodle_url("/course/publish/backup.php", $params);
+ redirect($backupprocessurl);
+ } else {
+ //redirect to the index publis page
+ redirect(new moodle_url('/course/publish/index.php',
+ array('sesskey' => sesskey(), 'id' => $id, 'published' => true,
+ 'hubname' => $hubname, 'huburl' => $huburl)));
+ }
+ }
+
+ /////// OUTPUT SECTION /////////////
+
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('publishcourseon', 'hub', !empty($hubname) ? $hubname : $huburl), 3, 'main');
+
+ //display hub information (logo, name, description)
+ $function = 'hub_get_info';
+ $params = array();
+ try {
+ $hubinfo = $xmlrpcclient->call($function, $params);
+ } catch (Exception $e) {
+ //only print error log in apache (for backward compatibility)
+ error_log(print_r($e->getMessage(), true));
+ }
+ $renderer = $PAGE->get_renderer('core', 'publish');
+ if (!empty($hubinfo)) {
+ echo $renderer->hubinfo($hubinfo);
+ }
+
+ //display metadata form
+ $coursepublicationform->display();
+ echo $OUTPUT->footer();
+}
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// This file is part of Moodle - http://moodle.org/ //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// //
+// 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/>. //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * Course publish renderer.
+ * @package course
+ * @subpackage publish
+ * @copyright 2010 Moodle Pty Ltd (http://moodle.com)
+ * @author Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_publish_renderer extends plugin_renderer_base {
+
+ /**
+ * Display the selector to advertise or publish a course
+ */
+ public function publicationselector($courseid) {
+ $text = '';
+
+ $advertiseurl = new moodle_url("/course/publish/hubselector.php",
+ array('sesskey' => sesskey(), 'id' => $courseid, 'advertise' => true));
+ $advertisebutton = new single_button($advertiseurl, get_string('advertise', 'hub'));
+ $text .= $this->output->render($advertisebutton);
+ $text .= html_writer::tag('div', get_string('advertisepublication_help', 'hub'),
+ array('class' => 'publishhelp'));
+
+ $text .= html_writer::empty_tag('br'); /// TODO Delete
+
+ $uploadurl = new moodle_url("/course/publish/hubselector.php",
+ array('sesskey' => sesskey(), 'id' => $courseid, 'share' => true));
+ $uploadbutton = new single_button($uploadurl, get_string('share', 'hub'));
+ $text .= $this->output->render($uploadbutton);
+ $text .= html_writer::tag('div', get_string('sharepublication_help', 'hub'),
+ array('class' => 'publishhelp'));
+
+ return $text;
+ }
+
+ /**
+ * Display the listing of hub where a course is registered on
+ */
+ public function registeredonhublisting($courseid, $publications) {
+ global $CFG;
+ $table = new html_table();
+ $table->head = array(get_string('type', 'hub'), get_string('hub', 'hub'),
+ get_string('date'), get_string('status', 'hub'), get_string('operation', 'hub'));
+ $table->size = array('10%', '40%', '20%', '%10', '%15');
+
+ $brtag = html_writer::empty_tag('br');
+
+ foreach ($publications as $publication) {
+
+ $updatebuttonhtml = '';
+
+ $params = array('sesskey' => sesskey(), 'id' => $publication->courseid,
+ 'hubcourseid' => $publication->hubcourseid,
+ 'huburl' => $publication->huburl, 'hubname' => $publication->hubname,
+ 'cancel' => true, 'publicationid' => $publication->id,
+ 'timepublished' => $publication->timepublished);
+ $cancelurl = new moodle_url("/course/publish/index.php", $params);
+ $cancelbutton = new single_button($cancelurl, get_string('removefromhub', 'hub'));
+ $cancelbutton->class = 'centeredbutton';
+ $cancelbuttonhtml = $this->output->render($cancelbutton);
+
+ if ($publication->enrollable) {
+ $params = array('sesskey' => sesskey(), 'id' => $publication->courseid,
+ 'huburl' => $publication->huburl, 'hubname' => $publication->hubname,
+ 'share' => !$publication->enrollable, 'advertise' => $publication->enrollable);
+ $updateurl = new moodle_url("/course/publish/metadata.php", $params);
+ $updatebutton = new single_button($updateurl, get_string('update', 'hub'));
+ $updatebutton->class = 'centeredbutton';
+ $updatebuttonhtml = $this->output->render($updatebutton);
+
+ $operations = $updatebuttonhtml . $brtag . $cancelbuttonhtml;
+ } else {
+ $operations = $cancelbuttonhtml;
+ }
+
+ $hubname = html_writer::tag('a',
+ $publication->hubname ? $publication->hubname : $publication->huburl,
+ array('href' => $publication->huburl));
+ //if the publication check time if bigger than May 2010, it has been checked
+ if ($publication->timechecked > 1273127954) {
+ if ($publication->status == 0) {
+ $status = get_string('statusunpublished', 'hub');
+ } else {
+ $status = get_string('statuspublished', 'hub');
+ }
+
+ $status .= $brtag . html_writer::tag('a', get_string('updatestatus', 'hub'),
+ array('href' => $CFG->wwwroot . '/course/publish/index.php?id='
+ . $courseid . "&updatestatusid=" . $publication->id
+ . "&sesskey=" . sesskey())) .
+ $brtag . get_string('lasttimechecked', 'hub') . ": "
+ . format_time(time() - $publication->timechecked);
+ } else {
+ $status = get_string('neverchecked', 'hub') . $brtag
+ . html_writer::tag('a', get_string('updatestatus', 'hub'),
+ array('href' => $CFG->wwwroot . '/course/publish/index.php?id='
+ . $courseid . "&updatestatusid=" . $publication->id
+ . "&sesskey=" . sesskey()));
+ }
+ //add button cells
+ $cells = array($publication->enrollable ?
+ get_string('advertised', 'hub') : get_string('shared', 'hub'),
+ $hubname, userdate($publication->timepublished,
+ get_string('strftimedatetimeshort')), $status, $operations);
+ $row = new html_table_row($cells);
+ $table->data[] = $row;
+ }
+
+ $contenthtml = html_writer::table($table);
+
+ return $contenthtml;
+ }
+
+ /**
+ * Display unpublishing confirmation page
+ * @param object $publication
+ * $publication->courseshortname
+ $publication->courseid
+ $publication->hubname
+ $publication->huburl
+ $publication->id
+ */
+ public function confirmunpublishing($publication) {
+ $optionsyes = array('sesskey' => sesskey(), 'id' => $publication->courseid,
+ 'hubcourseid' => $publication->hubcourseid,
+ 'huburl' => $publication->huburl, 'hubname' => $publication->hubname,
+ 'cancel' => true, 'publicationid' => $publication->id, 'confirm' => true);
+ $optionsno = array('sesskey' => sesskey(), 'id' => $publication->courseid);
+ $publication->hubname = html_writer::tag('a', $publication->hubname,
+ array('href' => $publication->huburl));
+ $formcontinue = new single_button(new moodle_url("/course/publish/index.php",
+ $optionsyes), get_string('unpublish', 'hub'), 'post');
+ $formcancel = new single_button(new moodle_url("/course/publish/index.php",
+ $optionsno), get_string('cancel'), 'get');
+ return $this->output->confirm(get_string('unpublishconfirmation', 'hub', $publication),
+ $formcontinue, $formcancel);
+ }
+
+ /**
+ * Display waiting information about backup size during uploading backup process
+ * @param object $backupfile the backup stored_file
+ * @return $html string
+ */
+ public function sendingbackupinfo($backupfile) {
+ $sizeinfo = new stdClass();
+ $sizeinfo->total = number_format($backupfile->get_filesize() / 1000000, 2);
+ $html = html_writer::tag('div', get_string('sendingsize', 'hub', $sizeinfo),
+ array('class' => 'courseuploadtextinfo'));
+ return $html;
+ }
+
+ /**
+ * Display upload successfull message and a button to the publish index page
+ * @param int $id the course id
+ * @param string $huburl the hub url where the course is published
+ * @param string $hubname the hub name where the course is published
+ * @return $html string
+ */
+ public function sentbackupinfo($id, $huburl, $hubname) {
+ $html = html_writer::tag('div', get_string('sent', 'hub'),
+ array('class' => 'courseuploadtextinfo'));
+ $publishindexurl = new moodle_url('/course/publish/index.php',
+ array('sesskey' => sesskey(), 'id' => $id,
+ 'published' => true, 'huburl' => $huburl, 'hubname' => $hubname));
+ $continue = $this->output->render(
+ new single_button($publishindexurl, get_string('continue', 'hub')));
+ $html .= html_writer::tag('div', $continue, array('class' => 'sharecoursecontinue'));
+ return $html;
+ }
+
+ /**
+ * Hub information (logo - name - description - link)
+ * @param object $hubinfo
+ * @return string html code
+ */
+ public function hubinfo($hubinfo) {
+ $params = array('filetype' => HUB_HUBSCREENSHOT_FILE_TYPE);
+ $imgurl = new moodle_url($hubinfo['url'] .
+ "/local/hub/webservice/download.php", $params);
+ $screenshothtml = html_writer::empty_tag('img',
+ array('src' => $imgurl, 'alt' => $hubinfo['name']));
+ $hubdescription = html_writer::tag('div', $screenshothtml,
+ array('class' => 'hubscreenshot'));
+
+ $hubdescription .= html_writer::tag('a', $hubinfo['name'],
+ array('class' => 'hublink', 'href' => $hubinfo['url'],
+ 'onclick' => 'this.target="_blank"'));
+
+ $hubdescription .= html_writer::tag('div', format_text($hubinfo['description'], FORMAT_PLAIN),
+ array('class' => 'hubdescription'));
+ $hubdescription = html_writer::tag('div', $hubdescription, array('class' => 'hubinfo'));
+
+ return $hubdescription;
+ }
+
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Display all recent activity in a flexible way
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require_once('../config.php');
+require_once('lib.php');
+require_once('recent_form.php');
+
+$id = required_param('id', PARAM_INT);
+
+$PAGE->set_url('/course/recent.php', array('id'=>$id));
+
+if (!$course = $DB->get_record('course', array('id'=>$id))) {
+ print_error("That's an invalid course id");
+}
+
+require_login($course);
+
+add_to_log($course->id, "course", "recent", "recent.php?id=$course->id", $course->id);
+
+$context = context_course::instance($course->id);
+
+$lastlogin = time() - COURSE_MAX_RECENT_PERIOD;
+if (!isguestuser() and !empty($USER->lastcourseaccess[$COURSE->id])) {
+ if ($USER->lastcourseaccess[$COURSE->id] > $lastlogin) {
+ $lastlogin = $USER->lastcourseaccess[$COURSE->id];
+ }
+}
+
+$param = new stdClass();
+$param->user = 0;
+$param->modid = 'all';
+$param->group = 0;
+$param->sortby = 'default';
+$param->date = $lastlogin;
+$param->id = $COURSE->id;
+
+$mform = new recent_form();
+$mform->set_data($param);
+if ($formdata = $mform->get_data()) {
+ $param = $formdata;
+}
+
+$userinfo = get_string('allparticipants');
+$dateinfo = get_string('alldays');
+
+if (!empty($param->user)) {
+ if (!$u = $DB->get_record('user', array('id'=>$param->user))) {
+ print_error("That's an invalid user!");
+ }
+ $userinfo = fullname($u);
+}
+
+$strrecentactivity = get_string('recentactivity');
+$PAGE->navbar->add($strrecentactivity, new moodle_url('/course/recent.php', array('id'=>$course->id)));
+$PAGE->navbar->add($userinfo);
+$PAGE->set_title("$course->shortname: $strrecentactivity");
+$PAGE->set_heading($course->fullname);
+echo $OUTPUT->header();
+echo $OUTPUT->heading(format_string($course->fullname) . ": $userinfo", 2);
+
+$mform->display();
+
+$modinfo = get_fast_modinfo($course);
+$modnames = get_module_types_names();
+
+if (has_capability('moodle/course:viewhiddensections', $context)) {
+ $hiddenfilter = "";
+} else {
+ $hiddenfilter = "AND cs.visible = 1";
+}
+$sections = array();
+foreach ($modinfo->get_section_info_all() as $i => $section) {
+ if (!empty($section->uservisible)) {
+ $sections[$i] = $section;
+ }
+}
+
+if ($param->modid === 'all') {
+ // ok
+
+} else if (strpos($param->modid, 'mod/') === 0) {
+ $modname = substr($param->modid, strlen('mod/'));
+ if (array_key_exists($modname, $modnames) and file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
+ $filter = $modname;
+ }
+
+} else if (strpos($param->modid, 'section/') === 0) {
+ $sectionid = substr($param->modid, strlen('section/'));
+ if (isset($sections[$sectionid])) {
+ $sections = array($sectionid=>$sections[$sectionid]);
+ }
+
+} else if (is_numeric($param->modid)) {
+ $sectionnum = $modinfo->cms[$param->modid]->sectionnum;
+ $filter_modid = $param->modid;
+ $sections = array($sectionnum => $sections[$sectionnum]);
+}
+
+
+$modinfo->get_groups(); // load all my groups and cache it in modinfo
+
+$activities = array();
+$index = 0;
+
+foreach ($sections as $sectionnum => $section) {
+
+ $activity = new stdClass();
+ $activity->type = 'section';
+ if ($section->section > 0) {
+ $activity->name = get_section_name($course, $section);
+ } else {
+ $activity->name = '';
+ }
+
+ $activity->visible = $section->visible;
+ $activities[$index++] = $activity;
+
+ if (empty($modinfo->sections[$sectionnum])) {
+ continue;
+ }
+
+ foreach ($modinfo->sections[$sectionnum] as $cmid) {
+ $cm = $modinfo->cms[$cmid];
+
+ if (!$cm->uservisible) {
+ continue;
+ }
+
+ if (!empty($filter) and $cm->modname != $filter) {
+ continue;
+ }
+
+ if (!empty($filter_modid) and $cmid != $filter_modid) {
+ continue;
+ }
+
+ $libfile = "$CFG->dirroot/mod/$cm->modname/lib.php";
+
+ if (file_exists($libfile)) {
+ require_once($libfile);
+ $get_recent_mod_activity = $cm->modname."_get_recent_mod_activity";
+
+ if (function_exists($get_recent_mod_activity)) {
+ $activity = new stdClass();
+ $activity->type = 'activity';
+ $activity->cmid = $cmid;
+ $activities[$index++] = $activity;
+ $get_recent_mod_activity($activities, $index, $param->date, $course->id, $cmid, $param->user, $param->group);
+ }
+ }
+ }
+}
+
+$detail = true;
+
+switch ($param->sortby) {
+ case 'datedesc' : usort($activities, 'compare_activities_by_time_desc'); break;
+ case 'dateasc' : usort($activities, 'compare_activities_by_time_asc'); break;
+ case 'default' :
+ default : $detail = false; $param->sortby = 'default';
+
+}
+
+if (!empty($activities)) {
+
+ $newsection = true;
+ $lastsection = '';
+ $newinstance = true;
+ $lastinstance = '';
+ $inbox = false;
+
+ $section = 0;
+
+ $activity_count = count($activities);
+ $viewfullnames = array();
+
+ foreach ($activities as $key => $activity) {
+
+ if ($activity->type == 'section') {
+ if ($param->sortby != 'default') {
+ continue; // no section if ordering by date
+ }
+ if ($activity_count == ($key + 1) or $activities[$key+1]->type == 'section') {
+ // peak at next activity. If it's another section, don't print this one!
+ // this means there are no activities in the current section
+ continue;
+ }
+ }
+
+ if (($activity->type == 'section') && ($param->sortby == 'default')) {
+ if ($inbox) {
+ echo $OUTPUT->box_end();
+ echo $OUTPUT->spacer(array('height'=>30, 'br'=>true)); // should be done with CSS instead
+ }
+ echo $OUTPUT->box_start();
+ if (!empty($activity->name)) {
+ echo html_writer::tag('h2', $activity->name);
+ }
+ $inbox = true;
+
+ } else if ($activity->type == 'activity') {
+
+ if ($param->sortby == 'default') {
+ $cm = $modinfo->cms[$activity->cmid];
+
+ if ($cm->visible) {
+ $class = '';
+ } else {
+ $class = 'dimmed';
+ }
+ $name = format_string($cm->name);
+ $modfullname = $modnames[$cm->modname];
+
+ $image = $OUTPUT->pix_icon('icon', $modfullname, $cm->modname, array('class' => 'icon smallicon'));
+ $link = html_writer::link(new moodle_url("/mod/$cm->modname/view.php",
+ array("id" => $cm->id)), $name, array('class' => $class));
+ echo html_writer::tag('h3', "$image $modfullname $link");
+ }
+
+ } else {
+
+ if (!isset($viewfullnames[$activity->cmid])) {
+ $cm_context = context_module::instance($activity->cmid);
+ $viewfullnames[$activity->cmid] = has_capability('moodle/site:viewfullnames', $cm_context);
+ }
+
+ if (!$inbox) {
+ echo $OUTPUT->box_start();
+ $inbox = true;
+ }
+
+ $print_recent_mod_activity = $activity->type.'_print_recent_mod_activity';
+
+ if (function_exists($print_recent_mod_activity)) {
+ $print_recent_mod_activity($activity, $course->id, $detail, $modnames, $viewfullnames[$activity->cmid]);
+ }
+ }
+ }
+
+ if ($inbox) {
+ echo $OUTPUT->box_end();
+ }
+
+
+} else {
+
+ echo html_writer::tag('h3', get_string('norecentactivity'), array('class' => 'mdl-align'));
+
+}
+
+echo $OUTPUT->footer();
+
+function compare_activities_by_time_desc($a, $b) {
+ // make sure the activities actually have a timestamp property
+ if ((!array_key_exists('timestamp', $a)) or (!array_key_exists('timestamp', $b))) {
+ return 0;
+ }
+ if ($a->timestamp == $b->timestamp)
+ return 0;
+ return ($a->timestamp > $b->timestamp) ? -1 : 1;
+}
+
+function compare_activities_by_time_asc($a, $b) {
+ // make sure the activities actually have a timestamp property
+ if ((!array_key_exists('timestamp', $a)) or (!array_key_exists('timestamp', $b))) {
+ return 0;
+ }
+ if ($a->timestamp == $b->timestamp)
+ return 0;
+ return ($a->timestamp < $b->timestamp) ? -1 : 1;
+}
+
--- /dev/null
+<?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/>.
+
+/**
+ * Display all recent activity in a flexible way
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+
+require_once($CFG->libdir.'/formslib.php');
+
+class recent_form extends moodleform {
+ function definition() {
+ global $CFG, $COURSE, $USER;
+
+ $mform =& $this->_form;
+ $context = context_course::instance($COURSE->id);
+ $modinfo = get_fast_modinfo($COURSE);
+
+ $mform->addElement('header', 'filters', get_string('managefilters')); //TODO: add better string
+
+ $groupoptions = array();
+ if (groups_get_course_groupmode($COURSE) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
+ // limited group access
+ $groups = groups_get_user_groups($COURSE->id);
+ $allgroups = groups_get_all_groups($COURSE->id);
+ if (!empty($groups[$COURSE->defaultgroupingid])) {
+ foreach ($groups[$COURSE->defaultgroupingid] AS $groupid) {
+ $groupoptions[$groupid] = format_string($allgroups[$groupid]->name, true, array('context'=>$context));
+ }
+ }
+ } else {
+ $groupoptions = array('0'=>get_string('allgroups'));
+ if (has_capability('moodle/site:accessallgroups', $context)) {
+ // user can see all groups
+ $allgroups = groups_get_all_groups($COURSE->id);
+ } else {
+ // user can see course level groups
+ $allgroups = groups_get_all_groups($COURSE->id, 0, $COURSE->defaultgroupingid);
+ }
+ foreach($allgroups as $group) {
+ $groupoptions[$group->id] = format_string($group->name, true, array('context'=>$context));
+ }
+ }
+
+ if ($COURSE->id == SITEID) {
+ $viewparticipants = has_capability('moodle/site:viewparticipants', context_system::instance());
+ } else {
+ $viewparticipants = has_capability('moodle/course:viewparticipants', $context);
+ }
+
+ if ($viewparticipants) {
+ $viewfullnames = has_capability('moodle/site:viewfullnames', context_course::instance($COURSE->id));
+
+ $options = array();
+ $options[0] = get_string('allparticipants');
+ $options[$CFG->siteguest] = get_string('guestuser');
+
+ if (isset($groupoptions[0])) {
+ // can see all enrolled users
+ if ($enrolled = get_enrolled_users($context, null, 0, user_picture::fields('u'))) {
+ foreach ($enrolled as $euser) {
+ $options[$euser->id] = fullname($euser, $viewfullnames);
+ }
+ }
+ } else {
+ // can see users from some groups only
+ foreach ($groupoptions as $groupid=>$unused) {
+ if ($enrolled = get_enrolled_users($context, null, $groupid, user_picture::fields('u'))) {
+ foreach ($enrolled as $euser) {
+ if (!array_key_exists($euser->id, $options)) {
+ $options[$euser->id] = fullname($euser, $viewfullnames);
+ }
+ }
+ }
+ }
+ }
+
+ $mform->addElement('select', 'user', get_string('participants'), $options);
+ $mform->setAdvanced('user');
+ } else {
+ // Default to no user.
+ $mform->addElement('hidden', 'user', 0);
+ }
+
+ $options = array(''=>get_string('allactivities'));
+ $modsused = array();
+
+ foreach($modinfo->cms as $cm) {
+ if (!$cm->uservisible) {
+ continue;
+ }
+ $modsused[$cm->modname] = true;
+ }
+
+ foreach ($modsused as $modname=>$unused) {
+ $libfile = "$CFG->dirroot/mod/$modname/lib.php";
+ if (!file_exists($libfile)) {
+ unset($modsused[$modname]);
+ continue;
+ }
+ include_once($libfile);
+ $libfunction = $modname."_get_recent_mod_activity";
+ if (!function_exists($libfunction)) {
+ unset($modsused[$modname]);
+ continue;
+ }
+ $options["mod/$modname"] = get_string('allmods', '', get_string('modulenameplural', $modname));
+ }
+
+ foreach ($modinfo->sections as $section=>$cmids) {
+ $options["section/$section"] = "-- ".get_section_name($COURSE, $section)." --";
+ foreach ($cmids as $cmid) {
+ $cm = $modinfo->cms[$cmid];
+ if (empty($modsused[$cm->modname]) or !$cm->uservisible) {
+ continue;
+ }
+ $options[$cm->id] = format_string($cm->name);
+ }
+ }
+ $mform->addElement('select', 'modid', get_string('activities'), $options);
+ $mform->setAdvanced('modid');
+
+
+ if ($groupoptions) {
+ $mform->addElement('select', 'group', get_string('groups'), $groupoptions);
+ $mform->setAdvanced('group');
+ } else {
+ // no access to groups in separate mode
+ $mform->addElement('hidden','group');
+ $mform->setType('group', PARAM_INT);
+ $mform->setConstants(array('group'=>-1));
+ }
+
+ $options = array('default' => get_string('bycourseorder'),
+ 'dateasc' => get_string('datemostrecentlast'),
+ 'datedesc' => get_string('datemostrecentfirst'));
+ $mform->addElement('select', 'sortby', get_string('sortby'), $options);
+ $mform->setAdvanced('sortby');
+
+ $mform->addElement('date_time_selector', 'date', get_string('since'), array('optional'=>true));
+
+ $mform->addElement('hidden','id');
+ $mform->setType('id', PARAM_INT);
+ $mform->setType('courseid', PARAM_INT);
+
+ $this->add_action_buttons(false, get_string('showrecent'));
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Renderer for use with the course section and all the goodness that falls
+ * within it.
+ *
+ * This renderer should contain methods useful to courses, and categories.
+ *
+ * @package moodlecore
+ * @copyright 2010 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * The core course renderer
+ *
+ * Can be retrieved with the following:
+ * $renderer = $PAGE->get_renderer('core','course');
+ */
+class core_course_renderer extends plugin_renderer_base {
+
+ /**
+ * A cache of strings
+ * @var stdClass
+ */
+ protected $strings;
+
+ /**
+ * Override the constructor so that we can initialise the string cache
+ *
+ * @param moodle_page $page
+ * @param string $target
+ */
+ public function __construct(moodle_page $page, $target) {
+ $this->strings = new stdClass;
+ parent::__construct($page, $target);
+ }
+
+ /**
+ * Renders course info box.
+ *
+ * @param stdClass $course
+ * @return string
+ */
+ public function course_info_box(stdClass $course) {
+ global $CFG;
+
+ $context = context_course::instance($course->id);
+
+ $content = '';
+ $content .= $this->output->box_start('generalbox info');
+
+ $summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', null);
+ $content .= format_text($summary, $course->summaryformat, array('overflowdiv'=>true), $course->id);
+ if (!empty($CFG->coursecontact)) {
+ $coursecontactroles = explode(',', $CFG->coursecontact);
+ foreach ($coursecontactroles as $roleid) {
+ if ($users = get_role_users($roleid, $context, true)) {
+ foreach ($users as $teacher) {
+ $role = new stdClass();
+ $role->id = $teacher->roleid;
+ $role->name = $teacher->rolename;
+ $role->shortname = $teacher->roleshortname;
+ $role->coursealias = $teacher->rolecoursealias;
+ $fullname = fullname($teacher, has_capability('moodle/site:viewfullnames', $context));
+ $namesarray[] = role_get_name($role, $context).': <a href="'.$CFG->wwwroot.'/user/view.php?id='.
+ $teacher->id.'&course='.SITEID.'">'.$fullname.'</a>';
+ }
+ }
+ }
+
+ if (!empty($namesarray)) {
+ $content .= "<ul class=\"teachers\">\n<li>";
+ $content .= implode('</li><li>', $namesarray);
+ $content .= "</li></ul>";
+ }
+ }
+
+ $content .= $this->output->box_end();
+
+ return $content;
+ }
+
+ /**
+ * Renderers a structured array of courses and categories into a nice
+ * XHTML tree structure.
+ *
+ * This method was designed initially to display the front page course/category
+ * combo view. The structure can be retrieved by get_course_category_tree()
+ *
+ * @param array $structure
+ * @return string
+ */
+ public function course_category_tree(array $structure) {
+ $this->strings->summary = get_string('summary');
+
+ // Generate an id and the required JS call to make this a nice widget
+ $id = html_writer::random_id('course_category_tree');
+ $this->page->requires->js_init_call('M.util.init_toggle_class_on_click', array($id, '.category.with_children .category_label', 'collapsed', '.category.with_children'));
+
+ // Start content generation
+ $content = html_writer::start_tag('div', array('class'=>'course_category_tree', 'id'=>$id));
+ foreach ($structure as $category) {
+ $content .= $this->course_category_tree_category($category);
+ }
+ $content .= html_writer::start_tag('div', array('class'=>'controls'));
+ $content .= html_writer::tag('div', get_string('collapseall'), array('class'=>'addtoall expandall'));
+ $content .= html_writer::tag('div', get_string('expandall'), array('class'=>'removefromall collapseall'));
+ $content .= html_writer::end_tag('div');
+ $content .= html_writer::end_tag('div');
+
+ // Return the course category tree HTML
+ return $content;
+ }
+
+ /**
+ * Renderers a category for use with course_category_tree
+ *
+ * @param array $category
+ * @param int $depth
+ * @return string
+ */
+ protected function course_category_tree_category(stdClass $category, $depth=1) {
+ $content = '';
+ $hassubcategories = (isset($category->categories) && count($category->categories)>0);
+ $hascourses = (isset($category->courses) && count($category->courses)>0);
+ $classes = array('category');
+ if ($category->parent != 0) {
+ $classes[] = 'subcategory';
+ }
+ if (empty($category->visible)) {
+ $classes[] = 'dimmed_category';
+ }
+ if ($hassubcategories || $hascourses) {
+ $classes[] = 'with_children';
+ if ($depth > 1) {
+ $classes[] = 'collapsed';
+ }
+ }
+ $categoryname = format_string($category->name, true, array('context' => context_coursecat::instance($category->id)));
+
+ $content .= html_writer::start_tag('div', array('class'=>join(' ', $classes)));
+ $content .= html_writer::start_tag('div', array('class'=>'category_label'));
+ $content .= html_writer::link(new moodle_url('/course/category.php', array('id'=>$category->id)), $categoryname, array('class'=>'category_link'));
+ $content .= html_writer::end_tag('div');
+ if ($hassubcategories) {
+ $content .= html_writer::start_tag('div', array('class'=>'subcategories'));
+ foreach ($category->categories as $subcategory) {
+ $content .= $this->course_category_tree_category($subcategory, $depth+1);
+ }
+ $content .= html_writer::end_tag('div');
+ }
+ if ($hascourses) {
+ $content .= html_writer::start_tag('div', array('class'=>'courses'));
+ $coursecount = 0;
+ $strinfo = new lang_string('info');
+ foreach ($category->courses as $course) {
+ $classes = array('course');
+ $linkclass = 'course_link';
+ if (!$course->visible) {
+ $linkclass .= ' dimmed';
+ }
+ $coursecount ++;
+ $classes[] = ($coursecount%2)?'odd':'even';
+ $content .= html_writer::start_tag('div', array('class'=>join(' ', $classes)));
+ $content .= html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($course->fullname), array('class'=>$linkclass));
+ $content .= html_writer::start_tag('div', array('class'=>'course_info clearfix'));
+
+ // print enrol info
+ if ($icons = enrol_get_course_info_icons($course)) {
+ foreach ($icons as $pix_icon) {
+ $content .= $this->render($pix_icon);
+ }
+ }
+
+ if ($course->summary) {
+ $url = new moodle_url('/course/info.php', array('id' => $course->id));
+ $image = html_writer::empty_tag('img', array('src'=>$this->output->pix_url('i/info'), 'alt'=>$this->strings->summary));
+ $content .= $this->action_link($url, $image, new popup_action('click', $url, 'courseinfo'), array('title' => $this->strings->summary));
+ }
+ $content .= html_writer::end_tag('div');
+ $content .= html_writer::end_tag('div');
+ }
+ $content .= html_writer::end_tag('div');
+ }
+ $content .= html_writer::end_tag('div');
+ return $content;
+ }
+
+ /**
+ * 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
+ */
+ public function course_modchooser($modules, $course) {
+ global $OUTPUT;
+
+ // Add the header
+ $header = html_writer::tag('div', get_string('addresourceoractivity', 'moodle'),
+ array('class' => 'hd choosertitle'));
+
+ $formcontent = html_writer::start_tag('form', array('action' => new moodle_url('/course/jumpto.php'),
+ 'id' => 'chooserform', 'method' => 'post'));
+ $formcontent .= html_writer::start_tag('div', array('id' => 'typeformdiv'));
+ $formcontent .= html_writer::tag('input', '', array('type' => 'hidden', 'id' => 'course',
+ 'name' => 'course', 'value' => $course->id));
+ $formcontent .= html_writer::tag('input', '',
+ array('type' => 'hidden', 'class' => 'jump', 'name' => 'jump', 'value' => ''));
+ $formcontent .= html_writer::tag('input', '', array('type' => 'hidden', 'name' => 'sesskey',
+ 'value' => sesskey()));
+ $formcontent .= html_writer::end_tag('div');
+
+ // Put everything into one tag 'options'
+ $formcontent .= html_writer::start_tag('div', array('class' => 'options'));
+ $formcontent .= html_writer::tag('div', get_string('selectmoduletoviewhelp', 'moodle'),
+ array('class' => 'instruction'));
+ // Put all options into one tag 'alloptions' to allow us to handle scrolling
+ $formcontent .= html_writer::start_tag('div', array('class' => 'alloptions'));
+
+ // Activities
+ $activities = array_filter($modules, function($mod) {
+ return ($mod->archetype !== MOD_ARCHETYPE_RESOURCE && $mod->archetype !== MOD_ARCHETYPE_SYSTEM);
+ });
+ if (count($activities)) {
+ $formcontent .= $this->course_modchooser_title('activities');
+ $formcontent .= $this->course_modchooser_module_types($activities);
+ }
+
+ // Resources
+ $resources = array_filter($modules, function($mod) {
+ return ($mod->archetype === MOD_ARCHETYPE_RESOURCE);
+ });
+ if (count($resources)) {
+ $formcontent .= $this->course_modchooser_title('resources');
+ $formcontent .= $this->course_modchooser_module_types($resources);
+ }
+
+ $formcontent .= html_writer::end_tag('div'); // modoptions
+ $formcontent .= html_writer::end_tag('div'); // types
+
+ $formcontent .= html_writer::start_tag('div', array('class' => 'submitbuttons'));
+ $formcontent .= html_writer::tag('input', '',
+ array('type' => 'submit', 'name' => 'submitbutton', 'class' => 'submitbutton', 'value' => get_string('add')));
+ $formcontent .= html_writer::tag('input', '',
+ array('type' => 'submit', 'name' => 'addcancel', 'class' => 'addcancel', 'value' => get_string('cancel')));
+ $formcontent .= html_writer::end_tag('div');
+ $formcontent .= html_writer::end_tag('form');
+
+ // Wrap the whole form in a div
+ $formcontent = html_writer::tag('div', $formcontent, array('id' => 'chooseform'));
+
+ // Put all of the content together
+ $content = $formcontent;
+
+ $content = html_writer::tag('div', $content, array('class' => 'choosercontainer'));
+ return $header . html_writer::tag('div', $content, array('class' => 'chooserdialoguebody'));
+ }
+
+ /**
+ * Build the HTML for a specified set of modules
+ *
+ * @param array $modules A set of modules as used by the
+ * course_modchooser_module function
+ * @return string The composed HTML for the module
+ */
+ protected function course_modchooser_module_types($modules) {
+ $return = '';
+ foreach ($modules as $module) {
+ if (!isset($module->types)) {
+ $return .= $this->course_modchooser_module($module);
+ } else {
+ $return .= $this->course_modchooser_module($module, array('nonoption'));
+ foreach ($module->types as $type) {
+ $return .= $this->course_modchooser_module($type, array('option', 'subtype'));
+ }
+ }
+ }
+ return $return;
+ }
+
+ /**
+ * Return the HTML for the specified module adding any required classes
+ *
+ * @param object $module An object containing the title, and link. An
+ * icon, and help text may optionally be specified. If the module
+ * contains subtypes in the types option, then these will also be
+ * displayed.
+ * @param array $classes Additional classes to add to the encompassing
+ * div element
+ * @return string The composed HTML for the module
+ */
+ protected function course_modchooser_module($module, $classes = array('option')) {
+ $output = '';
+ $output .= html_writer::start_tag('div', array('class' => implode(' ', $classes)));
+ $output .= html_writer::start_tag('label', array('for' => 'module_' . $module->name));
+ if (!isset($module->types)) {
+ $output .= html_writer::tag('input', '', array('type' => 'radio',
+ 'name' => 'jumplink', 'id' => 'module_' . $module->name, 'value' => $module->link));
+ }
+
+ $output .= html_writer::start_tag('span', array('class' => 'modicon'));
+ if (isset($module->icon)) {
+ // Add an icon if we have one
+ $output .= $module->icon;
+ }
+ $output .= html_writer::end_tag('span');
+
+ $output .= html_writer::tag('span', $module->title, array('class' => 'typename'));
+ if (!isset($module->help)) {
+ // Add help if found
+ $module->help = get_string('nohelpforactivityorresource', 'moodle');
+ }
+
+ // Format the help text using markdown with the following options
+ $options = new stdClass();
+ $options->trusted = false;
+ $options->noclean = false;
+ $options->smiley = false;
+ $options->filter = false;
+ $options->para = true;
+ $options->newlines = false;
+ $options->overflowdiv = false;
+ $module->help = format_text($module->help, FORMAT_MARKDOWN, $options);
+ $output .= html_writer::tag('span', $module->help, array('class' => 'typesummary'));
+ $output .= html_writer::end_tag('label');
+ $output .= html_writer::end_tag('div');
+
+ return $output;
+ }
+
+ protected function course_modchooser_title($title, $identifier = null) {
+ $module = new stdClass();
+ $module->name = $title;
+ $module->types = array();
+ $module->title = get_string($title, $identifier);
+ $module->help = '';
+ return $this->course_modchooser_module($module, array('moduletypetitle'));
+ }
+}
--- /dev/null
+<?php
+ // Display all the interfaces for importing data into a specific course
+
+ require_once('../config.php');
+
+ $id = required_param('id', PARAM_INT); // course id to import TO
+ $course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+
+ $PAGE->set_pagelayout('standard');
+ require_login($course);
+
+ $context = context_course::instance($course->id);
+ require_capability('moodle/site:viewreports', $context); // basic capability for listing of reports
+
+ $strreports = get_string('reports');
+
+ $PAGE->set_url(new moodle_url('/course/report.php', array('id'=>$id)));
+ $PAGE->set_title($course->fullname.': '.$strreports);
+ $PAGE->set_heading($course->fullname.': '.$strreports);
+ echo $OUTPUT->header();
+
+ $reports = get_plugin_list('coursereport');
+
+ foreach ($reports as $report => $reportdirectory) {
+ $pluginfile = $reportdirectory.'/mod.php';
+ if (file_exists($pluginfile)) {
+ ob_start();
+ include($pluginfile); // Fragment for listing
+ $html = ob_get_contents();
+ ob_end_clean();
+ // add div only if plugin accessible
+ if ($html !== '') {
+ echo '<div class="plugin">';
+ echo $html;
+ echo '</div>';
+ }
+ }
+ }
+
+ echo $OUTPUT->footer();
+
--- /dev/null
+<?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/>.
+
+/**
+ * This file contains functions used by course reports
+ *
+ * @since 2.1
+ * @package course-report
+ * @copyright 2011 Andrew Davis
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function coursereport_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype')
+ );
+ return $array;
+}
\ No newline at end of file
--- /dev/null
+<?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/>.
+
+/**
+ * Allows a user to request a course be created for them.
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require_once(dirname(__FILE__) . '/../config.php');
+require_once($CFG->dirroot . '/course/lib.php');
+require_once($CFG->dirroot . '/course/request_form.php');
+
+$PAGE->set_url('/course/request.php');
+
+/// Where we came from. Used in a number of redirects.
+$returnurl = $CFG->wwwroot . '/course/index.php';
+
+
+/// Check permissions.
+require_login();
+if (isguestuser()) {
+ print_error('guestsarenotallowed', '', $returnurl);
+}
+if (empty($CFG->enablecourserequests)) {
+ print_error('courserequestdisabled', '', $returnurl);
+}
+$context = context_system::instance();
+$PAGE->set_context($context);
+require_capability('moodle/course:request', $context);
+
+/// Set up the form.
+$data = course_request::prepare();
+$requestform = new course_request_form($CFG->wwwroot . '/course/request.php', compact('editoroptions'));
+$requestform->set_data($data);
+
+$strtitle = get_string('courserequest');
+$PAGE->set_title($strtitle);
+$PAGE->set_heading($strtitle);
+
+/// Standard form processing if statement.
+if ($requestform->is_cancelled()){
+ redirect($returnurl);
+
+} else if ($data = $requestform->get_data()) {
+ $request = course_request::create($data);
+
+ // and redirect back to the course listing.
+ notice(get_string('courserequestsuccess'), $returnurl);
+}
+
+$PAGE->navbar->add($strtitle);
+echo $OUTPUT->header();
+echo $OUTPUT->heading($strtitle);
+// Show the request form.
+$requestform->display();
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// NOTICE OF COPYRIGHT //
+// //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment //
+// http://moodle.org //
+// //
+// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
+// //
+// This program 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 2 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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: //
+// //
+// http://www.gnu.org/copyleft/gpl.html //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * Forms associated with requesting courses, and having requests approved.
+ * Note that several related forms are defined in this one file.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package course
+ */
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+
+require_once($CFG->libdir.'/formslib.php');
+
+/**
+ * A form for a user to request a course.
+ */
+class course_request_form extends moodleform {
+ function definition() {
+ global $CFG, $DB, $USER;
+
+ $mform =& $this->_form;
+
+ if ($pending = $DB->get_records('course_request', array('requester' => $USER->id))) {
+ $mform->addElement('header', 'pendinglist', get_string('coursespending'));
+ $list = array();
+ foreach ($pending as $cp) {
+ $list[] = format_string($cp->fullname);
+ }
+ $list = implode(', ', $list);
+ $mform->addElement('static', 'pendingcourses', get_string('courses'), $list);
+ }
+
+ $mform->addElement('header','coursedetails', get_string('courserequestdetails'));
+
+ $mform->addElement('text', 'fullname', get_string('fullnamecourse'), 'maxlength="254" size="50"');
+ $mform->addHelpButton('fullname', 'fullnamecourse');
+ $mform->addRule('fullname', get_string('missingfullname'), 'required', null, 'client');
+ $mform->setType('fullname', PARAM_TEXT);
+
+ $mform->addElement('text', 'shortname', get_string('shortnamecourse'), 'maxlength="100" size="20"');
+ $mform->addHelpButton('shortname', 'shortnamecourse');
+ $mform->addRule('shortname', get_string('missingshortname'), 'required', null, 'client');
+ $mform->setType('shortname', PARAM_TEXT);
+
+ if (!empty($CFG->requestcategoryselection)) {
+ $displaylist = array();
+ $parentlist = array();
+ make_categories_list($displaylist, $parentlist, '');
+ $mform->addElement('select', 'category', get_string('category'), $displaylist);
+ $mform->setDefault('category', $CFG->defaultrequestcategory);
+ $mform->addHelpButton('category', 'category');
+ }
+
+ $mform->addElement('editor', 'summary_editor', get_string('summary'), null, course_request::summary_editor_options());
+ $mform->addHelpButton('summary_editor', 'coursesummary');
+ $mform->setType('summary_editor', PARAM_RAW);
+
+ $mform->addElement('header','requestreason', get_string('courserequestreason'));
+
+ $mform->addElement('textarea', 'reason', get_string('courserequestsupport'), array('rows'=>'15', 'cols'=>'50'));
+ $mform->addRule('reason', get_string('missingreqreason'), 'required', null, 'client');
+ $mform->setType('reason', PARAM_TEXT);
+
+ $this->add_action_buttons(true, get_string('requestcourse'));
+ }
+
+ function validation($data, $files) {
+ global $DB;
+
+ $errors = parent::validation($data, $files);
+ $foundcourses = null;
+ $foundreqcourses = null;
+
+ if (!empty($data['shortname'])) {
+ $foundcourses = $DB->get_records('course', array('shortname'=>$data['shortname']));
+ $foundreqcourses = $DB->get_records('course_request', array('shortname'=>$data['shortname']));
+ }
+ if (!empty($foundreqcourses)) {
+ if (!empty($foundcourses)) {
+ $foundcourses = array_merge($foundcourses, $foundreqcourses);
+ } else {
+ $foundcourses = $foundreqcourses;
+ }
+ }
+
+ if (!empty($foundcourses)) {
+ foreach ($foundcourses as $foundcourse) {
+ if (!empty($foundcourse->requester)) {
+ $pending = 1;
+ $foundcoursenames[] = $foundcourse->fullname.' [*]';
+ } else {
+ $foundcoursenames[] = $foundcourse->fullname;
+ }
+ }
+ $foundcoursenamestring = implode(',', $foundcoursenames);
+
+ $errors['shortname'] = get_string('shortnametaken', '', $foundcoursenamestring);
+ if (!empty($pending)) {
+ $errors['shortname'] .= get_string('starpending');
+ }
+ }
+
+ return $errors;
+ }
+}
+
+/**
+ * A form for an administrator to reject a course request.
+ */
+class reject_request_form extends moodleform {
+ function definition() {
+ $mform =& $this->_form;
+
+ $mform->addElement('hidden', 'reject', 0);
+ $mform->setType('reject', PARAM_INT);
+
+ $mform->addElement('header','coursedetails', get_string('coursereasonforrejecting'));
+
+ $mform->addElement('textarea', 'rejectnotice', get_string('coursereasonforrejectingemail'), array('rows'=>'15', 'cols'=>'50'));
+ $mform->addRule('rejectnotice', get_string('missingreqreason'), 'required', null, 'client');
+ $mform->setType('rejectnotice', PARAM_TEXT);
+
+ $this->add_action_buttons(true, get_string('reject'));
+ }
+}
+
--- /dev/null
+<?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/>.
+
+/**
+ * The purpose of this feature is to quickly remove all user related data from a course
+ * in order to make it available for a new semester. This feature can handle the removal
+ * of general course data like students, teachers, logs, events and groups as well as module
+ * specific data. Each module must be modified to take advantage of this new feature.
+ * The feature will also reset the start date of the course if necessary.
+ *
+ * @copyright Mark Flach and moodle.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require('../config.php');
+require_once('reset_form.php');
+
+$id = required_param('id', PARAM_INT);
+
+if (!$course = $DB->get_record('course', array('id'=>$id))) {
+ print_error("invalidcourseid");
+}
+
+$PAGE->set_url('/course/reset.php', array('id'=>$id));
+
+require_login($course);
+require_capability('moodle/course:reset', context_course::instance($course->id));
+
+$strreset = get_string('reset');
+$strresetcourse = get_string('resetcourse');
+$strremove = get_string('remove');
+
+$PAGE->navbar->add($strresetcourse);
+$PAGE->set_title($course->fullname.': '.$strresetcourse);
+$PAGE->set_heading($course->fullname.': '.$strresetcourse);
+
+$mform = new course_reset_form();
+
+if ($mform->is_cancelled()) {
+ redirect($CFG->wwwroot.'/course/view.php?id='.$id);
+
+} else if ($data = $mform->get_data()) { // no magic quotes
+
+ if (isset($data->selectdefault)) {
+ $_POST = array();
+ $mform = new course_reset_form();
+ $mform->load_defaults();
+
+ } else if (isset($data->deselectall)) {
+ $_POST = array();
+ $mform = new course_reset_form();
+
+ } else {
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading($strresetcourse);
+
+ $data->reset_start_date_old = $course->startdate;
+ $status = reset_course_userdata($data);
+
+ $data = array();;
+ foreach ($status as $item) {
+ $line = array();
+ $line[] = $item['component'];
+ $line[] = $item['item'];
+ $line[] = ($item['error']===false) ? get_string('ok') : '<div class="notifyproblem">'.$item['error'].'</div>';
+ $data[] = $line;
+ }
+
+ $table = new html_table();
+ $table->head = array(get_string('resetcomponent'), get_string('resettask'), get_string('resetstatus'));
+ $table->size = array('20%', '40%', '40%');
+ $table->align = array('left', 'left', 'left');
+ $table->width = '80%';
+ $table->data = $data;
+ echo html_writer::table($table);
+
+ echo $OUTPUT->continue_button('view.php?id='.$course->id); // Back to course page
+ echo $OUTPUT->footer();
+ exit;
+ }
+}
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($strresetcourse);
+
+echo $OUTPUT->box(get_string('resetinfo'));
+
+$mform->display();
+echo $OUTPUT->footer();
+
+
--- /dev/null
+<?php
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+
+require_once $CFG->libdir.'/formslib.php';
+
+class course_reset_form extends moodleform {
+ function definition (){
+ global $CFG, $COURSE, $DB;
+
+ $mform =& $this->_form;
+
+ $mform->addElement('header', 'generalheader', get_string('general'));
+
+ $mform->addElement('date_selector', 'reset_start_date', get_string('startdate'), array('optional'=>true));
+ $mform->addHelpButton('reset_start_date', 'startdate');
+ $mform->addElement('checkbox', 'reset_events', get_string('deleteevents', 'calendar'));
+ $mform->addElement('checkbox', 'reset_logs', get_string('deletelogs'));
+ $mform->addElement('checkbox', 'reset_notes', get_string('deletenotes', 'notes'));
+ $mform->addElement('checkbox', 'reset_comments', get_string('deleteallcomments', 'moodle'));
+ $mform->addElement('checkbox', 'reset_completion', get_string('deletecompletiondata', 'completion'));
+ $mform->addElement('checkbox', 'delete_blog_associations', get_string('deleteblogassociations', 'blog'));
+ $mform->addHelpButton('delete_blog_associations', 'deleteblogassociations', 'blog');
+
+
+ $mform->addElement('header', 'rolesheader', get_string('roles'));
+
+ $roles = get_assignable_roles(context_course::instance($COURSE->id));
+ $roles[0] = get_string('noroles', 'role');
+ $roles = array_reverse($roles, true);
+
+ $mform->addElement('select', 'unenrol_users', get_string('unenrolroleusers', 'enrol'), $roles, array('multiple' => 'multiple'));
+ $mform->addElement('checkbox', 'reset_roles_overrides', get_string('deletecourseoverrides', 'role'));
+ $mform->setAdvanced('reset_roles_overrides');
+ $mform->addElement('checkbox', 'reset_roles_local', get_string('deletelocalroles', 'role'));
+
+
+ $mform->addElement('header', 'gradebookheader', get_string('gradebook', 'grades'));
+
+ $mform->addElement('checkbox', 'reset_gradebook_items', get_string('removeallcourseitems', 'grades'));
+ $mform->addElement('checkbox', 'reset_gradebook_grades', get_string('removeallcoursegrades', 'grades'));
+ $mform->disabledIf('reset_gradebook_grades', 'reset_gradebook_items', 'checked');
+
+
+ $mform->addElement('header', 'groupheader', get_string('groups'));
+
+ $mform->addElement('checkbox', 'reset_groups_remove', get_string('deleteallgroups', 'group'));
+ $mform->setAdvanced('reset_groups_remove');
+ $mform->addElement('checkbox', 'reset_groups_members', get_string('removegroupsmembers', 'group'));
+ $mform->setAdvanced('reset_groups_members');
+ $mform->disabledIf('reset_groups_members', 'reset_groups_remove', 'checked');
+
+ $mform->addElement('checkbox', 'reset_groupings_remove', get_string('deleteallgroupings', 'group'));
+ $mform->setAdvanced('reset_groupings_remove');
+ $mform->addElement('checkbox', 'reset_groupings_members', get_string('removegroupingsmembers', 'group'));
+ $mform->setAdvanced('reset_groupings_members');
+ $mform->disabledIf('reset_groupings_members', 'reset_groupings_remove', 'checked');
+
+ $unsupported_mods = array();
+ if ($allmods = $DB->get_records('modules') ) {
+ foreach ($allmods as $mod) {
+ $modname = $mod->name;
+ $modfile = $CFG->dirroot."/mod/$modname/lib.php";
+ $mod_reset_course_form_definition = $modname.'_reset_course_form_definition';
+ $mod_reset__userdata = $modname.'_reset_userdata';
+ if (file_exists($modfile)) {
+ if (!$DB->count_records($modname, array('course'=>$COURSE->id))) {
+ continue; // Skip mods with no instances
+ }
+ include_once($modfile);
+ if (function_exists($mod_reset_course_form_definition)) {
+ $mod_reset_course_form_definition($mform);
+ } else if (!function_exists($mod_reset__userdata)) {
+ $unsupported_mods[] = $mod;
+ }
+ } else {
+ debugging('Missing lib.php in '.$modname.' module');
+ }
+ }
+ }
+ // mention unsupported mods
+ if (!empty($unsupported_mods)) {
+ $mform->addElement('header', 'unsupportedheader', get_string('resetnotimplemented'));
+ foreach($unsupported_mods as $mod) {
+ $mform->addElement('static', 'unsup'.$mod->name, get_string('modulenameplural', $mod->name));
+ $mform->setAdvanced('unsup'.$mod->name);
+ }
+ }
+
+ $mform->addElement('hidden', 'id', $COURSE->id);
+ $mform->setType('id', PARAM_INT);
+
+ $buttonarray = array();
+ $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('resetcourse'));
+ $buttonarray[] = &$mform->createElement('submit', 'selectdefault', get_string('selectdefault'));
+ $buttonarray[] = &$mform->createElement('submit', 'deselectall', get_string('deselectall'));
+ $buttonarray[] = &$mform->createElement('cancel');
+ $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
+ $mform->closeHeaderBefore('buttonar');
+ }
+
+ function load_defaults() {
+ global $CFG, $COURSE, $DB;
+
+ $mform =& $this->_form;
+
+ $defaults = array ('reset_events'=>1, 'reset_logs'=>1, 'reset_roles_local'=>1, 'reset_gradebook_grades'=>1, 'reset_notes'=>1);
+
+ // Set student as default in unenrol user list, if role with student archetype exist.
+ if ($studentrole = get_archetype_roles('student')) {
+ $defaults['unenrol_users'] = array_keys($studentrole);
+ }
+
+ if ($allmods = $DB->get_records('modules') ) {
+ foreach ($allmods as $mod) {
+ $modname = $mod->name;
+ $modfile = $CFG->dirroot."/mod/$modname/lib.php";
+ $mod_reset_course_form_defaults = $modname.'_reset_course_form_defaults';
+ if (file_exists($modfile)) {
+ @include_once($modfile);
+ if (function_exists($mod_reset_course_form_defaults)) {
+ if ($moddefs = $mod_reset_course_form_defaults($COURSE)) {
+ $defaults = $defaults + $moddefs;
+ }
+ }
+ }
+ }
+ }
+
+ foreach ($defaults as $element=>$default) {
+ $mform->setDefault($element, $default);
+ }
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * List of all resource type modules in course
+ *
+ * @package moodlecore
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once("$CFG->libdir/resourcelib.php");
+
+$id = required_param('id', PARAM_INT); // course id
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$PAGE->set_pagelayout('course');
+require_course_login($course, true);
+
+// get list of all resource-like modules
+$allmodules = $DB->get_records('modules', array('visible'=>1));
+$modules = array();
+foreach ($allmodules as $key=>$module) {
+ $modname = $module->name;
+ $libfile = "$CFG->dirroot/mod/$modname/lib.php";
+ if (!file_exists($libfile)) {
+ continue;
+ }
+ $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
+ if ($archetype != MOD_ARCHETYPE_RESOURCE) {
+ continue;
+ }
+
+ $modules[$modname] = get_string('modulename', $modname);
+ //some hacky nasic logging
+ add_to_log($course->id, $modname, 'view all', "index.php?id=$course->id", '');
+}
+
+$strresources = get_string('resources');
+$strsectionname = get_string('sectionname', 'format_'.$course->format);
+$strname = get_string('name');
+$strintro = get_string('moduleintro');
+$strlastmodified = get_string('lastmodified');
+
+$PAGE->set_url('/course/resources.php', array('id' => $course->id));
+$PAGE->set_title($course->shortname.': '.$strresources);
+$PAGE->set_heading($course->fullname);
+$PAGE->navbar->add($strresources);
+echo $OUTPUT->header();
+
+$modinfo = get_fast_modinfo($course);
+$usesections = course_format_uses_sections($course->format);
+$cms = array();
+$resources = array();
+foreach ($modinfo->cms as $cm) {
+ if (!$cm->uservisible) {
+ continue;
+ }
+ if (!array_key_exists($cm->modname, $modules)) {
+ continue;
+ }
+ if (!$cm->has_view()) {
+ // Exclude label and similar
+ continue;
+ }
+ $cms[$cm->id] = $cm;
+ $resources[$cm->modname][] = $cm->instance;
+}
+
+// preload instances
+foreach ($resources as $modname=>$instances) {
+ $resources[$modname] = $DB->get_records_list($modname, 'id', $instances, 'id', 'id,name,intro,introformat,timemodified');
+}
+
+if (!$cms) {
+ notice(get_string('thereareno', 'moodle', $strresources), "$CFG->wwwroot/course/view.php?id=$course->id");
+ exit;
+}
+
+$table = new html_table();
+$table->attributes['class'] = 'generaltable mod_index';
+
+if ($usesections) {
+ $table->head = array ($strsectionname, $strname, $strintro);
+ $table->align = array ('center', 'left', 'left');
+} else {
+ $table->head = array ($strlastmodified, $strname, $strintro);
+ $table->align = array ('left', 'left', 'left');
+}
+
+$currentsection = '';
+foreach ($cms as $cm) {
+ if (!isset($resources[$cm->modname][$cm->instance])) {
+ continue;
+ }
+ $resource = $resources[$cm->modname][$cm->instance];
+ if ($usesections) {
+ $printsection = '';
+ if ($cm->sectionnum !== $currentsection) {
+ if ($cm->sectionnum) {
+ $printsection = get_section_name($course, $cm->sectionnum);
+ }
+ if ($currentsection !== '') {
+ $table->data[] = 'hr';
+ }
+ $currentsection = $cm->sectionnum;
+ }
+ } else {
+ $printsection = '<span class="smallinfo">'.userdate($resource->timemodified)."</span>";
+ }
+
+ $extra = empty($cm->extra) ? '' : $cm->extra;
+ if (!empty($cm->icon)) {
+ $icon = '<img src="'.$OUTPUT->pix_url($cm->icon).'" class="activityicon" alt="'.get_string('modulename', $cm->modname).'" /> ';
+ } else {
+ $icon = '<img src="'.$OUTPUT->pix_url('icon', $cm->modname).'" class="activityicon" alt="'.get_string('modulename', $cm->modname).'" /> ';
+ }
+
+ $class = $cm->visible ? '' : 'class="dimmed"'; // hidden modules are dimmed
+ $table->data[] = array (
+ $printsection,
+ "<a $class $extra href=\"$CFG->wwwroot/mod/$cm->modname/view.php?id=$cm->id\">".$icon.format_string($resource->name)."</a>",
+ format_module_intro('resource', $resource, $cm->id));
+}
+
+echo html_writer::table($table);
+
+echo $OUTPUT->footer();
--- /dev/null
+<?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/>.
+
+/**
+ * Provide interface for topics AJAX course formats
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+if (!defined('AJAX_SCRIPT')) {
+ define('AJAX_SCRIPT', true);
+}
+require_once(dirname(__FILE__) . '/../config.php');
+require_once($CFG->dirroot.'/course/lib.php');
+
+// Initialise ALL the incoming parameters here, up front.
+$courseid = required_param('courseId', PARAM_INT);
+$class = required_param('class', PARAM_ALPHA);
+$field = optional_param('field', '', PARAM_ALPHA);
+$instanceid = optional_param('instanceId', 0, PARAM_INT);
+$sectionid = optional_param('sectionId', 0, PARAM_INT);
+$beforeid = optional_param('beforeId', 0, PARAM_INT);
+$value = optional_param('value', 0, PARAM_INT);
+$column = optional_param('column', 0, PARAM_ALPHA);
+$id = optional_param('id', 0, PARAM_INT);
+$summary = optional_param('summary', '', PARAM_RAW);
+$sequence = optional_param('sequence', '', PARAM_SEQUENCE);
+$visible = optional_param('visible', 0, PARAM_INT);
+$pageaction = optional_param('action', '', PARAM_ALPHA); // Used to simulate a DELETE command
+$title = optional_param('title', '', PARAM_TEXT);
+
+$PAGE->set_url('/course/rest.php', array('courseId'=>$courseid,'class'=>$class));
+
+//NOTE: when making any changes here please make sure it is using the same access control as course/mod.php !!
+
+$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
+// Check user is logged in and set contexts if we are dealing with resource
+if (in_array($class, array('resource'))) {
+ $cm = get_coursemodule_from_id(null, $id, $course->id, false, MUST_EXIST);
+ require_login($course, false, $cm);
+ $modcontext = context_module::instance($cm->id);
+} else {
+ require_login($course);
+}
+$coursecontext = context_course::instance($course->id);
+require_sesskey();
+
+echo $OUTPUT->header(); // send headers
+
+// OK, now let's process the parameters and do stuff
+// MDL-10221 the DELETE method is not allowed on some web servers, so we simulate it with the action URL param
+$requestmethod = $_SERVER['REQUEST_METHOD'];
+if ($pageaction == 'DELETE') {
+ $requestmethod = 'DELETE';
+}
+
+switch($requestmethod) {
+ case 'POST':
+
+ switch ($class) {
+ case 'section':
+
+ if (!$DB->record_exists('course_sections', array('course'=>$course->id, 'section'=>$id))) {
+ throw new moodle_exception('AJAX commands.php: Bad Section ID '.$id);
+ }
+
+ switch ($field) {
+ case 'visible':
+ require_capability('moodle/course:sectionvisibility', $coursecontext);
+ $resourcestotoggle = set_section_visible($course->id, $id, $value);
+ echo json_encode(array('resourcestotoggle' => $resourcestotoggle));
+ break;
+
+ case 'move':
+ require_capability('moodle/course:movesections', $coursecontext);
+ move_section_to($course, $id, $value);
+ // See if format wants to do something about it
+ $response = course_get_format($course)->ajax_section_move();
+ if ($response !== null) {
+ echo json_encode($response);
+ }
+ break;
+ }
+ break;
+
+ case 'resource':
+ switch ($field) {
+ case 'visible':
+ require_capability('moodle/course:activityvisibility', $modcontext);
+ set_coursemodule_visible($cm->id, $value);
+ break;
+
+ case 'groupmode':
+ require_capability('moodle/course:manageactivities', $modcontext);
+ set_coursemodule_groupmode($cm->id, $value);
+ break;
+
+ case 'indent':
+ require_capability('moodle/course:manageactivities', $modcontext);
+ $cm->indent = $value;
+ if ($cm->indent >= 0) {
+ $DB->update_record('course_modules', $cm);
+ rebuild_course_cache($cm->course);
+ }
+ break;
+
+ case 'move':
+ require_capability('moodle/course:manageactivities', $modcontext);
+ if (!$section = $DB->get_record('course_sections', array('course'=>$course->id, 'section'=>$sectionid))) {
+ throw new moodle_exception('AJAX commands.php: Bad section ID '.$sectionid);
+ }
+
+ if ($beforeid > 0){
+ $beforemod = get_coursemodule_from_id('', $beforeid, $course->id);
+ $beforemod = $DB->get_record('course_modules', array('id'=>$beforeid));
+ } else {
+ $beforemod = NULL;
+ }
+
+ moveto_module($cm, $section, $beforemod);
+ echo json_encode(array('visible' => $cm->visible));
+ break;
+ case 'gettitle':
+ require_capability('moodle/course:manageactivities', $modcontext);
+ $cm = get_coursemodule_from_id('', $id, 0, false, MUST_EXIST);
+ $module = new stdClass();
+ $module->id = $cm->instance;
+
+ // Don't pass edit strings through multilang filters - we need the entire string
+ echo json_encode(array('instancename' => $cm->name));
+ break;
+ case 'updatetitle':
+ require_capability('moodle/course:manageactivities', $modcontext);
+ require_once($CFG->libdir . '/gradelib.php');
+ $cm = get_coursemodule_from_id('', $id, 0, false, MUST_EXIST);
+ $module = new stdClass();
+ $module->id = $cm->instance;
+
+ // Escape strings as they would be by mform
+ if (!empty($CFG->formatstringstriptags)) {
+ $module->name = clean_param($title, PARAM_TEXT);
+ } else {
+ $module->name = clean_param($title, PARAM_CLEANHTML);
+ }
+
+ if (!empty($module->name)) {
+ $DB->update_record($cm->modname, $module);
+ rebuild_course_cache($cm->course);
+ } else {
+ $module->name = $cm->name;
+ }
+
+ // Attempt to update the grade item if relevant
+ $grademodule = $DB->get_record($cm->modname, array('id' => $cm->instance));
+ $grademodule->cmidnumber = $cm->idnumber;
+ $grademodule->modname = $cm->modname;
+ grade_update_mod_grades($grademodule);
+
+ // We need to return strings after they've been through filters for multilang
+ $stringoptions = new stdClass;
+ $stringoptions->context = $coursecontext;
+ echo json_encode(array('instancename' => html_entity_decode(format_string($module->name, true, $stringoptions))));
+ break;
+ }
+ break;
+
+ case 'course':
+ switch($field) {
+ case 'marker':
+ require_capability('moodle/course:setcurrentsection', $coursecontext);
+ course_set_marker($course->id, $value);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 'DELETE':
+ switch ($class) {
+ case 'resource':
+ require_capability('moodle/course:manageactivities', $modcontext);
+ $modlib = "$CFG->dirroot/mod/$cm->modname/lib.php";
+
+ if (file_exists($modlib)) {
+ include_once($modlib);
+ } else {
+ throw new moodle_exception("Ajax rest.php: This module is missing mod/$cm->modname/lib.php");
+ }
+ $deleteinstancefunction = $cm->modname."_delete_instance";
+
+ // Run the module's cleanup funtion.
+ if (!$deleteinstancefunction($cm->instance)) {
+ throw new moodle_exception("Ajax rest.php: Could not delete the $cm->modname $cm->name (instance)");
+ die;
+ }
+
+ // remove all module files in case modules forget to do that
+ $fs = get_file_storage();
+ $fs->delete_area_files($modcontext->id);
+
+ if (!delete_course_module($cm->id)) {
+ throw new moodle_exception("Ajax rest.php: Could not delete the $cm->modname $cm->name (coursemodule)");
+ }
+ // Remove the course_modules entry.
+ if (!delete_mod_from_section($cm->id, $cm->section)) {
+ throw new moodle_exception("Ajax rest.php: Could not delete the $cm->modname $cm->name from section");
+ }
+
+ // Trigger a mod_deleted event with information about this module.
+ $eventdata = new stdClass();
+ $eventdata->modulename = $cm->modname;
+ $eventdata->cmid = $cm->id;
+ $eventdata->courseid = $course->id;
+ $eventdata->userid = $USER->id;
+ events_trigger('mod_deleted', $eventdata);
+
+ add_to_log($courseid, "course", "delete mod",
+ "view.php?id=$courseid",
+ "$cm->modname $cm->instance", $cm->id);
+ break;
+ }
+ break;
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Allows a creator to edit custom scales, and also display help about scales
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @deprecated - TODO remove this file or replace it with an alternative solution for scales overview
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require_once("../config.php");
+require_once("lib.php");
+
+$id = required_param('id', PARAM_INT); // course id
+$scaleid = optional_param('scaleid', 0, PARAM_INT); // scale id (show only this one)
+
+$url = new moodle_url('/course/scales.php', array('id'=>$id));
+if ($scaleid !== 0) {
+ $url->param('scaleid', $scaleid);
+}
+$PAGE->set_url($url);
+
+$context = null;
+if ($course = $DB->get_record('course', array('id'=>$id))) {
+ require_login($course);
+ $context = context_course::instance($course->id);
+} else {
+ //$id will be 0 for site level scales
+ require_login();
+ $context = context_system::instance();
+}
+
+$PAGE->set_context($context);
+require_capability('moodle/course:viewscales', $context);
+
+$strscales = get_string("scales");
+$strcustomscales = get_string("scalescustom");
+$strstandardscales = get_string("scalesstandard");
+
+$PAGE->set_title($strscales);
+if (!empty($course)) {
+ $PAGE->set_heading($course->fullname);
+} else {
+ $PAGE->set_heading($SITE->fullname);
+}
+echo $OUTPUT->header();
+
+if ($scaleid) {
+ if ($scale = $DB->get_record("scale", array('id'=>$scaleid))) {
+ if ($scale->courseid == 0 || $scale->courseid == $course->id) {
+
+ $scalemenu = make_menu_from_list($scale->scale);
+
+ echo $OUTPUT->box_start();
+ echo $OUTPUT->heading($scale->name);
+ echo "<center>";
+ echo html_writer::label(get_string('scales'), 'scaleunused'. $scaleid, false, array('class' => 'accesshide'));
+ echo html_writer::select($scalemenu, 'unused', '', array('' => 'choosedots'), array('id' => 'scaleunused'.$scaleid));
+ echo "</center>";
+ echo text_to_html($scale->description);
+ echo $OUTPUT->box_end();
+ echo $OUTPUT->close_window_button();
+ echo $OUTPUT->footer();
+ exit;
+ }
+ }
+}
+
+$systemcontext = context_system::instance();
+
+if ($scales = $DB->get_records("scale", array("courseid"=>$course->id), "name ASC")) {
+ echo $OUTPUT->heading($strcustomscales);
+
+ if (has_capability('moodle/course:managescales', $context)) {
+ echo "<p align=\"center\">(";
+ print_string('scalestip2');
+ echo ")</p>";
+ }
+
+ foreach ($scales as $scale) {
+
+ $scale->description = file_rewrite_pluginfile_urls($scale->description, 'pluginfile.php', $systemcontext->id, 'grade', 'scale', $scale->id);
+
+ $scalemenu = make_menu_from_list($scale->scale);
+
+ echo $OUTPUT->box_start();
+ echo $OUTPUT->heading($scale->name);
+ echo "<center>";
+ echo html_writer::label(get_string('scales'), 'courseunused' . $scale->id, false, array('class' => 'accesshide'));
+ echo html_writer::select($scalemenu, 'unused', '', array('' => 'choosedots'), array('id' => 'courseunused' . $scale->id));
+ echo "</center>";
+ echo text_to_html($scale->description);
+ echo $OUTPUT->box_end();
+ echo "<hr />";
+ }
+
+} else {
+ if (has_capability('moodle/course:managescales', $context)) {
+ echo "<p align=\"center\">(";
+ print_string("scalestip2");
+ echo ")</p>";
+ }
+}
+
+if ($scales = $DB->get_records("scale", array("courseid"=>0), "name ASC")) {
+ echo $OUTPUT->heading($strstandardscales);
+ foreach ($scales as $scale) {
+
+ $scale->description = file_rewrite_pluginfile_urls($scale->description, 'pluginfile.php', $systemcontext->id, 'grade', 'scale', $scale->id);
+
+ $scalemenu = make_menu_from_list($scale->scale);
+
+ echo $OUTPUT->box_start();
+ echo $OUTPUT->heading($scale->name);
+ echo "<center>";
+ echo html_writer::label(get_string('scales'), 'sitescale' . $scale->id, false, array('class' => 'accesshide'));
+ echo html_writer::select($scalemenu, 'unused', '', array('' => 'choosedots'), array('id' => 'sitescale' . $scale->id));
+ echo "</center>";
+ echo text_to_html($scale->description);
+ echo $OUTPUT->box_end();
+ echo "<hr />";
+ }
+}
+
+echo $OUTPUT->close_window_button();
+echo $OUTPUT->footer();
+
--- /dev/null
+<?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/>.
+
+/**
+ * Displays external information about a course
+ * @package core
+ * @category course
+ * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once("../config.php");
+require_once($CFG->dirroot.'/course/lib.php');
+
+$search = optional_param('search', '', PARAM_RAW); // search words
+$page = optional_param('page', 0, PARAM_INT); // which page to show
+$perpage = optional_param('perpage', 10, PARAM_INT); // how many per page
+$moveto = optional_param('moveto', 0, PARAM_INT); // move to category
+$edit = optional_param('edit', -1, PARAM_BOOL);
+$hide = optional_param('hide', 0, PARAM_INT);
+$show = optional_param('show', 0, PARAM_INT);
+$blocklist = optional_param('blocklist', 0, PARAM_INT);
+$modulelist= optional_param('modulelist', '', PARAM_PLUGIN);
+
+// List of minimum capabilities which user need to have for editing/moving course
+$capabilities = array('moodle/course:create', 'moodle/category:manage');
+
+// List of category id's in which current user has course:create and category:manage capability.
+$usercatlist = array();
+
+// List of parent category id's
+$catparentlist = array();
+
+// Populate usercatlist with list of category id's with required capabilities.
+make_categories_list($usercatlist, $catparentlist, $capabilities);
+
+$search = trim(strip_tags($search)); // trim & clean raw searched string
+if ($search) {
+ $searchterms = explode(" ", $search); // Search for words independently
+ foreach ($searchterms as $key => $searchterm) {
+ if (strlen($searchterm) < 2) {
+ unset($searchterms[$key]);
+ }
+ }
+ $search = trim(implode(" ", $searchterms));
+}
+
+$site = get_site();
+
+$urlparams = array();
+foreach (array('search', 'page', 'blocklist', 'modulelist', 'edit') as $param) {
+ if (!empty($$param)) {
+ $urlparams[$param] = $$param;
+ }
+}
+if ($perpage != 10) {
+ $urlparams['perpage'] = $perpage;
+}
+$PAGE->set_url('/course/search.php', $urlparams);
+$PAGE->set_context(context_system::instance());
+$PAGE->set_pagelayout('standard');
+
+if ($CFG->forcelogin) {
+ require_login();
+}
+
+// Editing is possible if user has system or category level create and manage capability
+if (can_edit_in_category() || !empty($usercatlist)) {
+ if ($edit !== -1) {
+ $USER->editing = $edit;
+ }
+ $adminediting = $PAGE->user_is_editing();
+
+ // Set perpage if user can edit in category
+ if ($perpage != 99999) {
+ $perpage = 30;
+ }
+} else {
+ $adminediting = false;
+}
+
+// Editing functions
+if (has_capability('moodle/course:visibility', context_system::instance())) {
+ // Hide or show a course
+ if (($hide || $show) && confirm_sesskey()) {
+ if ($hide) {
+ $course = $DB->get_record("course", array("id" => $hide));
+ $visible = 0;
+ } else {
+ $course = $DB->get_record("course", array("id" => $show));
+ $visible = 1;
+ }
+ if ($course) {
+ $DB->set_field("course", "visible", $visible, array("id" => $course->id));
+ }
+ }
+}
+
+$displaylist = array();
+$parentlist = array();
+make_categories_list($displaylist, $parentlist);
+
+$strcourses = new lang_string("courses");
+$strsearch = new lang_string("search");
+$strsearchresults = new lang_string("searchresults");
+$strcategory = new lang_string("category");
+$strselect = new lang_string("select");
+$strselectall = new lang_string("selectall");
+$strdeselectall = new lang_string("deselectall");
+$stredit = new lang_string("edit");
+$strfrontpage = new lang_string('frontpage', 'admin');
+$strnovalidcourses = new lang_string('novalidcourses');
+
+if (empty($search) and empty($blocklist) and empty($modulelist) and empty($moveto) and ($edit != -1)) {
+ $PAGE->navbar->add($strcourses, new moodle_url('/course/index.php'));
+ $PAGE->navbar->add($strsearch);
+ $PAGE->set_title("$site->fullname : $strsearch");
+ $PAGE->set_heading($site->fullname);
+
+ echo $OUTPUT->header();
+ echo $OUTPUT->box_start();
+ echo "<center>";
+ echo "<br />";
+ print_course_search("", false, "plain");
+ echo "<br /><p>";
+ print_string("searchhelp");
+ echo "</p>";
+ echo "</center>";
+ echo $OUTPUT->box_end();
+ echo $OUTPUT->footer();
+ exit;
+}
+
+$courses = array();
+if (!empty($moveto) and $data = data_submitted() and confirm_sesskey()) { // Some courses are being moved
+ if (!$destcategory = $DB->get_record("course_categories", array("id" => $moveto))) {
+ print_error('cannotfindcategory', '', '', $moveto);
+ }
+
+ // User should have manage and create capablity on destination category.
+ require_capability('moodle/category:manage', context_coursecat::instance($moveto));
+ require_capability('moodle/course:create', context_coursecat::instance($moveto));
+
+ foreach ( $data as $key => $value ) {
+ if (preg_match('/^c\d+$/', $key)) {
+ $courseid = substr($key, 1);
+ // user must have category:manage and course:create capability for the course to be moved.
+ $coursecontext = context_course::instance($courseid);
+ foreach ($capabilities as $capability) {
+ // Require capability here will result in a fatal error should the user not
+ // have the requried category ensuring that no moves occur if they are
+ // trying to move multiple courses.
+ require_capability($capability, $coursecontext);
+ array_push($courses, $courseid);
+ }
+ }
+ }
+ move_courses($courses, $moveto);
+}
+
+// get list of courses containing blocks if required
+if (!empty($blocklist) and confirm_sesskey()) {
+ $blockname = $DB->get_field('block', 'name', array('id' => $blocklist));
+ $courses = array();
+ list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
+ $sql = "SELECT c.* $select FROM {course} c
+ $join JOIN {block_instances} bi ON bi.parentcontextid = ctx.id
+ WHERE bi.blockname = ?";
+ $courses = $DB->get_records_sql($sql, array($blockname));
+ $totalcount = count($courses);
+ // Keep only chunk of array which you want to display
+ if ($totalcount > $perpage) {
+ $courses = array_chunk($courses, $perpage, true);
+ $courses = $courses[$page];
+ }
+ foreach ($courses as $course) {
+ $courses[$course->id] = $course;
+ }
+} elseif (!empty($modulelist) and confirm_sesskey()) { // get list of courses containing modules
+ $modulename = $modulelist;
+ list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
+ $sql = "SELECT c.* $select FROM {course} c $join
+ WHERE c.id IN (SELECT DISTINCT cc.id FROM {".$modulelist."} module, {course} cc
+ WHERE module.course = cc.id)";
+ $courselist = $DB->get_records_sql($sql);
+ $courses = array();
+ if (!empty($courselist)) {
+ $firstcourse = $page*$perpage;
+ $lastcourse = $page*$perpage + $perpage -1;
+ $i = 0;
+ foreach ($courselist as $course) {
+ if ($i >= $firstcourse && $i <= $lastcourse) {
+ $courses[$course->id] = $course;
+ }
+ $i++;
+ }
+ }
+ $totalcount = count($courselist);
+} else if (!empty($searchterm)) {
+ // Donot do search for empty search request.
+ $courses = get_courses_search($searchterms, "fullname ASC", $page, $perpage, $totalcount);
+}
+
+$searchform = '';
+// Turn editing should be visible if user have system or category level capability
+if (!empty($courses) && (can_edit_in_category() || !empty($usercatlist))) {
+ if ($PAGE->user_is_editing()) {
+ $string = new lang_string("turneditingoff");
+ $edit = "off";
+ } else {
+ $string = new lang_string("turneditingon");
+ $edit = "on";
+ }
+ $params = array_merge($urlparams, array('sesskey' => sesskey(), 'edit' => $edit));
+ $aurl = new moodle_url("$CFG->wwwroot/course/search.php", $params);
+ $searchform = $OUTPUT->single_button($aurl, $string, 'get');
+} else {
+ $searchform = print_course_search($search, true, "navbar");
+}
+
+$PAGE->navbar->add($strcourses, new moodle_url('/course/index.php'));
+$PAGE->navbar->add($strsearch, new moodle_url('/course/search.php'));
+if (!empty($search)) {
+ $PAGE->navbar->add(s($search));
+}
+$PAGE->set_title("$site->fullname : $strsearchresults");
+$PAGE->set_heading($site->fullname);
+$PAGE->set_button($searchform);
+
+echo $OUTPUT->header();
+
+$lastcategory = -1;
+if ($courses) {
+ echo $OUTPUT->heading("$strsearchresults: $totalcount");
+ $encodedsearch = urlencode($search);
+
+ // add the module/block parameter to the paging bar if they exists
+ $modulelink = "";
+ if (!empty($modulelist) and confirm_sesskey()) {
+ $modulelink = "&modulelist=".$modulelist."&sesskey=".sesskey();
+ } else if (!empty($blocklist) and confirm_sesskey()) {
+ $modulelink = "&blocklist=".$blocklist."&sesskey=".sesskey();
+ }
+
+ print_navigation_bar($totalcount, $page, $perpage, $encodedsearch, $modulelink);
+
+ // Show list of courses
+ if (!$adminediting) { //Not editing mode
+ foreach ($courses as $course) {
+ // front page don't belong to any category and block can exist.
+ if ($course->category > 0) {
+ $course->summary .= "<br /><p class=\"category\">";
+ $course->summary .= "$strcategory: <a href=\"category.php?id=$course->category\">";
+ $course->summary .= $displaylist[$course->category];
+ $course->summary .= "</a></p>";
+ }
+ print_course($course, $search);
+ echo $OUTPUT->spacer(array('height'=>5, 'width'=>5, 'br'=>true)); // should be done with CSS instead
+ }
+ } else {
+ // Editing mode
+ echo "<form id=\"movecourses\" action=\"search.php\" method=\"post\">\n";
+ echo "<div><input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />\n";
+ echo "<input type=\"hidden\" name=\"search\" value=\"".s($search)."\" />\n";
+ echo "<input type=\"hidden\" name=\"page\" value=\"$page\" />\n";
+ echo "<input type=\"hidden\" name=\"perpage\" value=\"$perpage\" /></div>\n";
+ if (!empty($modulelist) and confirm_sesskey()) {
+ echo "<input type=\"hidden\" name=\"modulelist\" value=\"$modulelist\" /></div>\n";
+ } else if (!empty($blocklist) and confirm_sesskey()) {
+ echo "<input type=\"hidden\" name=\"blocklist\" value=\"$blocklist\" /></div>\n";
+ }
+ echo "<table border=\"0\" cellspacing=\"2\" cellpadding=\"4\" class=\"generalbox boxaligncenter\">\n<tr>\n";
+ echo "<th scope=\"col\">$strcourses</th>\n";
+ echo "<th scope=\"col\">$strcategory</th>\n";
+ echo "<th scope=\"col\">$strselect</th>\n";
+ echo "<th scope=\"col\">$stredit</th></tr>\n";
+
+ foreach ($courses as $course) {
+
+ context_helper::preload_from_record($course);
+ $coursecontext = context_course::instance($course->id);
+
+ $linkcss = $course->visible ? "" : " class=\"dimmed\" ";
+
+ // are we displaying the front page (courseid=1)?
+ if ($course->id == 1) {
+ echo "<tr>\n";
+ echo "<td><a href=\"$CFG->wwwroot\">$strfrontpage</a></td>\n";
+
+ // can't do anything else with the front page
+ echo " <td> </td>\n"; // category place
+ echo " <td> </td>\n"; // select place
+ echo " <td> </td>\n"; // edit place
+ echo "</tr>\n";
+ continue;
+ }
+
+ echo "<tr>\n";
+ echo "<td><a $linkcss href=\"view.php?id=$course->id\">"
+ . highlight($search, $coursecontext->get_context_name(false)) . "</a></td>\n";
+ echo "<td>".$displaylist[$course->category]."</td>\n";
+ echo "<td>\n";
+
+ // If user has all required capabilities to move course then show selectable checkbox
+ if (has_all_capabilities($capabilities, $coursecontext)) {
+ echo "<input type=\"checkbox\" name=\"c$course->id\" />\n";
+ } else {
+ echo "<input type=\"checkbox\" name=\"c$course->id\" disabled=\"disabled\" />\n";
+ }
+
+ echo "</td>\n";
+ echo "<td>\n";
+
+ // checks whether user can update course settings
+ if (has_capability('moodle/course:update', $coursecontext)) {
+ echo "<a title=\"".get_string("settings")."\" href=\"$CFG->wwwroot/course/edit.php?id=$course->id\">\n<img".
+ " src=\"" . $OUTPUT->pix_url('t/edit') . "\" class=\"iconsmall\" alt=\"".get_string("settings")."\" /></a>\n ";
+ }
+
+ // checks whether user can do role assignment
+ if (has_capability('moodle/course:enrolreview', $coursecontext)) {
+ echo'<a title="'.get_string('enrolledusers', 'enrol').'" href="'.$CFG->wwwroot.'/enrol/users.php?id='.$course->id.'">';
+ echo '<img src="'.$OUTPUT->pix_url('i/enrolusers') . '" class="iconsmall" alt="'.get_string('enrolledusers', 'enrol').'" /></a> ' . "\n";
+ }
+
+ // checks whether user can delete course
+ if (has_capability('moodle/course:delete', $coursecontext)) {
+ echo "<a title=\"".get_string("delete")."\" href=\"delete.php?id=$course->id\">\n<img".
+ " src=\"" . $OUTPUT->pix_url('t/delete') . "\" class=\"iconsmall\" alt=\"".get_string("delete")."\" /></a>\n ";
+ }
+
+ // checks whether user can change visibility
+ if (has_capability('moodle/course:visibility', $coursecontext)) {
+ if (!empty($course->visible)) {
+ echo "<a title=\"".get_string("hide")."\" href=\"search.php?search=$encodedsearch&perpage=$perpage&page=$page&hide=$course->id&sesskey=".sesskey()."\">\n<img".
+ " src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"".get_string("hide")."\" /></a>\n ";
+ } else {
+ echo "<a title=\"".get_string("show")."\" href=\"search.php?search=$encodedsearch&perpage=$perpage&page=$page&show=$course->id&sesskey=".sesskey()."\">\n<img".
+ " src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"".get_string("show")."\" /></a>\n ";
+ }
+ }
+
+ // checks whether user can do site backup
+ if (has_capability('moodle/backup:backupcourse', $coursecontext)) {
+ $backupurl = new moodle_url('/backup/backup.php', array('id' => $course->id));
+ echo "<a title=\"".get_string("backup")."\" href=\"".$backupurl."\">\n<img".
+ " src=\"" . $OUTPUT->pix_url('t/backup') . "\" class=\"iconsmall\" alt=\"".get_string("backup")."\" /></a>\n ";
+ }
+
+ // checks whether user can do restore
+ if (has_capability('moodle/restore:restorecourse', $coursecontext)) {
+ $restoreurl = new moodle_url('/backup/restorefile.php', array('contextid' => $coursecontext->id));
+ echo "<a title=\"".get_string("restore")."\" href=\"".$restoreurl."\">\n<img".
+ " src=\"" . $OUTPUT->pix_url('t/restore') . "\" class=\"iconsmall\" alt=\"".get_string("restore")."\" /></a>\n ";
+ }
+
+ echo "</td>\n</tr>\n";
+ }
+ echo "<tr>\n<td colspan=\"4\" style=\"text-align:center\">\n";
+ echo "<br />";
+ echo "<input type=\"button\" onclick=\"checkall()\" value=\"$strselectall\" />\n";
+ echo "<input type=\"button\" onclick=\"checknone()\" value=\"$strdeselectall\" />\n";
+ // Select box should only show categories in which user has min capability to move course.
+ echo html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
+ echo html_writer::select($usercatlist, 'moveto', '', array(''=>get_string('moveselectedcoursesto')), array('id'=>'movetoid', 'class' => 'autosubmit'));
+ $PAGE->requires->yui_module('moodle-core-formautosubmit',
+ 'M.core.init_formautosubmit',
+ array(array('selectid' => 'movetoid', 'nothing' => false))
+ );
+ echo "</td>\n</tr>\n";
+ echo "</table>\n</form>";
+
+ }
+
+ print_navigation_bar($totalcount,$page,$perpage,$encodedsearch,$modulelink);
+
+} else {
+ if (!empty($search)) {
+ echo $OUTPUT->heading(get_string("nocoursesfound",'', s($search)));
+ }
+ else {
+ echo $OUTPUT->heading($strnovalidcourses);
+ }
+}
+
+echo "<br /><br />";
+
+print_course_search($search);
+
+echo $OUTPUT->footer();
+
+/**
+ * Print a list navigation bar
+ * Display page numbers, and a link for displaying all entries
+ * @param int $totalcount number of entry to display
+ * @param int $page page number
+ * @param int $perpage number of entry per page
+ * @param string $encodedsearch
+ * @param string $modulelink module name
+ */
+function print_navigation_bar($totalcount, $page, $perpage, $encodedsearch, $modulelink) {
+ global $OUTPUT;
+ echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "search.php?search=$encodedsearch".$modulelink."&perpage=$perpage");
+
+ // display
+ if ($perpage != 99999 && $totalcount > $perpage) {
+ echo "<center><p>";
+ echo "<a href=\"search.php?search=$encodedsearch".$modulelink."&perpage=99999\">".get_string("showall", "", $totalcount)."</a>";
+ echo "</p></center>";
+ } else if ($perpage === 99999) {
+ $defaultperpage = 10;
+ // If user has course:create or category:manage capability the show 30 records.
+ $capabilities = array('moodle/course:create', 'moodle/category:manage');
+ if (has_any_capability($capabilities, context_system::instance())) {
+ $defaultperpage = 30;
+ }
+
+ echo "<center><p>";
+ echo "<a href=\"search.php?search=$encodedsearch".$modulelink."&perpage=".$defaultperpage."\">".get_string("showperpage", "", $defaultperpage)."</a>";
+ echo "</p></center>";
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * The purpose of this file is to allow the user to switch roles and be redirected
+ * back to the page that they were on.
+ *
+ * This functionality is also supported in {@link /course/view.php} in order to comply
+ * with backwards compatibility
+ * The reason that we created this file was so that user didn't get redirected back
+ * to the course view page only to be redirected again.
+ *
+ * @since 2.0
+ * @package course
+ * @copyright 2009 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once($CFG->dirroot.'/course/lib.php');
+
+$id = required_param('id', PARAM_INT);
+$switchrole = optional_param('switchrole',-1, PARAM_INT);
+$returnurl = optional_param('returnurl', false, PARAM_LOCALURL);
+
+$PAGE->set_url('/course/switchrole.php', array('id'=>$id));
+
+if (!confirm_sesskey()) {
+ print_error('confirmsesskeybad', 'error');
+}
+
+if (! ($course = $DB->get_record('course', array('id'=>$id)))) {
+ print_error('invalidcourseid', 'error');
+}
+
+$context = context_course::instance($course->id);
+
+// Remove any switched roles before checking login
+if ($switchrole == 0) {
+ role_switch($switchrole, $context);
+}
+require_login($course);
+
+// Switchrole - sanity check in cost-order...
+if ($switchrole > 0 && has_capability('moodle/role:switchroles', $context)) {
+ // is this role assignable in this context?
+ // inquiring minds want to know...
+ $aroles = get_switchable_roles($context);
+ if (is_array($aroles) && isset($aroles[$switchrole])) {
+ role_switch($switchrole, $context);
+ // Double check that this role is allowed here
+ require_login($course);
+ }
+}
+
+// TODO: Using SESSION->returnurl is deprecated and should be removed in the future.
+// Till then this code remains to support any external applications calling this script.
+if (!empty($returnurl) && is_numeric($returnurl)) {
+ $returnurl = false;
+ if (!empty($SESSION->returnurl) && strpos($SESSION->returnurl, 'moodle_url')!==false) {
+ debugging('Code calling switchrole should be passing a URL as a param.', DEBUG_DEVELOPER);
+ $returnurl = @unserialize($SESSION->returnurl);
+ if (!($returnurl instanceof moodle_url)) {
+ $returnurl = false;
+ }
+ }
+}
+
+if ($returnurl === false) {
+ $returnurl = new moodle_url('/course/view.php', array('id' => $course->id));
+}
+
+redirect($returnurl);
--- /dev/null
+<?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/>.
+
+/**
+ * Course related unit tests
+ *
+ * @package core
+ * @category phpunit
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot.'/course/lib.php');
+
+class courselib_testcase extends advanced_testcase {
+
+ public function test_create_course() {
+ global $DB;
+ $this->resetAfterTest(true);
+ $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
+
+ $course = new stdClass();
+ $course->fullname = 'Apu loves Unit Təsts';
+ $course->shortname = 'Spread the lÅve';
+ $course->idnumber = '123';
+ $course->summary = 'Awesome!';
+ $course->summaryformat = FORMAT_PLAIN;
+ $course->format = 'topics';
+ $course->newsitems = 0;
+ $course->numsections = 5;
+ $course->category = $defaultcategory;
+
+ $created = create_course($course);
+ $context = context_course::instance($created->id);
+
+ // Compare original and created.
+ $original = (array) $course;
+ $this->assertEquals($original, array_intersect_key((array) $created, $original));
+
+ // Ensure default section is created.
+ $sectioncreated = $DB->record_exists('course_sections', array('course' => $created->id, 'section' => 0));
+ $this->assertTrue($sectioncreated);
+
+ // Ensure blocks have been associated to the course.
+ $blockcount = $DB->count_records('block_instances', array('parentcontextid' => $context->id));
+ $this->assertGreaterThan(0, $blockcount);
+ }
+
+ public function test_create_course_with_generator() {
+ global $DB;
+ $this->resetAfterTest(true);
+ $course = $this->getDataGenerator()->create_course();
+
+ // Ensure default section is created.
+ $sectioncreated = $DB->record_exists('course_sections', array('course' => $course->id, 'section' => 0));
+ $this->assertTrue($sectioncreated);
+ }
+
+ public function test_create_course_sections() {
+ global $DB;
+ $this->resetAfterTest(true);
+
+ $course = $this->getDataGenerator()->create_course(
+ array('shortname' => 'GrowingCourse',
+ 'fullname' => 'Growing Course',
+ 'numsections' => 5),
+ array('createsections' => true));
+
+ // Ensure all 6 (0-5) sections were created and modinfo/sectioninfo cache works properly
+ $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
+ $this->assertEquals(range(0, $course->numsections), $sectionscreated);
+
+ // this will do nothing, section already exists
+ $this->assertFalse(course_create_sections_if_missing($course, $course->numsections));
+
+ // this will create new section
+ $this->assertTrue(course_create_sections_if_missing($course, $course->numsections + 1));
+
+ // Ensure all 7 (0-6) sections were created and modinfo/sectioninfo cache works properly
+ $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
+ $this->assertEquals(range(0, $course->numsections + 1), $sectionscreated);
+ }
+
+ public function test_reorder_sections() {
+ global $DB;
+ $this->resetAfterTest(true);
+
+ $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
+ $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
+ $oldsections = array();
+ $sections = array();
+ foreach ($DB->get_records('course_sections', array('course'=>$course->id), 'id') as $section) {
+ $oldsections[$section->section] = $section->id;
+ $sections[$section->id] = $section->section;
+ }
+ ksort($oldsections);
+
+ $neworder = reorder_sections($sections, 2, 4);
+ $neworder = array_keys($neworder);
+ $this->assertEquals($oldsections[0], $neworder[0]);
+ $this->assertEquals($oldsections[1], $neworder[1]);
+ $this->assertEquals($oldsections[2], $neworder[4]);
+ $this->assertEquals($oldsections[3], $neworder[2]);
+ $this->assertEquals($oldsections[4], $neworder[3]);
+ $this->assertEquals($oldsections[5], $neworder[5]);
+ $this->assertEquals($oldsections[6], $neworder[6]);
+
+ $neworder = reorder_sections($sections, 4, 2);
+ $neworder = array_keys($neworder);
+ $this->assertEquals($oldsections[0], $neworder[0]);
+ $this->assertEquals($oldsections[1], $neworder[1]);
+ $this->assertEquals($oldsections[2], $neworder[3]);
+ $this->assertEquals($oldsections[3], $neworder[4]);
+ $this->assertEquals($oldsections[4], $neworder[2]);
+ $this->assertEquals($oldsections[5], $neworder[5]);
+ $this->assertEquals($oldsections[6], $neworder[6]);
+
+ $neworder = reorder_sections(1, 2, 4);
+ $this->assertFalse($neworder);
+ }
+
+ public function test_move_section_down() {
+ global $DB;
+ $this->resetAfterTest(true);
+
+ $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
+ $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
+ $oldsections = array();
+ foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
+ $oldsections[$section->section] = $section->id;
+ }
+ ksort($oldsections);
+
+ // Test move section down..
+ move_section_to($course, 2, 4);
+ $sections = array();
+ foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
+ $sections[$section->section] = $section->id;
+ }
+ ksort($sections);
+
+ $this->assertEquals($oldsections[0], $sections[0]);
+ $this->assertEquals($oldsections[1], $sections[1]);
+ $this->assertEquals($oldsections[2], $sections[4]);
+ $this->assertEquals($oldsections[3], $sections[2]);
+ $this->assertEquals($oldsections[4], $sections[3]);
+ $this->assertEquals($oldsections[5], $sections[5]);
+ $this->assertEquals($oldsections[6], $sections[6]);
+ }
+
+ public function test_move_section_up() {
+ global $DB;
+ $this->resetAfterTest(true);
+
+ $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
+ $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
+ $oldsections = array();
+ foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
+ $oldsections[$section->section] = $section->id;
+ }
+ ksort($oldsections);
+
+ // Test move section up..
+ move_section_to($course, 6, 4);
+ $sections = array();
+ foreach ($DB->get_records('course_sections', array('course'=>$course->id)) as $section) {
+ $sections[$section->section] = $section->id;
+ }
+ ksort($sections);
+
+ $this->assertEquals($oldsections[0], $sections[0]);
+ $this->assertEquals($oldsections[1], $sections[1]);
+ $this->assertEquals($oldsections[2], $sections[2]);
+ $this->assertEquals($oldsections[3], $sections[3]);
+ $this->assertEquals($oldsections[4], $sections[5]);
+ $this->assertEquals($oldsections[5], $sections[6]);
+ $this->assertEquals($oldsections[6], $sections[4]);
+ }
+
+ public function test_move_section_marker() {
+ global $DB;
+ $this->resetAfterTest(true);
+
+ $this->getDataGenerator()->create_course(array('numsections'=>5), array('createsections'=>true));
+ $course = $this->getDataGenerator()->create_course(array('numsections'=>10), array('createsections'=>true));
+
+ // Set course marker to the section we are going to move..
+ course_set_marker($course->id, 2);
+ // Verify that the course marker is set correctly.
+ $course = $DB->get_record('course', array('id' => $course->id));
+ $this->assertEquals(2, $course->marker);
+
+ // Test move the marked section down..
+ move_section_to($course, 2, 4);
+
+ // Verify that the coruse marker has been moved along with the section..
+ $course = $DB->get_record('course', array('id' => $course->id));
+ $this->assertEquals(4, $course->marker);
+
+ // Test move the marked section up..
+ move_section_to($course, 4, 3);
+
+ // Verify that the course marker has been moved along with the section..
+ $course = $DB->get_record('course', array('id' => $course->id));
+ $this->assertEquals(3, $course->marker);
+
+ // Test moving a non-marked section above the marked section..
+ move_section_to($course, 4, 2);
+
+ // Verify that the course marker has been moved down to accomodate..
+ $course = $DB->get_record('course', array('id' => $course->id));
+ $this->assertEquals(4, $course->marker);
+
+ // Test moving a non-marked section below the marked section..
+ move_section_to($course, 3, 6);
+
+ // Verify that the course marker has been up to accomodate..
+ $course = $DB->get_record('course', array('id' => $course->id));
+ $this->assertEquals(3, $course->marker);
+ }
+
+ public function test_get_course_display_name_for_list() {
+ global $CFG;
+ $this->resetAfterTest(true);
+
+ $course = $this->getDataGenerator()->create_course(array('shortname' => 'FROG101', 'fullname' => 'Introduction to pond life'));
+
+ $CFG->courselistshortnames = 0;
+ $this->assertEquals('Introduction to pond life', get_course_display_name_for_list($course));
+
+ $CFG->courselistshortnames = 1;
+ $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course));
+ }
+
+ public function test_create_course_category() {
+ global $CFG, $DB;
+ $this->resetAfterTest(true);
+
+ // Create the category
+ $data = new stdClass();
+ $data->name = 'aaa';
+ $data->description = 'aaa';
+ $data->idnumber = '';
+
+ $category1 = create_course_category($data);
+
+ // Initially confirm that base data was inserted correctly
+ $this->assertEquals($data->name, $category1->name);
+ $this->assertEquals($data->description, $category1->description);
+ $this->assertEquals($data->idnumber, $category1->idnumber);
+
+ // sortorder should be blank initially
+ $this->assertEmpty($category1->sortorder);
+
+ // Calling fix_course_sortorder() should provide a new sortorder
+ fix_course_sortorder();
+ $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
+
+ $this->assertGreaterThanOrEqual(1, $category1->sortorder);
+
+ // Create two more categories and test the sortorder worked correctly
+ $data->name = 'ccc';
+ $category2 = create_course_category($data);
+ $this->assertEmpty($category2->sortorder);
+
+ $data->name = 'bbb';
+ $category3 = create_course_category($data);
+ $this->assertEmpty($category3->sortorder);
+
+ // Calling fix_course_sortorder() should provide a new sortorder to give category1,
+ // category2, category3. New course categories are ordered by id not name
+ fix_course_sortorder();
+
+ $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
+ $category2 = $DB->get_record('course_categories', array('id' => $category2->id));
+ $category3 = $DB->get_record('course_categories', array('id' => $category3->id));
+
+ $this->assertGreaterThanOrEqual($category1->sortorder, $category2->sortorder);
+ $this->assertGreaterThanOrEqual($category2->sortorder, $category3->sortorder);
+ $this->assertGreaterThanOrEqual($category1->sortorder, $category3->sortorder);
+ }
+
+ public function test_move_module_in_course() {
+ $this->resetAfterTest(true);
+ // Setup fixture
+ $course = $this->getDataGenerator()->create_course(array('numsections'=>5));
+ $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
+
+ $cms = get_fast_modinfo($course)->get_cms();
+ $cm = reset($cms);
+
+ course_create_sections_if_missing($course, 3);
+ $section3 = get_fast_modinfo($course)->get_section_info(3);
+
+ moveto_module($cm, $section3);
+
+ $modinfo = get_fast_modinfo($course);
+ $this->assertTrue(empty($modinfo->sections[0]));
+ $this->assertFalse(empty($modinfo->sections[3]));
+ }
+
+ public function test_module_visibility() {
+ $this->setAdminUser();
+ $this->resetAfterTest(true);
+
+ // Create course and modules.
+ $course = $this->getDataGenerator()->create_course(array('numsections' => 5));
+ $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
+ $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(), 'course' => $course->id));
+ $modules = compact('forum', 'assign');
+
+ // Hiding the modules.
+ foreach ($modules as $mod) {
+ set_coursemodule_visible($mod->cmid, 0);
+ $this->check_module_visibility($mod, 0, 0);
+ }
+
+ // Showing the modules.
+ foreach ($modules as $mod) {
+ set_coursemodule_visible($mod->cmid, 1);
+ $this->check_module_visibility($mod, 1, 1);
+ }
+ }
+
+ public function test_section_visibility() {
+ $this->setAdminUser();
+ $this->resetAfterTest(true);
+
+ // Create course.
+ $course = $this->getDataGenerator()->create_course(array('numsections' => 3), array('createsections' => true));
+
+ // Testing an empty section.
+ $sectionnumber = 1;
+ set_section_visible($course->id, $sectionnumber, 0);
+ $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
+ $this->assertEquals($section_info->visible, 0);
+ set_section_visible($course->id, $sectionnumber, 1);
+ $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
+ $this->assertEquals($section_info->visible, 1);
+
+ // Testing a section with visible modules.
+ $sectionnumber = 2;
+ $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
+ array('section' => $sectionnumber));
+ $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
+ 'course' => $course->id), array('section' => $sectionnumber));
+ $modules = compact('forum', 'assign');
+ set_section_visible($course->id, $sectionnumber, 0);
+ $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
+ $this->assertEquals($section_info->visible, 0);
+ foreach ($modules as $mod) {
+ $this->check_module_visibility($mod, 0, 1);
+ }
+ set_section_visible($course->id, $sectionnumber, 1);
+ $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
+ $this->assertEquals($section_info->visible, 1);
+ foreach ($modules as $mod) {
+ $this->check_module_visibility($mod, 1, 1);
+ }
+
+ // Testing a section with hidden modules, which should stay hidden.
+ $sectionnumber = 3;
+ $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
+ array('section' => $sectionnumber));
+ $assign = $this->getDataGenerator()->create_module('assign', array('duedate' => time(),
+ 'course' => $course->id), array('section' => $sectionnumber));
+ $modules = compact('forum', 'assign');
+ foreach ($modules as $mod) {
+ set_coursemodule_visible($mod->cmid, 0);
+ $this->check_module_visibility($mod, 0, 0);
+ }
+ set_section_visible($course->id, $sectionnumber, 0);
+ $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
+ $this->assertEquals($section_info->visible, 0);
+ foreach ($modules as $mod) {
+ $this->check_module_visibility($mod, 0, 0);
+ }
+ set_section_visible($course->id, $sectionnumber, 1);
+ $section_info = get_fast_modinfo($course->id)->get_section_info($sectionnumber);
+ $this->assertEquals($section_info->visible, 1);
+ foreach ($modules as $mod) {
+ $this->check_module_visibility($mod, 0, 0);
+ }
+ }
+
+ /**
+ * Helper function to assert that a module has correctly been made visible, or hidden.
+ *
+ * @param stdClass $mod module information
+ * @param int $visibility the current state of the module
+ * @param int $visibleold the current state of the visibleold property
+ * @return void
+ */
+ public function check_module_visibility($mod, $visibility, $visibleold) {
+ global $DB;
+ $cm = get_fast_modinfo($mod->course)->get_cm($mod->cmid);
+ $this->assertEquals($visibility, $cm->visible);
+ $this->assertEquals($visibleold, $cm->visibleold);
+
+ // Check the module grade items.
+ $grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $cm->modname,
+ 'iteminstance' => $cm->instance, 'courseid' => $cm->course));
+ if ($grade_items) {
+ foreach ($grade_items as $grade_item) {
+ if ($visibility) {
+ $this->assertFalse($grade_item->is_hidden(), "$cm->modname grade_item not visible");
+ } else {
+ $this->assertTrue($grade_item->is_hidden(), "$cm->modname grade_item not hidden");
+ }
+ }
+ }
+
+ // Check the events visibility.
+ if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $cm->modname))) {
+ foreach ($events as $event) {
+ $calevent = new calendar_event($event);
+ $this->assertEquals($visibility, $calevent->visible, "$cm->modname calendar_event visibility");
+ }
+ }
+ }
+
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Course request related unit tests
+ *
+ * @package core
+ * @category phpunit
+ * @copyright 2012 Frédéric Massart
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot.'/course/lib.php');
+
+class courserequest_testcase extends advanced_testcase {
+
+ public function test_create_request() {
+ global $DB, $USER;
+ $this->resetAfterTest(true);
+
+ $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
+ set_config('enablecourserequests', 1);
+ set_config('requestcategoryselection', 0);
+ set_config('defaultrequestcategory', $defaultcategory);
+
+ // Create some categories.
+ $cat1 = $this->getDataGenerator()->create_category();
+ $cat2 = $this->getDataGenerator()->create_category();
+ $cat3 = $this->getDataGenerator()->create_category();
+
+ // Basic course request.
+ $data = new stdClass();
+ $data->fullname = 'Həllo World!';
+ $data->shortname = 'Hi th€re!';
+ $data->summary_editor['text'] = 'Lorem Ipsum ©';
+ $data->summary_editor['format'] = FORMAT_HTML;
+ $data->reason = 'Because PHP Unit is cool.';
+ $cr = course_request::create($data);
+
+ $this->assertEquals($data->fullname, $cr->fullname);
+ $this->assertEquals($data->shortname, $cr->shortname);
+ $this->assertEquals($data->summary_editor['text'], $cr->summary);
+ $this->assertEquals($data->summary_editor['format'], $cr->summaryformat);
+ $this->assertEquals($data->reason, $cr->reason);
+ $this->assertEquals($USER->id, $cr->requester);
+ $this->assertEquals($defaultcategory, $cr->category);
+
+ // Request with category but category selection not allowed.
+ set_config('defaultrequestcategory', $cat2->id);
+ $data->category = $cat1->id;
+ $cr = course_request::create($data);
+ $this->assertEquals($cat2->id, $cr->category);
+
+ // Request with category different than default and category selection allowed.
+ set_config('defaultrequestcategory', $cat3->id);
+ set_config('requestcategoryselection', 1);
+ $data->category = $cat1->id;
+ $cr = course_request::create($data);
+ $this->assertEquals($cat1->id, $cr->category);
+ }
+
+ public function test_approve_request() {
+ global $DB;
+ $this->resetAfterTest(true);
+ $this->preventResetByRollback();
+
+ $this->setAdminUser();
+ $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
+ set_config('enablecourserequests', 1);
+ set_config('requestcategoryselection', 0);
+ set_config('defaultrequestcategory', $defaultcategory);
+
+ // Create some categories.
+ $cat1 = $this->getDataGenerator()->create_category();
+ $cat2 = $this->getDataGenerator()->create_category();
+
+ $data = new stdClass();
+ $data->fullname = 'Həllo World!';
+ $data->shortname = 'Hi th€re!';
+ $data->summary_editor['text'] = 'Lorem Ipsum ©';
+ $data->summary_editor['format'] = FORMAT_HTML;
+ $data->reason = 'Because PHP Unit is cool.';
+
+ // Test without category.
+ $cr = course_request::create($data);
+ $id = $cr->approve();
+ $this->assertDebuggingCalled(); // Caused by sending of message.
+ $course = $DB->get_record('course', array('id' => $id));
+ $this->assertEquals($data->fullname, $course->fullname);
+ $this->assertEquals($data->shortname, $course->shortname);
+ $this->assertEquals($data->summary_editor['text'], $course->summary);
+ $this->assertEquals($data->summary_editor['format'], $course->summaryformat);
+ $this->assertEquals(1, $course->requested);
+ $this->assertEquals($defaultcategory, $course->category);
+
+ // Test with category.
+ set_config('requestcategoryselection', 1);
+ set_config('defaultrequestcategory', $cat2->id);
+ $data->shortname .= ' 2nd';
+ $data->category = $cat1->id;
+ $cr = course_request::create($data);
+ $id = $cr->approve();
+ $this->assertDebuggingCalled(); // Caused by sending of message.
+ $course = $DB->get_record('course', array('id' => $id));
+ $this->assertEquals($data->category, $course->category);
+ }
+
+ public function test_reject_request() {
+ global $DB;
+ $this->resetAfterTest(true);
+ $this->preventResetByRollback();
+ $this->setAdminUser();
+ set_config('enablecourserequests', 1);
+ set_config('requestcategoryselection', 0);
+ set_config('defaultrequestcategory', $DB->get_field_select('course_categories', "MIN(id)", "parent=0"));
+
+ $data = new stdClass();
+ $data->fullname = 'Həllo World!';
+ $data->shortname = 'Hi th€re!';
+ $data->summary_editor['text'] = 'Lorem Ipsum ©';
+ $data->summary_editor['format'] = FORMAT_HTML;
+ $data->reason = 'Because PHP Unit is cool.';
+
+ $cr = course_request::create($data);
+ $this->assertTrue($DB->record_exists('course_request', array('id' => $cr->id)));
+ $cr->reject('Sorry!');
+ $this->assertDebuggingCalled(); // Caused by sending of message.
+ $this->assertFalse($DB->record_exists('course_request', array('id' => $cr->id)));
+ }
+
+}
--- /dev/null
+<?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/>.
+
+/**
+ * External course functions unit tests
+ *
+ * @package core_course
+ * @category external
+ * @copyright 2012 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+
+/**
+ * External course functions unit tests
+ *
+ * @package core_course
+ * @category external
+ * @copyright 2012 Jerome Mouneyrac
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_course_external_testcase extends externallib_advanced_testcase {
+
+ /**
+ * Tests set up
+ */
+ protected function setUp() {
+ global $CFG;
+ require_once($CFG->dirroot . '/course/externallib.php');
+ }
+
+ /**
+ * Test create_categories
+ */
+ public function test_create_categories() {
+
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ // Set the required capabilities by the external function
+ $contextid = context_system::instance()->id;
+ $roleid = $this->assignUserCapability('moodle/category:manage', $contextid);
+
+ // Create base categories.
+ $category1 = new stdClass();
+ $category1->name = 'Root Test Category 1';
+ $category2 = new stdClass();
+ $category2->name = 'Root Test Category 2';
+ $category2->idnumber = 'rootcattest2';
+ $category2->desc = 'Description for root test category 1';
+ $category2->theme = 'base';
+ $categories = array(
+ array('name' => $category1->name, 'parent' => 0),
+ array('name' => $category2->name, 'parent' => 0, 'idnumber' => $category2->idnumber,
+ 'description' => $category2->desc, 'theme' => $category2->theme)
+ );
+
+ $createdcats = core_course_external::create_categories($categories);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $createdcats = external_api::clean_returnvalue(core_course_external::create_categories_returns(), $createdcats);
+
+ // Initially confirm that base data was inserted correctly.
+ $this->assertEquals($category1->name, $createdcats[0]['name']);
+ $this->assertEquals($category2->name, $createdcats[1]['name']);
+
+ // Save the ids.
+ $category1->id = $createdcats[0]['id'];
+ $category2->id = $createdcats[1]['id'];
+
+ // Create on sub category.
+ $category3 = new stdClass();
+ $category3->name = 'Sub Root Test Category 3';
+ $subcategories = array(
+ array('name' => $category3->name, 'parent' => $category1->id)
+ );
+
+ $createdsubcats = core_course_external::create_categories($subcategories);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $createdsubcats = external_api::clean_returnvalue(core_course_external::create_categories_returns(), $createdsubcats);
+
+ // Confirm that sub categories were inserted correctly.
+ $this->assertEquals($category3->name, $createdsubcats[0]['name']);
+
+ // Save the ids.
+ $category3->id = $createdsubcats[0]['id'];
+
+ // Calling the ws function should provide a new sortorder to give category1,
+ // category2, category3. New course categories are ordered by id not name.
+ $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
+ $category2 = $DB->get_record('course_categories', array('id' => $category2->id));
+ $category3 = $DB->get_record('course_categories', array('id' => $category3->id));
+
+ $this->assertGreaterThanOrEqual($category1->sortorder, $category3->sortorder);
+ $this->assertGreaterThanOrEqual($category2->sortorder, $category3->sortorder);
+
+ // Call without required capability
+ $this->unassignUserCapability('moodle/category:manage', $contextid, $roleid);
+ $this->setExpectedException('required_capability_exception');
+ $createdsubcats = core_course_external::create_categories($subcategories);
+
+ }
+
+ /**
+ * Test delete categories
+ */
+ public function test_delete_categories() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ // Set the required capabilities by the external function
+ $contextid = context_system::instance()->id;
+ $roleid = $this->assignUserCapability('moodle/category:manage', $contextid);
+
+ $category1 = self::getDataGenerator()->create_category();
+ $category2 = self::getDataGenerator()->create_category(
+ array('parent' => $category1->id));
+ $category3 = self::getDataGenerator()->create_category();
+ $category4 = self::getDataGenerator()->create_category(
+ array('parent' => $category3->id));
+ $category5 = self::getDataGenerator()->create_category(
+ array('parent' => $category4->id));
+
+ //delete category 1 and 2 + delete category 4, category 5 moved under category 3
+ core_course_external::delete_categories(array(
+ array('id' => $category1->id, 'recursive' => 1),
+ array('id' => $category4->id)
+ ));
+
+ //check $category 1 and 2 are deleted
+ $notdeletedcount = $DB->count_records_select('course_categories',
+ 'id IN ( ' . $category1->id . ',' . $category2->id . ',' . $category4->id . ')');
+ $this->assertEquals(0, $notdeletedcount);
+
+ //check that $category5 as $category3 for parent
+ $dbcategory5 = $DB->get_record('course_categories', array('id' => $category5->id));
+ $this->assertEquals($dbcategory5->path, $category3->path . '/' . $category5->id);
+
+ // Call without required capability
+ $this->unassignUserCapability('moodle/category:manage', $contextid, $roleid);
+ $this->setExpectedException('required_capability_exception');
+ $createdsubcats = core_course_external::delete_categories(
+ array(array('id' => $category3->id)));
+ }
+
+ /**
+ * Test get categories
+ */
+ public function test_get_categories() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ $generatedcats = array();
+ $category1data['idnumber'] = 'idnumbercat1';
+ $category1data['name'] = 'Category 1 for PHPunit test';
+ $category1data['description'] = 'Category 1 description';
+ $category1data['descriptionformat'] = FORMAT_MOODLE;
+ $category1 = self::getDataGenerator()->create_category($category1data);
+ $generatedcats[$category1->id] = $category1;
+ $category2 = self::getDataGenerator()->create_category(
+ array('parent' => $category1->id));
+ $generatedcats[$category2->id] = $category2;
+ $category6 = self::getDataGenerator()->create_category(
+ array('parent' => $category1->id, 'visible' => 0));
+ $generatedcats[$category6->id] = $category6;
+ $category3 = self::getDataGenerator()->create_category();
+ $generatedcats[$category3->id] = $category3;
+ $category4 = self::getDataGenerator()->create_category(
+ array('parent' => $category3->id));
+ $generatedcats[$category4->id] = $category4;
+ $category5 = self::getDataGenerator()->create_category(
+ array('parent' => $category4->id));
+ $generatedcats[$category5->id] = $category5;
+
+ // Set the required capabilities by the external function.
+ $context = context_system::instance();
+ $roleid = $this->assignUserCapability('moodle/category:manage', $context->id);
+
+ // Retrieve category1 + sub-categories except not visible ones
+ $categories = core_course_external::get_categories(array(
+ array('key' => 'id', 'value' => $category1->id),
+ array('key' => 'visible', 'value' => 1)), 1);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories);
+
+ // Check we retrieve the good total number of categories.
+ $this->assertEquals(2, count($categories));
+
+ // Check the return values
+ foreach ($categories as $category) {
+ $generatedcat = $generatedcats[$category['id']];
+ $this->assertEquals($category['idnumber'], $generatedcat->idnumber);
+ $this->assertEquals($category['name'], $generatedcat->name);
+ $this->assertEquals($category['description'], $generatedcat->description);
+ $this->assertEquals($category['descriptionformat'], FORMAT_HTML);
+ }
+
+ // Check different params.
+ $categories = core_course_external::get_categories(array(
+ array('key' => 'id', 'value' => $category1->id),
+ array('key' => 'idnumber', 'value' => $category1->idnumber),
+ array('key' => 'visible', 'value' => 1)), 0);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories);
+
+ $this->assertEquals(1, count($categories));
+
+ // Retrieve categories from parent.
+ $categories = core_course_external::get_categories(array(
+ array('key' => 'parent', 'value' => $category3->id)), 1);
+ $this->assertEquals(2, count($categories));
+
+ // Retrieve all categories.
+ $categories = core_course_external::get_categories();
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories);
+
+ $this->assertEquals($DB->count_records('course_categories'), count($categories));
+
+ // Call without required capability (it will fail cause of the search on idnumber).
+ $this->unassignUserCapability('moodle/category:manage', $context->id, $roleid);
+ $this->setExpectedException('moodle_exception');
+ $categories = core_course_external::get_categories(array(
+ array('key' => 'id', 'value' => $category1->id),
+ array('key' => 'idnumber', 'value' => $category1->idnumber),
+ array('key' => 'visible', 'value' => 1)), 0);
+ }
+
+ /**
+ * Test update_categories
+ */
+ public function test_update_categories() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ // Set the required capabilities by the external function
+ $contextid = context_system::instance()->id;
+ $roleid = $this->assignUserCapability('moodle/category:manage', $contextid);
+
+ // Create base categories.
+ $category1data['idnumber'] = 'idnumbercat1';
+ $category1data['name'] = 'Category 1 for PHPunit test';
+ $category1data['description'] = 'Category 1 description';
+ $category1data['descriptionformat'] = FORMAT_MOODLE;
+ $category1 = self::getDataGenerator()->create_category($category1data);
+ $category2 = self::getDataGenerator()->create_category(
+ array('parent' => $category1->id));
+ $category3 = self::getDataGenerator()->create_category();
+ $category4 = self::getDataGenerator()->create_category(
+ array('parent' => $category3->id));
+ $category5 = self::getDataGenerator()->create_category(
+ array('parent' => $category4->id));
+
+ // We update all category1 attribut.
+ // Then we move cat4 and cat5 parent: cat3 => cat1
+ $categories = array(
+ array('id' => $category1->id,
+ 'name' => $category1->name . '_updated',
+ 'idnumber' => $category1->idnumber . '_updated',
+ 'description' => $category1->description . '_updated',
+ 'descriptionformat' => FORMAT_HTML,
+ 'theme' => $category1->theme),
+ array('id' => $category4->id, 'parent' => $category1->id));
+
+ core_course_external::update_categories($categories);
+
+ // Check the values were updated.
+ $dbcategories = $DB->get_records_select('course_categories',
+ 'id IN (' . $category1->id . ',' . $category2->id . ',' . $category2->id
+ . ',' . $category3->id . ',' . $category4->id . ',' . $category5->id .')');
+ $this->assertEquals($category1->name . '_updated',
+ $dbcategories[$category1->id]->name);
+ $this->assertEquals($category1->idnumber . '_updated',
+ $dbcategories[$category1->id]->idnumber);
+ $this->assertEquals($category1->description . '_updated',
+ $dbcategories[$category1->id]->description);
+ $this->assertEquals(FORMAT_HTML, $dbcategories[$category1->id]->descriptionformat);
+
+ // Check that category4 and category5 have been properly moved.
+ $this->assertEquals('/' . $category1->id . '/' . $category4->id,
+ $dbcategories[$category4->id]->path);
+ $this->assertEquals('/' . $category1->id . '/' . $category4->id . '/' . $category5->id,
+ $dbcategories[$category5->id]->path);
+
+ // Call without required capability.
+ $this->unassignUserCapability('moodle/category:manage', $contextid, $roleid);
+ $this->setExpectedException('required_capability_exception');
+ core_course_external::update_categories($categories);
+ }
+
+ /**
+ * Test create_courses
+ */
+ public function test_create_courses() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ // Enable course completion.
+ set_config('enablecompletion', 1);
+
+ // Set the required capabilities by the external function
+ $contextid = context_system::instance()->id;
+ $roleid = $this->assignUserCapability('moodle/course:create', $contextid);
+ $this->assignUserCapability('moodle/course:visibility', $contextid, $roleid);
+
+ $category = self::getDataGenerator()->create_category();
+
+ // Create base categories.
+ $course1['fullname'] = 'Test course 1';
+ $course1['shortname'] = 'Testcourse1';
+ $course1['categoryid'] = $category->id;
+ $course2['fullname'] = 'Test course 2';
+ $course2['shortname'] = 'Testcourse2';
+ $course2['categoryid'] = $category->id;
+ $course2['idnumber'] = 'testcourse2idnumber';
+ $course2['summary'] = 'Description for course 2';
+ $course2['summaryformat'] = FORMAT_MOODLE;
+ $course2['format'] = 'weeks';
+ $course2['showgrades'] = 1;
+ $course2['newsitems'] = 3;
+ $course2['startdate'] = 1420092000; // 01/01/2015
+ $course2['numsections'] = 4;
+ $course2['maxbytes'] = 100000;
+ $course2['showreports'] = 1;
+ $course2['visible'] = 0;
+ $course2['hiddensections'] = 0;
+ $course2['groupmode'] = 0;
+ $course2['groupmodeforce'] = 0;
+ $course2['defaultgroupingid'] = 0;
+ $course2['enablecompletion'] = 1;
+ $course2['completionstartonenrol'] = 1;
+ $course2['completionnotify'] = 1;
+ $course2['lang'] = 'en';
+ $course2['forcetheme'] = 'base';
+ $course3['fullname'] = 'Test course 3';
+ $course3['shortname'] = 'Testcourse3';
+ $course3['categoryid'] = $category->id;
+ $course3['format'] = 'topics';
+ $course3options = array('numsections' => 8,
+ 'hiddensections' => 1,
+ 'coursedisplay' => 1);
+ $course3['courseformatoptions'] = array();
+ foreach ($course3options as $key => $value) {
+ $course3['courseformatoptions'][] = array('name' => $key, 'value' => $value);
+ }
+ $courses = array($course1, $course2, $course3);
+
+ $createdcourses = core_course_external::create_courses($courses);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $createdcourses = external_api::clean_returnvalue(core_course_external::create_courses_returns(), $createdcourses);
+
+ // Check that right number of courses were created.
+ $this->assertEquals(3, count($createdcourses));
+
+ // Check that the courses were correctly created.
+ foreach ($createdcourses as $createdcourse) {
+ $courseinfo = course_get_format($createdcourse['id'])->get_course();
+
+ if ($createdcourse['shortname'] == $course2['shortname']) {
+ $this->assertEquals($courseinfo->fullname, $course2['fullname']);
+ $this->assertEquals($courseinfo->shortname, $course2['shortname']);
+ $this->assertEquals($courseinfo->category, $course2['categoryid']);
+ $this->assertEquals($courseinfo->idnumber, $course2['idnumber']);
+ $this->assertEquals($courseinfo->summary, $course2['summary']);
+ $this->assertEquals($courseinfo->summaryformat, $course2['summaryformat']);
+ $this->assertEquals($courseinfo->format, $course2['format']);
+ $this->assertEquals($courseinfo->showgrades, $course2['showgrades']);
+ $this->assertEquals($courseinfo->newsitems, $course2['newsitems']);
+ $this->assertEquals($courseinfo->startdate, $course2['startdate']);
+ $this->assertEquals($courseinfo->numsections, $course2['numsections']);
+ $this->assertEquals($courseinfo->maxbytes, $course2['maxbytes']);
+ $this->assertEquals($courseinfo->showreports, $course2['showreports']);
+ $this->assertEquals($courseinfo->visible, $course2['visible']);
+ $this->assertEquals($courseinfo->hiddensections, $course2['hiddensections']);
+ $this->assertEquals($courseinfo->groupmode, $course2['groupmode']);
+ $this->assertEquals($courseinfo->groupmodeforce, $course2['groupmodeforce']);
+ $this->assertEquals($courseinfo->defaultgroupingid, $course2['defaultgroupingid']);
+ $this->assertEquals($courseinfo->completionnotify, $course2['completionnotify']);
+ $this->assertEquals($courseinfo->lang, $course2['lang']);
+
+ if (!empty($CFG->allowcoursethemes)) {
+ $this->assertEquals($courseinfo->theme, $course2['forcetheme']);
+ }
+
+ // We enabled completion at the beginning of the test.
+ $this->assertEquals($courseinfo->enablecompletion, $course2['enablecompletion']);
+ $this->assertEquals($courseinfo->completionstartonenrol, $course2['completionstartonenrol']);
+
+ } else if ($createdcourse['shortname'] == $course1['shortname']) {
+ $courseconfig = get_config('moodlecourse');
+ $this->assertEquals($courseinfo->fullname, $course1['fullname']);
+ $this->assertEquals($courseinfo->shortname, $course1['shortname']);
+ $this->assertEquals($courseinfo->category, $course1['categoryid']);
+ $this->assertEquals($courseinfo->summaryformat, FORMAT_HTML);
+ $this->assertEquals($courseinfo->format, $courseconfig->format);
+ $this->assertEquals($courseinfo->showgrades, $courseconfig->showgrades);
+ $this->assertEquals($courseinfo->newsitems, $courseconfig->newsitems);
+ $this->assertEquals($courseinfo->maxbytes, $courseconfig->maxbytes);
+ $this->assertEquals($courseinfo->showreports, $courseconfig->showreports);
+ $this->assertEquals($courseinfo->groupmode, $courseconfig->groupmode);
+ $this->assertEquals($courseinfo->groupmodeforce, $courseconfig->groupmodeforce);
+ $this->assertEquals($courseinfo->defaultgroupingid, 0);
+ } else if ($createdcourse['shortname'] == $course3['shortname']) {
+ $this->assertEquals($courseinfo->fullname, $course3['fullname']);
+ $this->assertEquals($courseinfo->shortname, $course3['shortname']);
+ $this->assertEquals($courseinfo->category, $course3['categoryid']);
+ $this->assertEquals($courseinfo->format, $course3['format']);
+ $this->assertEquals($courseinfo->hiddensections, $course3options['hiddensections']);
+ $this->assertEquals($courseinfo->numsections, $course3options['numsections']);
+ $this->assertEquals($courseinfo->coursedisplay, $course3options['coursedisplay']);
+ } else {
+ throw moodle_exception('Unexpected shortname');
+ }
+ }
+
+ // Call without required capability
+ $this->unassignUserCapability('moodle/course:create', $contextid, $roleid);
+ $this->setExpectedException('required_capability_exception');
+ $createdsubcats = core_course_external::create_courses($courses);
+ }
+
+ /**
+ * Test delete_courses
+ */
+ public function test_delete_courses() {
+ global $DB, $USER;
+
+ $this->resetAfterTest(true);
+
+ // Admin can delete a course.
+ $this->setAdminUser();
+ // Validate_context() will fail as the email is not set by $this->setAdminUser().
+ $USER->email = 'emailtopass@contextvalidation.me';
+
+ $course1 = self::getDataGenerator()->create_course();
+ $course2 = self::getDataGenerator()->create_course();
+ $course3 = self::getDataGenerator()->create_course();
+
+ // Delete courses.
+ core_course_external::delete_courses(array($course1->id, $course2->id));
+
+ // Check $course 1 and 2 are deleted.
+ $notdeletedcount = $DB->count_records_select('course',
+ 'id IN ( ' . $course1->id . ',' . $course2->id . ')');
+ $this->assertEquals(0, $notdeletedcount);
+
+ // Fail when the user is not allow to access the course (enrolled) or is not admin.
+ $this->setGuestUser();
+ $this->setExpectedException('require_login_exception');
+ $createdsubcats = core_course_external::delete_courses(array($course3->id));
+ }
+
+ /**
+ * Test get_courses
+ */
+ public function test_get_courses () {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ $generatedcourses = array();
+ $coursedata['idnumber'] = 'idnumbercourse1';
+ $coursedata['fullname'] = 'Course 1 for PHPunit test';
+ $coursedata['summary'] = 'Course 1 description';
+ $coursedata['summaryformat'] = FORMAT_MOODLE;
+ $course1 = self::getDataGenerator()->create_course($coursedata);
+ $generatedcourses[$course1->id] = $course1;
+ $course2 = self::getDataGenerator()->create_course();
+ $generatedcourses[$course2->id] = $course2;
+ $course3 = self::getDataGenerator()->create_course(array('format' => 'topics'));
+ $generatedcourses[$course3->id] = $course3;
+
+ // Set the required capabilities by the external function.
+ $context = context_system::instance();
+ $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
+ $this->assignUserCapability('moodle/course:update',
+ context_course::instance($course1->id)->id, $roleid);
+ $this->assignUserCapability('moodle/course:update',
+ context_course::instance($course2->id)->id, $roleid);
+ $this->assignUserCapability('moodle/course:update',
+ context_course::instance($course3->id)->id, $roleid);
+
+ $courses = core_course_external::get_courses(array('ids' =>
+ array($course1->id, $course2->id)));
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $courses = external_api::clean_returnvalue(core_course_external::get_courses_returns(), $courses);
+
+ // Check we retrieve the good total number of categories.
+ $this->assertEquals(2, count($courses));
+
+ foreach ($courses as $course) {
+ $dbcourse = $generatedcourses[$course['id']];
+ $this->assertEquals($course['idnumber'], $dbcourse->idnumber);
+ $this->assertEquals($course['fullname'], $dbcourse->fullname);
+ $this->assertEquals($course['summary'], $dbcourse->summary);
+ $this->assertEquals($course['summaryformat'], FORMAT_HTML);
+ $this->assertEquals($course['shortname'], $dbcourse->shortname);
+ $this->assertEquals($course['categoryid'], $dbcourse->category);
+ $this->assertEquals($course['format'], $dbcourse->format);
+ $this->assertEquals($course['showgrades'], $dbcourse->showgrades);
+ $this->assertEquals($course['newsitems'], $dbcourse->newsitems);
+ $this->assertEquals($course['startdate'], $dbcourse->startdate);
+ $this->assertEquals($course['numsections'], $dbcourse->numsections);
+ $this->assertEquals($course['maxbytes'], $dbcourse->maxbytes);
+ $this->assertEquals($course['showreports'], $dbcourse->showreports);
+ $this->assertEquals($course['visible'], $dbcourse->visible);
+ $this->assertEquals($course['hiddensections'], $dbcourse->hiddensections);
+ $this->assertEquals($course['groupmode'], $dbcourse->groupmode);
+ $this->assertEquals($course['groupmodeforce'], $dbcourse->groupmodeforce);
+ $this->assertEquals($course['defaultgroupingid'], $dbcourse->defaultgroupingid);
+ $this->assertEquals($course['completionnotify'], $dbcourse->completionnotify);
+ $this->assertEquals($course['lang'], $dbcourse->lang);
+ $this->assertEquals($course['forcetheme'], $dbcourse->theme);
+ $this->assertEquals($course['completionstartonenrol'], $dbcourse->completionstartonenrol);
+ $this->assertEquals($course['enablecompletion'], $dbcourse->enablecompletion);
+ $this->assertEquals($course['completionstartonenrol'], $dbcourse->completionstartonenrol);
+ if ($dbcourse->format === 'topics') {
+ $this->assertEquals($course['courseformatoptions'], array(
+ array('name' => 'numsections', 'value' => $dbcourse->numsections),
+ array('name' => 'hiddensections', 'value' => $dbcourse->hiddensections),
+ array('name' => 'coursedisplay', 'value' => $dbcourse->coursedisplay),
+ ));
+ }
+ }
+
+ // Get all courses in the DB
+ $courses = core_course_external::get_courses(array());
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $courses = external_api::clean_returnvalue(core_course_external::get_courses_returns(), $courses);
+
+ $this->assertEquals($DB->count_records('course'), count($courses));
+ }
+
+ /**
+ * Test get_course_contents
+ */
+ public function test_get_course_contents() {
+ $this->resetAfterTest(true);
+
+ $course = self::getDataGenerator()->create_course();
+ $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
+ $forumcm = get_coursemodule_from_id('forum', $forum->cmid);
+ $forumcontext = context_module::instance($forum->cmid);
+ $data = $this->getDataGenerator()->create_module('data', array('assessed'=>1, 'scale'=>100, 'course'=>$course->id));
+ $datacontext = context_module::instance($data->cmid);
+ $datacm = get_coursemodule_from_instance('page', $data->id);
+ $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
+ $pagecontext = context_module::instance($page->cmid);
+ $pagecm = get_coursemodule_from_instance('page', $page->id);
+
+ // Set the required capabilities by the external function.
+ $context = context_course::instance($course->id);
+ $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
+ $this->assignUserCapability('moodle/course:update', $context->id, $roleid);
+
+ $courses = core_course_external::get_course_contents($course->id, array());
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $courses = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $courses);
+
+ // Check that the course has the 3 created modules
+ $this->assertEquals(3, count($courses[0]['modules']));
+ }
+
+ /**
+ * Test duplicate_course
+ */
+ public function test_duplicate_course() {
+ $this->resetAfterTest(true);
+
+ // Create one course with three modules.
+ $course = self::getDataGenerator()->create_course();
+ $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
+ $forumcm = get_coursemodule_from_id('forum', $forum->cmid);
+ $forumcontext = context_module::instance($forum->cmid);
+ $data = $this->getDataGenerator()->create_module('data', array('assessed'=>1, 'scale'=>100, 'course'=>$course->id));
+ $datacontext = context_module::instance($data->cmid);
+ $datacm = get_coursemodule_from_instance('page', $data->id);
+ $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
+ $pagecontext = context_module::instance($page->cmid);
+ $pagecm = get_coursemodule_from_instance('page', $page->id);
+
+ // Set the required capabilities by the external function.
+ $coursecontext = context_course::instance($course->id);
+ $categorycontext = context_coursecat::instance($course->category);
+ $roleid = $this->assignUserCapability('moodle/course:create', $categorycontext->id);
+ $this->assignUserCapability('moodle/course:view', $categorycontext->id, $roleid);
+ $this->assignUserCapability('moodle/restore:restorecourse', $categorycontext->id, $roleid);
+ $this->assignUserCapability('moodle/backup:backupcourse', $coursecontext->id, $roleid);
+ $this->assignUserCapability('moodle/backup:configure', $coursecontext->id, $roleid);
+ // Optional capabilities to copy user data.
+ $this->assignUserCapability('moodle/backup:userinfo', $coursecontext->id, $roleid);
+ $this->assignUserCapability('moodle/restore:userinfo', $categorycontext->id, $roleid);
+
+ $newcourse['fullname'] = 'Course duplicate';
+ $newcourse['shortname'] = 'courseduplicate';
+ $newcourse['categoryid'] = $course->category;
+ $newcourse['visible'] = true;
+ $newcourse['options'][] = array('name' => 'users', 'value' => true);
+
+ $duplicate = core_course_external::duplicate_course($course->id, $newcourse['fullname'],
+ $newcourse['shortname'], $newcourse['categoryid'], $newcourse['visible'], $newcourse['options']);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $duplicate = external_api::clean_returnvalue(core_course_external::duplicate_course_returns(), $duplicate);
+
+ // Check that the course has been duplicated.
+ $this->assertEquals($newcourse['shortname'], $duplicate['shortname']);
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Toggles the manual completion flag for a particular activity or course completion
+ * and the current user.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require_once('../config.php');
+require_once($CFG->libdir.'/completionlib.php');
+
+// Parameters
+$cmid = optional_param('id', 0, PARAM_INT);
+$courseid = optional_param('course', 0, PARAM_INT);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+if (!$cmid && !$courseid) {
+ print_error('invalidarguments');
+}
+
+// Process self completion
+if ($courseid) {
+ $PAGE->set_url(new moodle_url('/course/togglecompletion.php', array('course'=>$courseid)));
+
+ // Check user is logged in
+ $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
+ $context = context_course::instance($course->id);
+ require_login($course);
+
+ $completion = new completion_info($course);
+ if (!$completion->is_enabled()) {
+ throw new moodle_exception('completionnotenabled', 'completion');
+ } elseif (!$completion->is_tracked_user($USER->id)) {
+ throw new moodle_exception('nottracked', 'completion');
+ }
+
+ // Check if we are marking a user complete via the completion report
+ $user = optional_param('user', 0, PARAM_INT);
+ $rolec = optional_param('rolec', 0, PARAM_INT);
+
+ if ($user && $rolec) {
+ require_sesskey();
+
+ completion_criteria::factory(array('id'=>$rolec, 'criteriatype'=>COMPLETION_CRITERIA_TYPE_ROLE)); //TODO: this is dumb, because it does not fetch the data?!?!
+ $criteria = completion_criteria_role::fetch(array('id'=>$rolec));
+
+ if ($criteria and user_has_role_assignment($USER->id, $criteria->role, $context->id)) {
+ $criteria_completions = $completion->get_completions($user, COMPLETION_CRITERIA_TYPE_ROLE);
+
+ foreach ($criteria_completions as $criteria_completion) {
+ if ($criteria_completion->criteriaid == $rolec) {
+ $criteria->complete($criteria_completion);
+ break;
+ }
+ }
+ }
+
+ // Return to previous page
+ if (!empty($_SERVER['HTTP_REFERER'])) {
+ redirect($_SERVER['HTTP_REFERER']);
+ } else {
+ redirect('view.php?id='.$course->id);
+ }
+
+ } else {
+
+ // Confirm with user
+ if ($confirm and confirm_sesskey()) {
+ $completion = $completion->get_completion($USER->id, COMPLETION_CRITERIA_TYPE_SELF);
+
+ if (!$completion) {
+ print_error('noselfcompletioncriteria', 'completion');
+ }
+
+ // Check if the user has already marked themselves as complete
+ if ($completion->is_complete()) {
+ print_error('useralreadymarkedcomplete', 'completion');
+ }
+
+ $completion->mark_complete();
+
+ redirect($CFG->wwwroot.'/course/view.php?id='.$courseid);
+ return;
+ }
+
+ $strconfirm = get_string('confirmselfcompletion', 'completion');
+ $PAGE->set_title($strconfirm);
+ $PAGE->set_heading($course->fullname);
+ $PAGE->navbar->add($strconfirm);
+ echo $OUTPUT->header();
+ $buttoncontinue = new single_button(new moodle_url('/course/togglecompletion.php', array('course'=>$courseid, 'confirm'=>1, 'sesskey'=>sesskey())), get_string('yes'), 'post');
+ $buttoncancel = new single_button(new moodle_url('/course/view.php', array('id'=>$courseid)), get_string('no'), 'get');
+ echo $OUTPUT->confirm($strconfirm, $buttoncontinue, $buttoncancel);
+ echo $OUTPUT->footer();
+ exit;
+ }
+}
+
+
+$targetstate = required_param('completionstate', PARAM_INT);
+$fromajax = optional_param('fromajax', 0, PARAM_INT);
+
+$PAGE->set_url('/course/togglecompletion.php', array('id'=>$cmid, 'completionstate'=>$targetstate));
+
+switch($targetstate) {
+ case COMPLETION_COMPLETE:
+ case COMPLETION_INCOMPLETE:
+ break;
+ default:
+ print_error('unsupportedstate');
+}
+
+// Get course-modules entry
+$cm = get_coursemodule_from_id(null, $cmid, null, false, MUST_EXIST);
+$course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
+
+// Check user is logged in
+require_login($course, false, $cm);
+
+if (isguestuser() or !confirm_sesskey()) {
+ print_error('error');
+}
+
+// Now change state
+$completion = new completion_info($course);
+if (!$completion->is_enabled()) {
+ throw new moodle_exception('completionnotenabled', 'completion');
+} elseif (!$completion->is_tracked_user($USER->id)) {
+ throw new moodle_exception('nottracked', 'completion');
+}
+
+// Check completion state is manual
+if($cm->completion != COMPLETION_TRACKING_MANUAL) {
+ error_or_ajax('cannotmanualctrack', $fromajax);
+}
+
+$completion->update_state($cm, $targetstate);
+
+// And redirect back to course
+if ($fromajax) {
+ print 'OK';
+} else {
+ // In case of use in other areas of code we allow a 'backto' parameter,
+ // otherwise go back to course page
+ $backto = optional_param('backto', 'view.php?id='.$course->id, PARAM_URL);
+ redirect($backto);
+}
+
+// utility functions
+
+function error_or_ajax($message, $fromajax) {
+ if ($fromajax) {
+ print get_string($message, 'error');
+ exit;
+ } else {
+ print_error($message);
+ }
+}
+
--- /dev/null
+<?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/>.
+
+/**
+ * Display user activity reports for a course
+ *
+ * @copyright 1999 Martin Dougiamas http://dougiamas.com
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package course
+ */
+
+require_once("../config.php");
+require_once("lib.php");
+
+$id = required_param('id',PARAM_INT); // course id
+$user = required_param('user',PARAM_INT); // user id
+$mode = optional_param('mode', "todaylogs", PARAM_ALPHA);
+
+$url = new moodle_url('/course/user.php', array('id'=>$id,'user'=>$user, 'mode'=>$mode));
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$user = $DB->get_record("user", array("id"=>$user, 'deleted'=>0), '*', MUST_EXIST);
+
+if ($mode === 'outline' or $mode === 'complete') {
+ $url = new moodle_url('/report/outline/user.php', array('id'=>$user->id, 'course'=>$course->id, 'mode'=>$mode));
+ redirect($url);
+}
+if ($mode === 'todaylogs' or $mode === 'alllogs') {
+ $logmode = ($mode === 'todaylogs') ? 'today' : 'all';
+ $url = new moodle_url('/report/log/user.php', array('id'=>$user->id, 'course'=>$course->id, 'mode'=>$logmode));
+ redirect($url);
+}
+if ($mode === 'stats') {
+ $url = new moodle_url('/report/stats/user.php', array('id'=>$user->id, 'course'=>$course->id));
+ redirect($url);
+}
+if ($mode === 'coursecompletions' or $mode === 'coursecompletion') {
+ $url = new moodle_url('/report/completion/user.php', array('id'=>$user->id, 'course'=>$course->id));
+ redirect($url);
+}
+
+$coursecontext = context_course::instance($course->id);
+$personalcontext = context_user::instance($user->id);
+
+$PAGE->set_url('/course/user.php', array('id'=>$id, 'user'=>$user->id, 'mode'=>$mode));
+
+require_login();
+$PAGE->set_pagelayout('admin');
+if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) and !is_enrolled($coursecontext)) {
+ // do not require parents to be enrolled in courses ;-)
+ $PAGE->set_course($course);
+} else {
+ require_login($course);
+}
+
+if ($user->deleted) {
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('userdeleted'));
+ echo $OUTPUT->footer();
+ die;
+}
+
+// prepare list of allowed modes
+$myreports = ($course->showreports and $USER->id == $user->id);
+$anyreport = has_capability('moodle/user:viewuseractivitiesreport', $personalcontext);
+
+$modes = array();
+
+if (has_capability('moodle/grade:viewall', $coursecontext)) {
+ //ok - can view all course grades
+ $modes[] = 'grade';
+
+} else if ($course->showgrades and $user->id == $USER->id and has_capability('moodle/grade:view', $coursecontext)) {
+ //ok - can view own grades
+ $modes[] = 'grade';
+
+} else if ($course->showgrades and has_capability('moodle/grade:viewall', $personalcontext)) {
+ // ok - can view grades of this user - parent most probably
+ $modes[] = 'grade';
+
+} else if ($course->showgrades and $anyreport) {
+ // ok - can view grades of this user - parent most probably
+ $modes[] = 'grade';
+}
+
+if (empty($modes)) {
+ require_capability('moodle/user:viewuseractivitiesreport', $personalcontext);
+}
+
+if (!in_array($mode, $modes)) {
+ // forbidden or non-existent mode
+ $mode = reset($modes);
+}
+
+add_to_log($course->id, "course", "user report", "user.php?id=$course->id&user=$user->id&mode=$mode", "$user->id");
+
+$stractivityreport = get_string("activityreport");
+
+$PAGE->navigation->extend_for_user($user);
+$PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for reasons and for full commit reference for reversal when fixed.
+$PAGE->set_title("$course->shortname: $stractivityreport ($mode)");
+$PAGE->set_heading($course->fullname);
+echo $OUTPUT->header();
+
+switch ($mode) {
+ case "grade":
+ if (empty($CFG->grade_profilereport) or !file_exists($CFG->dirroot.'/grade/report/'.$CFG->grade_profilereport.'/lib.php')) {
+ $CFG->grade_profilereport = 'user';
+ }
+ require_once $CFG->libdir.'/gradelib.php';
+ require_once $CFG->dirroot.'/grade/lib.php';
+ require_once $CFG->dirroot.'/grade/report/'.$CFG->grade_profilereport.'/lib.php';
+
+ $functionname = 'grade_report_'.$CFG->grade_profilereport.'_profilereport';
+ if (function_exists($functionname)) {
+ $functionname($course, $user);
+ }
+ break;
+
+ break;
+ default:
+ // can not be reached ;-)
+}
+
+
+echo $OUTPUT->footer();
--- /dev/null
+<?php
+
+// Display the course home page.
+
+ require_once('../config.php');
+ require_once('lib.php');
+ require_once($CFG->dirroot.'/mod/forum/lib.php');
+ require_once($CFG->libdir.'/conditionlib.php');
+ require_once($CFG->libdir.'/completionlib.php');
+
+ $id = optional_param('id', 0, PARAM_INT);
+ $name = optional_param('name', '', PARAM_RAW);
+ $edit = optional_param('edit', -1, PARAM_BOOL);
+ $hide = optional_param('hide', 0, PARAM_INT);
+ $show = optional_param('show', 0, PARAM_INT);
+ $idnumber = optional_param('idnumber', '', PARAM_RAW);
+ $sectionid = optional_param('sectionid', 0, PARAM_INT);
+ $section = optional_param('section', 0, PARAM_INT);
+ $move = optional_param('move', 0, PARAM_INT);
+ $marker = optional_param('marker',-1 , PARAM_INT);
+ $switchrole = optional_param('switchrole',-1, PARAM_INT);
+ $modchooser = optional_param('modchooser', -1, PARAM_BOOL);
+ $return = optional_param('return', 0, PARAM_LOCALURL);
+
+ $params = array();
+ if (!empty($name)) {
+ $params = array('shortname' => $name);
+ } else if (!empty($idnumber)) {
+ $params = array('idnumber' => $idnumber);
+ } else if (!empty($id)) {
+ $params = array('id' => $id);
+ }else {
+ print_error('unspecifycourseid', 'error');
+ }
+
+ $course = $DB->get_record('course', $params, '*', MUST_EXIST);
+
+ $urlparams = array('id' => $course->id);
+
+ // Sectionid should get priority over section number
+ if ($sectionid) {
+ $section = $DB->get_field('course_sections', 'section', array('id' => $sectionid, 'course' => $course->id), MUST_EXIST);
+ }
+ if ($section) {
+ $urlparams['section'] = $section;
+ }
+
+ $PAGE->set_url('/course/view.php', $urlparams); // Defined here to avoid notices on errors etc
+
+ // Prevent caching of this page to stop confusion when changing page after making AJAX changes
+ $PAGE->set_cacheable(false);
+
+ preload_course_contexts($course->id);
+ $context = context_course::instance($course->id, MUST_EXIST);
+
+ // Remove any switched roles before checking login
+ if ($switchrole == 0 && confirm_sesskey()) {
+ role_switch($switchrole, $context);
+ }
+
+ require_login($course);
+
+ // Switchrole - sanity check in cost-order...
+ $reset_user_allowed_editing = false;
+ if ($switchrole > 0 && confirm_sesskey() &&
+ has_capability('moodle/role:switchroles', $context)) {
+ // is this role assignable in this context?
+ // inquiring minds want to know...
+ $aroles = get_switchable_roles($context);
+ if (is_array($aroles) && isset($aroles[$switchrole])) {
+ role_switch($switchrole, $context);
+ // Double check that this role is allowed here
+ require_login($course);
+ }
+ // reset course page state - this prevents some weird problems ;-)
+ $USER->activitycopy = false;
+ $USER->activitycopycourse = NULL;
+ unset($USER->activitycopyname);
+ unset($SESSION->modform);
+ $USER->editing = 0;
+ $reset_user_allowed_editing = true;
+ }
+
+ //If course is hosted on an external server, redirect to corresponding
+ //url with appropriate authentication attached as parameter
+ if (file_exists($CFG->dirroot .'/course/externservercourse.php')) {
+ include $CFG->dirroot .'/course/externservercourse.php';
+ if (function_exists('extern_server_course')) {
+ if ($extern_url = extern_server_course($course)) {
+ redirect($extern_url);
+ }
+ }
+ }
+
+
+ require_once($CFG->dirroot.'/calendar/lib.php'); /// This is after login because it needs $USER
+
+ $logparam = 'id='. $course->id;
+ $loglabel = 'view';
+ $infoid = $course->id;
+ if ($section and $section > 0) {
+ $loglabel = 'view section';
+
+ // Get section details and check it exists.
+ $modinfo = get_fast_modinfo($course);
+ $coursesections = $modinfo->get_section_info($section, MUST_EXIST);
+
+ // Check user is allowed to see it.
+ if (!$coursesections->uservisible) {
+ // Note: We actually already know they don't have this capability
+ // or uservisible would have been true; this is just to get the
+ // correct error message shown.
+ require_capability('moodle/course:viewhiddensections', $context);
+ }
+ $infoid = $coursesections->id;
+ $logparam .= '§ionid='. $infoid;
+ }
+ add_to_log($course->id, 'course', $loglabel, "view.php?". $logparam, $infoid);
+
+ // Fix course format if it is no longer installed
+ $course->format = course_get_format($course)->get_format();
+
+ $PAGE->set_pagelayout('course');
+ $PAGE->set_pagetype('course-view-' . $course->format);
+ $PAGE->set_other_editing_capability('moodle/course:manageactivities');
+
+ // Preload course format renderer before output starts.
+ // This is a little hacky but necessary since
+ // format.php is not included until after output starts
+ if (file_exists($CFG->dirroot.'/course/format/'.$course->format.'/renderer.php')) {
+ require_once($CFG->dirroot.'/course/format/'.$course->format.'/renderer.php');
+ if (class_exists('format_'.$course->format.'_renderer')) {
+ // call get_renderer only if renderer is defined in format plugin
+ // otherwise an exception would be thrown
+ $PAGE->get_renderer('format_'. $course->format);
+ }
+ }
+
+ if ($reset_user_allowed_editing) {
+ // ugly hack
+ unset($PAGE->_user_allowed_editing);
+ }
+
+ if (!isset($USER->editing)) {
+ $USER->editing = 0;
+ }
+ if ($PAGE->user_allowed_editing()) {
+ if (($edit == 1) and confirm_sesskey()) {
+ $USER->editing = 1;
+ // Redirect to site root if Editing is toggled on frontpage
+ if ($course->id == SITEID) {
+ redirect($CFG->wwwroot .'/?redirect=0');
+ } else if (!empty($return)) {
+ redirect($CFG->wwwroot . $return);
+ } else {
+ $url = new moodle_url($PAGE->url, array('notifyeditingon' => 1));
+ redirect($url);
+ }
+ } else if (($edit == 0) and confirm_sesskey()) {
+ $USER->editing = 0;
+ if(!empty($USER->activitycopy) && $USER->activitycopycourse == $course->id) {
+ $USER->activitycopy = false;
+ $USER->activitycopycourse = NULL;
+ }
+ // Redirect to site root if Editing is toggled on frontpage
+ if ($course->id == SITEID) {
+ redirect($CFG->wwwroot .'/?redirect=0');
+ } else if (!empty($return)) {
+ redirect($CFG->wwwroot . $return);
+ } else {
+ redirect($PAGE->url);
+ }
+ }
+ if (($modchooser == 1) && confirm_sesskey()) {
+ set_user_preference('usemodchooser', $modchooser);
+ } else if (($modchooser == 0) && confirm_sesskey()) {
+ set_user_preference('usemodchooser', $modchooser);
+ }
+
+ if (has_capability('moodle/course:sectionvisibility', $context)) {
+ if ($hide && confirm_sesskey()) {
+ set_section_visible($course->id, $hide, '0');
+ redirect($PAGE->url);
+ }
+
+ if ($show && confirm_sesskey()) {
+ set_section_visible($course->id, $show, '1');
+ redirect($PAGE->url);
+ }
+ }
+
+ if (has_capability('moodle/course:update', $context)) {
+ if (!empty($section)) {
+ if (!empty($move) and has_capability('moodle/course:movesections', $context) and confirm_sesskey()) {
+ $destsection = $section + $move;
+ if (move_section_to($course, $section, $destsection)) {
+ if ($course->id == SITEID) {
+ redirect($CFG->wwwroot . '/?redirect=0');
+ } else {
+ redirect(course_get_url($course));
+ }
+ } else {
+ echo $OUTPUT->notification('An error occurred while moving a section');
+ }
+ }
+ }
+ }
+ } else {
+ $USER->editing = 0;
+ }
+
+ $SESSION->fromdiscussion = $PAGE->url->out(false);
+
+
+ if ($course->id == SITEID) {
+ // This course is not a real course.
+ redirect($CFG->wwwroot .'/');
+ }
+
+ $completion = new completion_info($course);
+ if ($completion->is_enabled() && ajaxenabled()) {
+ $PAGE->requires->string_for_js('completion-title-manual-y', 'completion');
+ $PAGE->requires->string_for_js('completion-title-manual-n', 'completion');
+ $PAGE->requires->string_for_js('completion-alt-manual-y', 'completion');
+ $PAGE->requires->string_for_js('completion-alt-manual-n', 'completion');
+
+ $PAGE->requires->js_init_call('M.core_completion.init');
+ }
+
+ // We are currently keeping the button here from 1.x to help new teachers figure out
+ // what to do, even though the link also appears in the course admin block. It also
+ // means you can back out of a situation where you removed the admin block. :)
+ if ($PAGE->user_allowed_editing()) {
+ $buttons = $OUTPUT->edit_button($PAGE->url);
+ $PAGE->set_button($buttons);
+ }
+
+ $PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+ $PAGE->set_heading($course->fullname);
+ echo $OUTPUT->header();
+
+ if ($completion->is_enabled() && ajaxenabled()) {
+ // This value tracks whether there has been a dynamic change to the page.
+ // It is used so that if a user does this - (a) set some tickmarks, (b)
+ // go to another page, (c) clicks Back button - the page will
+ // automatically reload. Otherwise it would start with the wrong tick
+ // values.
+ echo html_writer::start_tag('form', array('action'=>'.', 'method'=>'get'));
+ echo html_writer::start_tag('div');
+ echo html_writer::empty_tag('input', array('type'=>'hidden', 'id'=>'completion_dynamic_change', 'name'=>'completion_dynamic_change', 'value'=>'0'));
+ echo html_writer::end_tag('div');
+ echo html_writer::end_tag('form');
+ }
+
+ // Course wrapper start.
+ echo html_writer::start_tag('div', array('class'=>'course-content'));
+
+ // make sure that section 0 exists (this function will create one if it is missing)
+ course_create_sections_if_missing($course, 0);
+
+ // get information about course modules and existing module types
+ // format.php in course formats may rely on presence of these variables
+ $modinfo = get_fast_modinfo($course);
+ $modnames = get_module_types_names();
+ $modnamesplural = get_module_types_names(true);
+ $modnamesused = $modinfo->get_used_module_names();
+ $mods = $modinfo->get_cms();
+ $sections = $modinfo->get_section_info_all();
+
+ // CAUTION, hacky fundamental variable defintion to follow!
+ // Note that because of the way course fromats are constructed though
+ // inclusion we pass parameters around this way..
+ $displaysection = $section;
+
+ // Include the actual course format.
+ require($CFG->dirroot .'/course/format/'. $course->format .'/format.php');
+ // Content wrapper end.
+
+ echo html_writer::end_tag('div');
+
+ // Include course AJAX
+ if (include_course_ajax($course, $modnamesused)) {
+ // Add the module chooser
+ $renderer = $PAGE->get_renderer('core', 'course');
+ echo $renderer->course_modchooser(get_module_metadata($course, $modnames, $displaysection), $course);
+ }
+
+ echo $OUTPUT->footer();
--- /dev/null
+YUI.add('moodle-course-coursebase', function(Y) {
+
+ /**
+ * The coursebase class
+ */
+ var COURSEBASENAME = 'course-coursebase';
+
+ var COURSEBASE = function() {
+ COURSEBASE.superclass.constructor.apply(this, arguments);
+ }
+
+ Y.extend(COURSEBASE, Y.Base, {
+ // Registered Modules
+ registermodules : [],
+
+ /**
+ * Initialize the coursebase module
+ */
+ initializer : function(config) {
+ // We don't actually perform any work here
+ },
+
+ /**
+ * Register a new Javascript Module
+ *
+ * @param object The instantiated module to call functions on
+ */
+ register_module : function(object) {
+ this.registermodules.push(object);
+ },
+
+ /**
+ * Invoke the specified function in all registered modules with the given arguments
+ *
+ * @param functionname The name of the function to call
+ * @param args The argument supplied to the function
+ */
+ invoke_function : function(functionname, args) {
+ for (module in this.registermodules) {
+ if (functionname in this.registermodules[module]) {
+ this.registermodules[module][functionname](args);
+ }
+ }
+ }
+ },
+ {
+ NAME : COURSEBASENAME,
+ ATTRS : {}
+ }
+ );
+
+ // Ensure that M.course exists and that coursebase is initialised correctly
+ M.course = M.course || {};
+ M.course.coursebase = M.course.coursebase || new COURSEBASE();
+
+ // Abstract functions that needs to be defined per format (course/format/somename/format.js)
+ M.course.format = M.course.format || {}
+
+ /**
+ * Swap section (should be defined in format.js if requred)
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {string} node1 node to swap to
+ * @param {string} node2 node to swap with
+ * @return {NodeList} section list
+ */
+ M.course.format.swap_sections = M.course.format.swap_sections || function(Y, node1, node2) {
+ return null;
+ }
+
+ /**
+ * Process sections after ajax response (should be defined in format.js)
+ * If some response is expected, we pass it over to format, as it knows better
+ * hot to process it.
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {NodeList} list of sections
+ * @param {array} response ajax response
+ * @param {string} sectionfrom first affected section
+ * @param {string} sectionto last affected section
+ * @return void
+ */
+ M.course.format.process_sections = M.course.format.process_sections || function(Y, sectionlist, response, sectionfrom, sectionto) {
+ return null;
+ }
+
+ /**
+ * Get sections config for this format, for examples see function definition
+ * in the formats.
+ *
+ * @return {object} section list configuration
+ */
+ M.course.format.get_config = M.course.format.get_config || function() {
+ return {
+ container_node : null, // compulsory
+ container_class : null, // compulsory
+ section_wrapper_node : null, // optional
+ section_wrapper_class : null, // optional
+ section_node : null, // compulsory
+ section_class : null // compulsory
+ }
+ }
+
+ /**
+ * Get section list for this format (usually items inside container_node.container_class selector)
+ *
+ * @param {YUI} Y YUI3 instance
+ * @return {string} section selector
+ */
+ M.course.format.get_section_selector = M.course.format.get_section_selector || function(Y) {
+ var config = M.course.format.get_config();
+ if (config.section_node && config.section_class) {
+ return config.section_node + '.' + config.section_class;
+ }
+ console.log('section_node and section_class are not defined in M.course.format.get_config');
+ return null;
+ }
+
+ /**
+ * Get section wraper for this format (only used in case when each
+ * container_node.container_class node is wrapped in some other element).
+ *
+ * @param {YUI} Y YUI3 instance
+ * @return {string} section wrapper selector or M.course.format.get_section_selector
+ * if section_wrapper_node and section_wrapper_class are not defined in the format config.
+ */
+ M.course.format.get_section_wrapper = M.course.format.get_section_wrapper || function(Y) {
+ var config = M.course.format.get_config();
+ if (config.section_wrapper_node && config.section_wrapper_class) {
+ return config.section_wrapper_node + '.' + config.section_wrapper_class;
+ }
+ return M.course.format.get_section_selector(Y);
+ }
+
+ /**
+ * Get the tag of container node
+ *
+ * @return {string} tag of container node.
+ */
+ M.course.format.get_containernode = M.course.format.get_containernode || function() {
+ var config = M.course.format.get_config();
+ if (config.container_node) {
+ return config.container_node;
+ } else {
+ console.log('container_node is not defined in M.course.format.get_config');
+ }
+ }
+
+ /**
+ * Get the class of container node
+ *
+ * @return {string} class of the container node.
+ */
+ M.course.format.get_containerclass = M.course.format.get_containerclass || function() {
+ var config = M.course.format.get_config();
+ if (config.container_class) {
+ return config.container_class;
+ } else {
+ console.log('container_class is not defined in M.course.format.get_config');
+ }
+ }
+
+ /**
+ * Get the tag of draggable node (section wrapper if exists, otherwise section)
+ *
+ * @return {string} tag of the draggable node.
+ */
+ M.course.format.get_sectionwrappernode = M.course.format.get_sectionwrappernode || function() {
+ var config = M.course.format.get_config();
+ if (config.section_wrapper_node) {
+ return config.section_wrapper_node;
+ } else {
+ return config.section_node;
+ }
+ }
+
+ /**
+ * Get the class of draggable node (section wrapper if exists, otherwise section)
+ *
+ * @return {string} class of the draggable node.
+ */
+ M.course.format.get_sectionwrapperclass = M.course.format.get_sectionwrapperclass || function() {
+ var config = M.course.format.get_config();
+ if (config.section_wrapper_class) {
+ return config.section_wrapper_class;
+ } else {
+ return config.section_class;
+ }
+ }
+
+ /**
+ * Get the tag of section node
+ *
+ * @return {string} tag of section node.
+ */
+ M.course.format.get_sectionnode = M.course.format.get_sectionnode || function() {
+ var config = M.course.format.get_config();
+ if (config.section_node) {
+ return config.section_node;
+ } else {
+ console.log('section_node is not defined in M.course.format.get_config');
+ }
+ }
+
+ /**
+ * Get the class of section node
+ *
+ * @return {string} class of the section node.
+ */
+ M.course.format.get_sectionclass = M.course.format.get_sectionclass || function() {
+ var config = M.course.format.get_config();
+ if (config.section_class) {
+ return config.section_class;
+ } else {
+ console.log('section_class is not defined in M.course.format.get_config');
+ }
+
+ }
+
+},
+'@VERSION@', {
+ requires : ['base', 'node']
+}
+);
--- /dev/null
+YUI.add('moodle-course-dragdrop', function(Y) {
+
+ var CSS = {
+ ACTIVITY : 'activity',
+ COMMANDSPAN : 'span.commands',
+ CONTENT : 'content',
+ COURSECONTENT : 'course-content',
+ EDITINGMOVE : 'editing_move',
+ ICONCLASS : 'iconsmall',
+ JUMPMENU : 'jumpmenu',
+ LEFT : 'left',
+ LIGHTBOX : 'lightbox',
+ MOVEDOWN : 'movedown',
+ MOVEUP : 'moveup',
+ PAGECONTENT : 'page-content',
+ RIGHT : 'right',
+ SECTION : 'section',
+ SECTIONADDMENUS : 'section_add_menus',
+ SECTIONHANDLE : 'section-handle',
+ SUMMARY : 'summary'
+ };
+
+ var DRAGSECTION = function() {
+ DRAGSECTION.superclass.constructor.apply(this, arguments);
+ };
+ Y.extend(DRAGSECTION, M.core.dragdrop, {
+ sectionlistselector : null,
+
+ initializer : function(params) {
+ // Set group for parent class
+ this.groups = ['section'];
+ this.samenodeclass = M.course.format.get_sectionwrapperclass();
+ this.parentnodeclass = M.course.format.get_containerclass();
+
+ // Check if we are in single section mode
+ if (Y.Node.one('.'+CSS.JUMPMENU)) {
+ return false;
+ }
+ // Initialise sections dragging
+ this.sectionlistselector = M.course.format.get_section_wrapper(Y);
+ if (this.sectionlistselector) {
+ this.sectionlistselector = '.'+CSS.COURSECONTENT+' '+this.sectionlistselector;
+ this.setup_for_section(this.sectionlistselector);
+
+ // Make each li element in the lists of sections draggable
+ var nodeselector = this.sectionlistselector.slice(CSS.COURSECONTENT.length+2);
+ var del = new Y.DD.Delegate({
+ container: '.'+CSS.COURSECONTENT,
+ nodes: nodeselector,
+ target: true,
+ handles: ['.'+CSS.LEFT],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#'+CSS.PAGECONTENT,
+ stickY: true
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @param baseselector The CSS selector or node to limit scope to
+ * @return void
+ */
+ setup_for_section : function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ // Determine the section ID
+ var sectionid = this.get_section_id(sectionnode);
+
+ // We skip the top section as it is not draggable
+ if (sectionid > 0) {
+ // Remove move icons
+ var movedown = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEDOWN);
+ var moveup = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEUP);
+
+ // Add dragger icon
+ var title = M.util.get_string('movesection', 'moodle', sectionid);
+ var cssleft = sectionnode.one('.'+CSS.LEFT);
+
+ if ((movedown || moveup) && cssleft) {
+ cssleft.setStyle('cursor', 'move');
+ cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
+
+ if (moveup) {
+ moveup.remove();
+ }
+ if (movedown) {
+ movedown.remove();
+ }
+ }
+ }
+ }, this);
+ },
+
+ get_section_id : function(node) {
+ return Number(node.get('id').replace(/section-/i, ''));
+ },
+
+ /*
+ * Drag-dropping related functions
+ */
+ drag_start : function(e) {
+ // Get our drag object
+ var drag = e.target;
+ // Creat a dummy structure of the outer elemnents for clean styles application
+ var containernode = Y.Node.create('<'+M.course.format.get_containernode()+'></'+M.course.format.get_containernode()+'>');
+ containernode.addClass(M.course.format.get_containerclass());
+ var sectionnode = Y.Node.create('<'+ M.course.format.get_sectionwrappernode()+'></'+ M.course.format.get_sectionwrappernode()+'>');
+ sectionnode.addClass( M.course.format.get_sectionwrapperclass());
+ sectionnode.setStyle('margin', 0);
+ sectionnode.setContent(drag.get('node').get('innerHTML'));
+ containernode.appendChild(sectionnode);
+ drag.get('dragNode').setContent(containernode);
+ drag.get('dragNode').addClass(CSS.COURSECONTENT);
+ },
+
+ drag_dropmiss : function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ drop_hit : function(e) {
+ var drag = e.drag;
+ // Get a reference to our drag node
+ var dragnode = drag.get('node');
+ var dropnode = e.drop.get('node');
+ // Prepare some variables
+ var dragnodeid = Number(this.get_section_id(dragnode));
+ var dropnodeid = Number(this.get_section_id(dropnode));
+
+ var loopstart = dragnodeid;
+ var loopend = dropnodeid;
+
+ if (this.goingup) {
+ loopstart = dropnodeid;
+ loopend = dragnodeid;
+ }
+
+ // Get the list of nodes
+ drag.get('dragNode').removeClass(CSS.COURSECONTENT);
+ var sectionlist = Y.Node.all(this.sectionlistselector);
+
+ // Add lightbox if it not there
+ var lightbox = M.util.add_lightbox(Y, dragnode);
+
+ var params = {};
+
+ // Handle any variables which we must pass back through to
+ var pageparams = this.get('config').pageparams;
+ for (varname in pageparams) {
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'section';
+ params.field = 'move';
+ params.id = dragnodeid;
+ params.value = dropnodeid;
+
+ // Do AJAX request
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start : function(tid) {
+ lightbox.show();
+ },
+ success: function(tid, response) {
+ // Update section titles, we can't simply swap them as
+ // they might have custom title
+ try {
+ var responsetext = Y.JSON.parse(response.responseText);
+ if (responsetext.error) {
+ new M.core.ajaxException(responsetext);
+ }
+ M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
+ } catch (e) {}
+
+ // Classic bubble sort algorithm is applied to the section
+ // nodes between original drag node location and the new one.
+ do {
+ var swapped = false;
+ for (var i = loopstart; i <= loopend; i++) {
+ if (this.get_section_id(sectionlist.item(i-1)) > this.get_section_id(sectionlist.item(i))) {
+ // Swap section id
+ var sectionid = sectionlist.item(i-1).get('id');
+ sectionlist.item(i-1).set('id', sectionlist.item(i).get('id'));
+ sectionlist.item(i).set('id', sectionid);
+ // See what format needs to swap
+ M.course.format.swap_sections(Y, i-1, i);
+ // Update flag
+ swapped = true;
+ }
+ }
+ loopend = loopend - 1;
+ } while (swapped);
+
+ // Finally, hide the lightbox
+ window.setTimeout(function(e) {
+ lightbox.hide();
+ }, 250);
+ },
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ lightbox.hide();
+ }
+ },
+ context:this
+ });
+ }
+
+ }, {
+ NAME : 'course-dragdrop-section',
+ ATTRS : {
+ courseid : {
+ value : null
+ },
+ ajaxurl : {
+ 'value' : 0
+ },
+ config : {
+ 'value' : 0
+ }
+ }
+ });
+
+ var DRAGRESOURCE = function() {
+ DRAGRESOURCE.superclass.constructor.apply(this, arguments);
+ };
+ Y.extend(DRAGRESOURCE, M.core.dragdrop, {
+ initializer : function(params) {
+ // Set group for parent class
+ this.groups = ['resource'];
+ this.samenodeclass = CSS.ACTIVITY;
+ this.parentnodeclass = CSS.SECTION;
+ this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS);
+
+ // Go through all sections
+ var sectionlistselector = M.course.format.get_section_selector(Y);
+ if (sectionlistselector) {
+ sectionlistselector = '.'+CSS.COURSECONTENT+' '+sectionlistselector;
+ this.setup_for_section(sectionlistselector);
+
+ // Initialise drag & drop for all resources/activities
+ var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length+2)+' li.'+CSS.ACTIVITY;
+ var del = new Y.DD.Delegate({
+ container: '.'+CSS.COURSECONTENT,
+ nodes: nodeselector,
+ target: true,
+ handles: ['.' + CSS.EDITINGMOVE],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false,
+ cloneNode: true
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#'+CSS.PAGECONTENT
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+
+ M.course.coursebase.register_module(this);
+ M.course.dragres = this;
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @param baseselector The CSS selector or node to limit scope to
+ * @return void
+ */
+ setup_for_section : function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ var resources = sectionnode.one('.'+CSS.CONTENT+' ul.'+CSS.SECTION);
+ // See if resources ul exists, if not create one
+ if (!resources) {
+ var resources = Y.Node.create('<ul></ul>');
+ resources.addClass(CSS.SECTION);
+ sectionnode.one('.'+CSS.CONTENT+' div.'+CSS.SUMMARY).insert(resources, 'after');
+ }
+ // Define empty ul as droptarget, so that item could be moved to empty list
+ var tar = new Y.DD.Drop({
+ node: resources,
+ groups: this.groups,
+ padding: '20 0 20 0'
+ });
+
+ // Initialise each resource/activity in this section
+ this.setup_for_resource('#'+sectionnode.get('id')+' li.'+CSS.ACTIVITY);
+ }, this);
+ },
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to resource(s)
+ *
+ * @param baseselector The CSS selector or node to limit scope to
+ * @return void
+ */
+ setup_for_resource : function(baseselector) {
+ Y.Node.all(baseselector).each(function(resourcesnode) {
+ // Replace move icons
+ var move = resourcesnode.one('a.'+CSS.EDITINGMOVE);
+ if (move) {
+ move.replace(this.resourcedraghandle.cloneNode(true));
+ }
+ }, this);
+ },
+
+ get_section_id : function(node) {
+ return Number(node.get('id').replace(/section-/i, ''));
+ },
+
+ get_resource_id : function(node) {
+ return Number(node.get('id').replace(/module-/i, ''));
+ },
+
+ drag_start : function(e) {
+ // Get our drag object
+ var drag = e.target;
+ drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
+ drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
+ },
+
+ drag_dropmiss : function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ drop_hit : function(e) {
+ var drag = e.drag;
+ // Get a reference to our drag node
+ var dragnode = drag.get('node');
+ var dropnode = e.drop.get('node');
+
+ // Add spinner if it not there
+ var spinner = M.util.add_spinner(Y, dragnode.one(CSS.COMMANDSPAN));
+
+ var params = {};
+
+ // Handle any variables which we must pass back through to
+ var pageparams = this.get('config').pageparams;
+ for (varname in pageparams) {
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'resource';
+ params.field = 'move';
+ params.id = Number(this.get_resource_id(dragnode));
+ params.sectionId = this.get_section_id(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true));
+
+ if (dragnode.next()) {
+ params.beforeId = Number(this.get_resource_id(dragnode.next()));
+ }
+
+ // Do AJAX request
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start : function(tid) {
+ this.lock_drag_handle(drag, CSS.EDITINGMOVE);
+ spinner.show();
+ },
+ success: function(tid, response) {
+ var responsetext = Y.JSON.parse(response.responseText);
+ var params = {element: dragnode, visible: responsetext.visible};
+ M.course.coursebase.invoke_function('set_visibility_resource_ui', params);
+ this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
+ window.setTimeout(function(e) {
+ spinner.hide();
+ }, 250);
+ },
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
+ spinner.hide();
+ // TODO: revert nodes location
+ }
+ },
+ context:this
+ });
+ }
+ }, {
+ NAME : 'course-dragdrop-resource',
+ ATTRS : {
+ courseid : {
+ value : null
+ },
+ ajaxurl : {
+ 'value' : 0
+ },
+ config : {
+ 'value' : 0
+ }
+ }
+ });
+
+ M.course = M.course || {};
+ M.course.init_resource_dragdrop = function(params) {
+ new DRAGRESOURCE(params);
+ }
+ M.course.init_section_dragdrop = function(params) {
+ new DRAGSECTION(params);
+ }
+}, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'dd-scroll', 'moodle-core-dragdrop', 'moodle-core-notification', 'moodle-course-coursebase']});
--- /dev/null
+YUI.add('moodle-course-formatchooser', function(Y) {
+ var FORMATCHOOSER = function() {
+ FORMATCHOOSER.superclass.constructor.apply(this, arguments);
+ }
+
+ Y.extend(FORMATCHOOSER, Y.Base, {
+ initializer : function(params) {
+ if (params && params.formid) {
+ var updatebut = Y.one('#'+params.formid+' #id_updatecourseformat');
+ var formatselect = Y.one('#'+params.formid+' #id_format');
+ if (updatebut && formatselect) {
+ updatebut.setStyle('display', 'none');
+ formatselect.on('change', function() {
+ updatebut.simulate('click');
+ });
+ }
+ }
+ }
+ });
+
+ M.course = M.course || {};
+ M.course.init_formatchooser = function(params) {
+ return new FORMATCHOOSER(params);
+ }
+}, '@VERSION@', {requires:['base', 'node', 'node-event-simulate']});
--- /dev/null
+YUI.add('moodle-course-modchooser', function(Y) {
+ var CSS = {
+ PAGECONTENT : 'div#page-content',
+ SECTION : 'li.section',
+ SECTIONMODCHOOSER : 'span.section-modchooser-link',
+ SITEMENU : 'div.block_site_main_menu',
+ SITETOPIC : 'div.sitetopic'
+ };
+
+ var MODCHOOSERNAME = 'course-modchooser';
+
+ var MODCHOOSER = function() {
+ MODCHOOSER.superclass.constructor.apply(this, arguments);
+ }
+
+ Y.extend(MODCHOOSER, M.core.chooserdialogue, {
+ // The current section ID
+ sectionid : null,
+
+ // The hidden element holding the jump param
+ jumplink : null,
+
+ initializer : function(config) {
+ var dialogue = Y.one('.chooserdialoguebody');
+ var header = Y.one('.choosertitle');
+ var params = {};
+ this.setup_chooser_dialogue(dialogue, header, params);
+
+ // Initialize existing sections and register for dynamically created sections
+ this.setup_for_section();
+ M.course.coursebase.register_module(this);
+
+ // Catch the page toggle
+ Y.all('.block_settings #settingsnav .type_course .modchoosertoggle a').on('click', this.toggle_mod_chooser, this);
+ },
+ /**
+ * Update any section areas within the scope of the specified
+ * selector with AJAX equivalents
+ *
+ * @param baseselector The selector to limit scope to
+ * @return void
+ */
+ setup_for_section : function(baseselector) {
+ if (!baseselector) {
+ var baseselector = CSS.PAGECONTENT;
+ }
+
+ // Setup for site topics
+ Y.one(baseselector).all(CSS.SITETOPIC).each(function(section) {
+ this._setup_for_section(section);
+ }, this);
+
+ // Setup for standard course topics
+ Y.one(baseselector).all(CSS.SECTION).each(function(section) {
+ this._setup_for_section(section);
+ }, this);
+
+ // Setup for the block site menu
+ Y.one(baseselector).all(CSS.SITEMENU).each(function(section) {
+ this._setup_for_section(section);
+ }, this);
+ },
+ _setup_for_section : function(section, sectionid) {
+ var chooserspan = section.one(CSS.SECTIONMODCHOOSER);
+ if (!chooserspan) {
+ return;
+ }
+ var chooserlink = Y.Node.create("<a href='#' />");
+ chooserspan.get('children').each(function(node) {
+ chooserlink.appendChild(node);
+ });
+ chooserspan.insertBefore(chooserlink);
+ chooserlink.on('click', this.display_mod_chooser, this);
+ },
+ /**
+ * Display the module chooser
+ *
+ * @param e Event Triggering Event
+ * @param secitonid integer The ID of the section triggering the dialogue
+ * @return void
+ */
+ display_mod_chooser : function (e) {
+ // Set the section for this version of the dialogue
+ if (e.target.ancestor(CSS.SITETOPIC)) {
+ // The site topic has a sectionid of 1
+ this.sectionid = 1;
+ } else if (e.target.ancestor(CSS.SECTION)) {
+ var section = e.target.ancestor(CSS.SECTION);
+ this.sectionid = section.get('id').replace('section-', '');
+ } else if (e.target.ancestor(CSS.SITEMENU)) {
+ // The block site menu has a sectionid of 0
+ this.sectionid = 0;
+ }
+ this.display_chooser(e);
+ },
+ toggle_mod_chooser : function(e) {
+ // Get the add section link
+ var modchooserlinks = Y.all('div.addresourcemodchooser');
+
+ // Get the dropdowns
+ var dropdowns = Y.all('div.addresourcedropdown');
+
+ if (modchooserlinks.size() == 0) {
+ // Continue with non-js action if there are no modchoosers to add
+ return;
+ }
+
+ // We need to update the text and link
+ var togglelink = Y.one('.block_settings #settingsnav .type_course .modchoosertoggle a');
+
+ // The actual text is in the last child
+ var toggletext = togglelink.get('lastChild');
+
+ var usemodchooser;
+ // Determine whether they're currently hidden
+ if (modchooserlinks.item(0).hasClass('visibleifjs')) {
+ // The modchooser is currently visible, hide it
+ usemodchooser = 0;
+ modchooserlinks
+ .removeClass('visibleifjs')
+ .addClass('hiddenifjs');
+ dropdowns
+ .addClass('visibleifjs')
+ .removeClass('hiddenifjs');
+ toggletext.set('data', M.util.get_string('modchooserenable', 'moodle'));
+ togglelink.set('href', togglelink.get('href').replace('off', 'on'));
+ } else {
+ // The modchooser is currently not visible, show it
+ usemodchooser = 1;
+ modchooserlinks
+ .addClass('visibleifjs')
+ .removeClass('hiddenifjs');
+ dropdowns
+ .removeClass('visibleifjs')
+ .addClass('hiddenifjs');
+ toggletext.set('data', M.util.get_string('modchooserdisable', 'moodle'));
+ togglelink.set('href', togglelink.get('href').replace('on', 'off'));
+ }
+
+ M.util.set_user_preference('usemodchooser', usemodchooser);
+
+ // Prevent the page from reloading
+ e.preventDefault();
+ },
+ option_selected : function(thisoption) {
+ // Add the sectionid to the URL
+ this.jumplink.set('value', thisoption.get('value') + '§ion=' + this.sectionid);
+ }
+ },
+ {
+ NAME : MODCHOOSERNAME,
+ ATTRS : {
+ maxheight : {
+ value : 800
+ }
+ }
+ });
+ M.course = M.course || {};
+ M.course.init_chooser = function(config) {
+ return new MODCHOOSER(config);
+ }
+},
+'@VERSION@', {
+ requires:['base', 'overlay', 'moodle-core-chooserdialogue', 'transition']
+}
+);
--- /dev/null
+YUI.add('moodle-course-toolboxes', function(Y) {
+ WAITICON = {'pix':"i/loading_small",'component':'moodle'};
+ // The CSS selectors we use
+ var CSS = {
+ ACTIVITYLI : 'li.activity',
+ COMMANDSPAN : 'span.commands',
+ SPINNERCOMMANDSPAN : 'span.commands',
+ CONTENTAFTERLINK : 'div.contentafterlink',
+ DELETE : 'a.editing_delete',
+ DIMCLASS : 'dimmed',
+ DIMMEDTEXT : 'dimmed_text',
+ EDITTITLE : 'a.editing_title',
+ EDITTITLECLASS : 'edittitle',
+ GENERICICONCLASS : 'iconsmall',
+ GROUPSNONE : 'a.editing_groupsnone',
+ GROUPSSEPARATE : 'a.editing_groupsseparate',
+ GROUPSVISIBLE : 'a.editing_groupsvisible',
+ HASLABEL : 'label',
+ HIDE : 'a.editing_hide',
+ HIGHLIGHT : 'a.editing_highlight',
+ INSTANCENAME : 'span.instancename',
+ LIGHTBOX : 'lightbox',
+ MODINDENTCOUNT : 'mod-indent-',
+ MODINDENTDIV : 'div.mod-indent',
+ MODINDENTHUGE : 'mod-indent-huge',
+ MODULEIDPREFIX : 'module-',
+ MOVELEFT : 'a.editing_moveleft',
+ MOVELEFTCLASS : 'editing_moveleft',
+ MOVERIGHT : 'a.editing_moveright',
+ PAGECONTENT : 'div#page-content',
+ RIGHTSIDE : '.right',
+ SECTIONHIDDENCLASS : 'hidden',
+ SECTIONIDPREFIX : 'section-',
+ SECTIONLI : 'li.section',
+ SHOW : 'a.editing_show',
+ SHOWHIDE : 'a.editing_showhide',
+ CONDITIONALHIDDEN : 'conditionalhidden',
+ AVAILABILITYINFODIV : 'div.availabilityinfo',
+ SHOWCLASS : 'editing_show',
+ HIDECLASS : 'hide'
+ };
+
+ /**
+ * The toolbox classes
+ *
+ * TOOLBOX is a generic class which should never be directly instantiated
+ * RESOURCETOOLBOX is a class extending TOOLBOX containing code specific to resources
+ * SECTIONTOOLBOX is a class extending TOOLBOX containing code specific to sections
+ */
+ var TOOLBOX = function() {
+ TOOLBOX.superclass.constructor.apply(this, arguments);
+ }
+
+ Y.extend(TOOLBOX, Y.Base, {
+ /**
+ * Toggle the visibility and availability for the specified
+ * resource show/hide button
+ */
+ toggle_hide_resource_ui : function(button) {
+ var element = button.ancestor(CSS.ACTIVITYLI);
+ var hideicon = button.one('img');
+
+ var dimarea;
+ var toggle_class;
+ if (this.is_label(element)) {
+ toggle_class = CSS.DIMMEDTEXT;
+ dimarea = element.all(CSS.MODINDENTDIV + ' > div').item(1);
+ } else {
+ toggle_class = CSS.DIMCLASS;
+ dimarea = element.one('a');
+ }
+
+ var status = '';
+ var value;
+ if (button.hasClass(CSS.SHOWCLASS)) {
+ status = 'hide';
+ value = 1;
+ } else {
+ status = 'show';
+ value = 0;
+ }
+ // Update button info.
+ var newstring = M.util.get_string(status, 'moodle');
+ hideicon.setAttrs({
+ 'alt' : newstring,
+ 'src' : M.util.image_url('t/' + status)
+ });
+ button.set('title', newstring);
+ button.set('className', 'editing_'+status);
+
+ // If activity is conditionally hidden, then don't toggle.
+ if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) {
+ // Change the UI.
+ dimarea.toggleClass(toggle_class);
+ // We need to toggle dimming on the description too.
+ element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
+ }
+ // Toggle availablity info for conditional activities.
+ var availabilityinfo = element.one(CSS.AVAILABILITYINFODIV);
+
+ if (availabilityinfo) {
+ availabilityinfo.toggleClass(CSS.HIDECLASS);
+ }
+ return value;
+ },
+ /**
+ * Send a request using the REST API
+ *
+ * @param data The data to submit
+ * @param statusspinner (optional) A statusspinner which may contain a section loader
+ * @param optionalconfig (optional) Any additional configuration to submit
+ * @return response responseText field from responce
+ */
+ send_request : function(data, statusspinner, optionalconfig) {
+ // Default data structure
+ if (!data) {
+ data = {};
+ }
+ // Handle any variables which we must pass back through to
+ var pageparams = this.get('config').pageparams;
+ for (varname in pageparams) {
+ data[varname] = pageparams[varname];
+ }
+
+ data.sesskey = M.cfg.sesskey;
+ data.courseId = this.get('courseid');
+
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+
+ // Define the configuration to send with the request
+ var responsetext = [];
+ var config = {
+ method: 'POST',
+ data: data,
+ on: {
+ success: function(tid, response) {
+ try {
+ responsetext = Y.JSON.parse(response.responseText);
+ if (responsetext.error) {
+ new M.core.ajaxException(responsetext);
+ }
+ } catch (e) {}
+ if (statusspinner) {
+ window.setTimeout(function(e) {
+ statusspinner.hide();
+ }, 400);
+ }
+ },
+ failure : function(tid, response) {
+ if (statusspinner) {
+ statusspinner.hide();
+ }
+ new M.core.ajaxException(response);
+ }
+ },
+ context: this,
+ sync: true
+ }
+
+ // Apply optional config
+ if (optionalconfig) {
+ for (varname in optionalconfig) {
+ config[varname] = optionalconfig[varname];
+ }
+ }
+
+ if (statusspinner) {
+ statusspinner.show();
+ }
+
+ // Send the request
+ Y.io(uri, config);
+ return responsetext;
+ },
+ is_label : function(target) {
+ return target.hasClass(CSS.HASLABEL);
+ },
+ /**
+ * Return the module ID for the specified element
+ *
+ * @param element The <li> element to determine a module-id number for
+ * @return string The module ID
+ */
+ get_element_id : function(element) {
+ return element.get('id').replace(CSS.MODULEIDPREFIX, '');
+ },
+ /**
+ * Return the module ID for the specified element
+ *
+ * @param element The <li> element to determine a module-id number for
+ * @return string The module ID
+ */
+ get_section_id : function(section) {
+ return section.get('id').replace(CSS.SECTIONIDPREFIX, '');
+ }
+ },
+ {
+ NAME : 'course-toolbox',
+ ATTRS : {
+ // The ID of the current course
+ courseid : {
+ 'value' : 0
+ },
+ ajaxurl : {
+ 'value' : 0
+ },
+ config : {
+ 'value' : 0
+ }
+ }
+ }
+ );
+
+
+ var RESOURCETOOLBOX = function() {
+ RESOURCETOOLBOX.superclass.constructor.apply(this, arguments);
+ }
+
+ Y.extend(RESOURCETOOLBOX, TOOLBOX, {
+ // Variables
+ GROUPS_NONE : 0,
+ GROUPS_SEPARATE : 1,
+ GROUPS_VISIBLE : 2,
+
+ /**
+ * Initialize the resource toolbox
+ *
+ * Updates all span.commands with relevant handlers and other required changes
+ */
+ initializer : function(config) {
+ this.setup_for_resource();
+ M.course.coursebase.register_module(this);
+
+ var prefix = CSS.ACTIVITYLI + ' ' + CSS.COMMANDSPAN + ' ';
+ Y.delegate('click', this.edit_resource_title, CSS.PAGECONTENT, prefix + CSS.EDITTITLE, this);
+ Y.delegate('click', this.move_left, CSS.PAGECONTENT, prefix + CSS.MOVELEFT, this);
+ Y.delegate('click', this.move_right, CSS.PAGECONTENT, prefix + CSS.MOVERIGHT, this);
+ Y.delegate('click', this.delete_resource, CSS.PAGECONTENT, prefix + CSS.DELETE, this);
+ Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.HIDE, this);
+ Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.SHOW, this);
+ Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSNONE, this);
+ Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSSEPARATE, this);
+ Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSVISIBLE, this);
+ },
+
+ /**
+ * Update any span.commands within the scope of the specified
+ * selector with AJAX equivelants
+ *
+ * @param baseselector The selector to limit scope to
+ * @return void
+ */
+ setup_for_resource : function(baseselector) {
+ if (!baseselector) {
+ var baseselector = CSS.PAGECONTENT + ' ' + CSS.ACTIVITYLI;
+ }
+
+ Y.all(baseselector).each(this._setup_for_resource, this);
+ },
+ _setup_for_resource : function(toolboxtarget) {
+ toolboxtarget = Y.one(toolboxtarget);
+
+ // Set groupmode attribute for use by this.toggle_groupmode()
+ var groups;
+ groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSNONE);
+ groups.setAttribute('groupmode', this.GROUPS_NONE);
+
+ groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSSEPARATE);
+ groups.setAttribute('groupmode', this.GROUPS_SEPARATE);
+
+ groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSVISIBLE);
+ groups.setAttribute('groupmode', this.GROUPS_VISIBLE);
+ },
+ move_left : function(e) {
+ this.move_leftright(e, -1);
+ },
+ move_right : function(e) {
+ this.move_leftright(e, 1);
+ },
+ move_leftright : function(e, direction) {
+ // Prevent the default button action
+ e.preventDefault();
+
+ // Get the element we're working on
+ var element = e.target.ancestor(CSS.ACTIVITYLI);
+
+ // And we need to determine the current and new indent level
+ var indentdiv = element.one(CSS.MODINDENTDIV);
+ var indent = indentdiv.getAttribute('class').match(/mod-indent-(\d{1,})/);
+
+ if (indent) {
+ var oldindent = parseInt(indent[1]);
+ var newindent = Math.max(0, (oldindent + parseInt(direction)));
+ indentdiv.removeClass(indent[0]);
+ } else {
+ var oldindent = 0;
+ var newindent = 1;
+ }
+
+ // Perform the move
+ indentdiv.addClass(CSS.MODINDENTCOUNT + newindent);
+ var data = {
+ 'class' : 'resource',
+ 'field' : 'indent',
+ 'value' : newindent,
+ 'id' : this.get_element_id(element)
+ };
+ var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
+ this.send_request(data, spinner);
+
+ // Handle removal/addition of the moveleft button
+ if (newindent == 0) {
+ element.one(CSS.MOVELEFT).remove();
+ } else if (newindent == 1 && oldindent == 0) {
+ this.add_moveleft(element);
+ }
+
+ // Handle massive indentation to match non-ajax display
+ var hashugeclass = indentdiv.hasClass(CSS.MODINDENTHUGE);
+ if (newindent > 15 && !hashugeclass) {
+ indentdiv.addClass(CSS.MODINDENTHUGE);
+ } else if (newindent <= 15 && hashugeclass) {
+ indentdiv.removeClass(CSS.MODINDENTHUGE);
+ }
+ },
+ delete_resource : function(e) {
+ // Prevent the default button action
+ e.preventDefault();
+
+ // Get the element we're working on
+ var element = e.target.ancestor(CSS.ACTIVITYLI);
+
+ var confirmstring = '';
+ if (this.is_label(element)) {
+ // Labels are slightly different to other activities
+ var plugindata = {
+ type : M.util.get_string('pluginname', 'label')
+ }
+ confirmstring = M.util.get_string('deletechecktype', 'moodle', plugindata)
+ } else {
+ var plugindata = {
+ type : M.util.get_string('pluginname', element.getAttribute('class').match(/modtype_([^\s]*)/)[1]),
+ name : element.one(CSS.INSTANCENAME).get('firstChild').get('data')
+ }
+ confirmstring = M.util.get_string('deletechecktypename', 'moodle', plugindata);
+ }
+
+ // Confirm element removal
+ if (!confirm(confirmstring)) {
+ return false;
+ }
+
+ // Actually remove the element
+ element.remove();
+ var data = {
+ 'class' : 'resource',
+ 'action' : 'DELETE',
+ 'id' : this.get_element_id(element)
+ };
+ this.send_request(data);
+ },
+ toggle_hide_resource : function(e) {
+ // Prevent the default button action
+ e.preventDefault();
+
+ // Return early if the current section is hidden
+ var section = e.target.ancestor(M.course.format.get_section_selector(Y));
+ if (section && section.hasClass(CSS.SECTIONHIDDENCLASS)) {
+ return;
+ }
+
+ // Get the element we're working on
+ var element = e.target.ancestor(CSS.ACTIVITYLI);
+
+ var button = e.target.ancestor('a', true);
+
+ var value = this.toggle_hide_resource_ui(button);
+
+ // Send the request
+ var data = {
+ 'class' : 'resource',
+ 'field' : 'visible',
+ 'value' : value,
+ 'id' : this.get_element_id(element)
+ };
+ var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
+ this.send_request(data, spinner);
+ return false; // Need to return false to stop the delegate for the new state firing
+ },
+ toggle_groupmode : function(e) {
+ // Prevent the default button action
+ e.preventDefault();
+
+ // Get the element we're working on
+ var element = e.target.ancestor(CSS.ACTIVITYLI);
+
+ var button = e.target.ancestor('a', true);
+ var icon = button.one('img');
+
+ // Current Mode
+ var groupmode = button.getAttribute('groupmode');
+ groupmode++;
+ if (groupmode > 2) {
+ groupmode = 0;
+ }
+ button.setAttribute('groupmode', groupmode);
+
+ var newtitle = '';
+ var iconsrc = '';
+ switch (groupmode) {
+ case this.GROUPS_NONE:
+ newtitle = 'groupsnone';
+ iconsrc = M.util.image_url('t/groupn');
+ break;
+ case this.GROUPS_SEPARATE:
+ newtitle = 'groupsseparate';
+ iconsrc = M.util.image_url('t/groups');
+ break;
+ case this.GROUPS_VISIBLE:
+ newtitle = 'groupsvisible';
+ iconsrc = M.util.image_url('t/groupv');
+ break;
+ }
+ newtitle = M.util.get_string('clicktochangeinbrackets', 'moodle',
+ M.util.get_string(newtitle, 'moodle'));
+
+ // Change the UI
+ icon.setAttrs({
+ 'alt' : newtitle,
+ 'src' : iconsrc
+ });
+ button.setAttribute('title', newtitle);
+
+ // And send the request
+ var data = {
+ 'class' : 'resource',
+ 'field' : 'groupmode',
+ 'value' : groupmode,
+ 'id' : this.get_element_id(element)
+ };
+ var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
+ this.send_request(data, spinner);
+ return false; // Need to return false to stop the delegate for the new state firing
+ },
+ /**
+ * Add the moveleft button
+ * This is required after moving left from an initial position of 0
+ *
+ * @param target The encapsulating <li> element
+ */
+ add_moveleft : function(target) {
+ var left_string = M.util.get_string('moveleft', 'moodle');
+ var moveimage = 't/left'; // ltr mode
+ if ( Y.one(document.body).hasClass('dir-rtl') ) {
+ moveimage = 't/right';
+ } else {
+ moveimage = 't/left';
+ }
+ var newicon = Y.Node.create('<img />')
+ .addClass(CSS.GENERICICONCLASS)
+ .setAttrs({
+ 'src' : M.util.image_url(moveimage, 'moodle'),
+ 'alt' : left_string
+ });
+ var moveright = target.one(CSS.MOVERIGHT);
+ var newlink = moveright.getAttribute('href').replace('indent=1', 'indent=-1');
+ var anchor = new Y.Node.create('<a />')
+ .setStyle('cursor', 'pointer')
+ .addClass(CSS.MOVELEFTCLASS)
+ .setAttribute('href', newlink)
+ .setAttribute('title', left_string);
+ anchor.appendChild(newicon);
+ moveright.insert(anchor, 'before');
+ },
+ /**
+ * Edit the title for the resource
+ */
+ edit_resource_title : function(e) {
+ // Get the element we're working on
+ var element = e.target.ancestor(CSS.ACTIVITYLI);
+ var elementdiv = element.one('div');
+ var instancename = element.one(CSS.INSTANCENAME);
+ var currenttitle = instancename.get('firstChild');
+ var oldtitle = currenttitle.get('data');
+ var titletext = oldtitle;
+ var editbutton = element.one('a.' + CSS.EDITTITLECLASS + ' img');
+
+ // Handle events for edit_resource_title
+ var listenevents = [];
+ var thisevent;
+
+ // Grab the anchor so that we can swap it with the edit form
+ var anchor = instancename.ancestor('a');
+
+ var data = {
+ 'class' : 'resource',
+ 'field' : 'gettitle',
+ 'id' : this.get_element_id(element)
+ };
+
+ // Try to retrieve the existing string from the server
+ var response = this.send_request(data, editbutton);
+ if (response.instancename) {
+ titletext = response.instancename;
+ }
+
+ // Create the editor and submit button
+ var editor = Y.Node.create('<input />')
+ .setAttrs({
+ 'name' : 'title',
+ 'value' : titletext,
+ 'autocomplete' : 'off'
+ })
+ .addClass('titleeditor');
+ var editform = Y.Node.create('<form />')
+ .addClass('activityinstance')
+ .setAttribute('action', '#');
+ var editinstructions = Y.Node.create('<span />')
+ .addClass('editinstructions')
+ .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
+ var activityicon = element.one('img.activityicon').cloneNode();
+
+ // Clear the existing content and put the editor in
+ currenttitle.set('data', '');
+ editform.appendChild(activityicon);
+ editform.appendChild(editor);
+ anchor.replace(editform);
+ elementdiv.appendChild(editinstructions);
+ e.preventDefault();
+
+ // Focus and select the editor text
+ editor.focus().select();
+
+ // Handle removal of the editor
+ var clear_edittitle = function() {
+ // Detach all listen events to prevent duplicate triggers
+ var thisevent;
+ while (thisevent = listenevents.shift()) {
+ thisevent.detach();
+ }
+
+ if (editinstructions) {
+ // Convert back to anchor and remove instructions
+ editform.replace(anchor);
+ editinstructions.remove();
+ editinstructions = null;
+ }
+ }
+
+ // Handle cancellation of the editor
+ var cancel_edittitle = function(e) {
+ clear_edittitle();
+
+ // Set the title and anchor back to their previous settings
+ currenttitle.set('data', oldtitle);
+ };
+
+ // Cancel the edit if we lose focus or the escape key is pressed
+ thisevent = editor.on('blur', cancel_edittitle);
+ listenevents.push(thisevent);
+ thisevent = Y.one('document').on('keydown', function(e) {
+ if (e.keyCode === 27) {
+ e.preventDefault();
+ cancel_edittitle(e);
+ }
+ });
+ listenevents.push(thisevent);
+
+ // Handle form submission
+ thisevent = editform.on('submit', function(e) {
+ // We don't actually want to submit anything
+ e.preventDefault();
+
+ // Clear the edit title boxes
+ clear_edittitle();
+
+ // We only accept strings which have valid content
+ var newtitle = Y.Lang.trim(editor.get('value'));
+ if (newtitle != null && newtitle != "" && newtitle != titletext) {
+ var data = {
+ 'class' : 'resource',
+ 'field' : 'updatetitle',
+ 'title' : newtitle,
+ 'id' : this.get_element_id(element)
+ };
+ var response = this.send_request(data, editbutton);
+ if (response.instancename) {
+ currenttitle.set('data', response.instancename);
+ }
+ } else {
+ // Invalid content. Set the title back to it's original contents
+ currenttitle.set('data', oldtitle);
+ }
+ }, this);
+ listenevents.push(thisevent);
+ },
+ /**
+ * Set the visibility of the current resource (identified by the element)
+ * to match the hidden parameter (this is not a toggle).
+ * Only changes the visibility in the browser (no ajax update).
+ * @param args An object with 'element' being the A node containing the resource
+ * and 'visible' being the state that the visiblity should be set to.
+ * @return void
+ */
+ set_visibility_resource_ui: function(args) {
+ var element = args.element;
+ var shouldbevisible = args.visible;
+ var buttonnode = element.one(CSS.SHOW);
+ var visible = (buttonnode === null);
+ if (visible) {
+ buttonnode = element.one(CSS.HIDE);
+ }
+ if (visible != shouldbevisible) {
+ this.toggle_hide_resource_ui(buttonnode);
+ }
+ }
+ }, {
+ NAME : 'course-resource-toolbox',
+ ATTRS : {
+ courseid : {
+ 'value' : 0
+ },
+ format : {
+ 'value' : 'topics'
+ }
+ }
+ });
+
+ var SECTIONTOOLBOX = function() {
+ SECTIONTOOLBOX.superclass.constructor.apply(this, arguments);
+ }
+
+ Y.extend(SECTIONTOOLBOX, TOOLBOX, {
+ /**
+ * Initialize the toolboxes module
+ *
+ * Updates all span.commands with relevant handlers and other required changes
+ */
+ initializer : function(config) {
+ this.setup_for_section();
+ M.course.coursebase.register_module(this);
+
+ // Section Highlighting
+ Y.delegate('click', this.toggle_highlight, CSS.PAGECONTENT, CSS.SECTIONLI + ' ' + CSS.HIGHLIGHT, this);
+ // Section Visibility
+ Y.delegate('click', this.toggle_hide_section, CSS.PAGECONTENT, CSS.SECTIONLI + ' ' + CSS.SHOWHIDE, this);
+ },
+ /**
+ * Update any section areas within the scope of the specified
+ * selector with AJAX equivelants
+ *
+ * @param baseselector The selector to limit scope to
+ * @return void
+ */
+ setup_for_section : function(baseselector) {
+ // Left here for potential future use - not currently needed due to YUI delegation in initializer()
+ /*if (!baseselector) {
+ var baseselector = CSS.PAGECONTENT;
+ }
+
+ Y.all(baseselector).each(this._setup_for_section, this);*/
+ },
+ _setup_for_section : function(toolboxtarget) {
+ // Left here for potential future use - not currently needed due to YUI delegation in initializer()
+ },
+ toggle_hide_section : function(e) {
+ // Prevent the default button action
+ e.preventDefault();
+
+ // Get the section we're working on
+ var section = e.target.ancestor(M.course.format.get_section_selector(Y));
+ var button = e.target.ancestor('a', true);
+ var hideicon = button.one('img');
+
+ // The value to submit
+ var value;
+ // The status text for strings and images
+ var status;
+
+ if (!section.hasClass(CSS.SECTIONHIDDENCLASS)) {
+ section.addClass(CSS.SECTIONHIDDENCLASS);
+ value = 0;
+ status = 'show';
+
+ } else {
+ section.removeClass(CSS.SECTIONHIDDENCLASS);
+ value = 1;
+ status = 'hide';
+ }
+
+ var newstring = M.util.get_string(status + 'fromothers', 'format_' + this.get('format'));
+ hideicon.setAttrs({
+ 'alt' : newstring,
+ 'src' : M.util.image_url('i/' + status)
+ });
+ button.set('title', newstring);
+
+ // Change the highlight status
+ var data = {
+ 'class' : 'section',
+ 'field' : 'visible',
+ 'id' : this.get_section_id(section.ancestor(M.course.format.get_section_wrapper(Y), true)),
+ 'value' : value
+ };
+
+ var lightbox = M.util.add_lightbox(Y, section);
+ lightbox.show();
+
+ var response = this.send_request(data, lightbox);
+
+ var activities = section.all(CSS.ACTIVITYLI);
+ activities.each(function(node) {
+ if (node.one(CSS.SHOW)) {
+ var button = node.one(CSS.SHOW);
+ } else {
+ var button = node.one(CSS.HIDE);
+ }
+ var activityid = this.get_element_id(node);
+
+ if (Y.Array.indexOf(response.resourcestotoggle, activityid) != -1) {
+ this.toggle_hide_resource_ui(button);
+ }
+ }, this);
+ },
+ toggle_highlight : function(e) {
+ // Prevent the default button action
+ e.preventDefault();
+
+ // Get the section we're working on
+ var section = e.target.ancestor(M.course.format.get_section_selector(Y));
+ var button = e.target.ancestor('a', true);
+ var buttonicon = button.one('img');
+
+ // Determine whether the marker is currently set
+ var togglestatus = section.hasClass('current');
+ var value = 0;
+
+ // Set the current highlighted item text
+ var old_string = M.util.get_string('markthistopic', 'moodle');
+ Y.one(CSS.PAGECONTENT)
+ .all(M.course.format.get_section_selector(Y) + '.current ' + CSS.HIGHLIGHT)
+ .set('title', old_string);
+ Y.one(CSS.PAGECONTENT)
+ .all(M.course.format.get_section_selector(Y) + '.current ' + CSS.HIGHLIGHT + ' img')
+ .set('alt', old_string)
+ .set('src', M.util.image_url('i/marker'));
+
+ // Remove the highlighting from all sections
+ var allsections = Y.one(CSS.PAGECONTENT).all(M.course.format.get_section_selector(Y))
+ .removeClass('current');
+
+ // Then add it if required to the selected section
+ if (!togglestatus) {
+ section.addClass('current');
+ value = this.get_section_id(section.ancestor(M.course.format.get_section_wrapper(Y), true));
+ var new_string = M.util.get_string('markedthistopic', 'moodle');
+ button
+ .set('title', new_string);
+ buttonicon
+ .set('alt', new_string)
+ .set('src', M.util.image_url('i/marked'));
+ }
+
+ // Change the highlight status
+ var data = {
+ 'class' : 'course',
+ 'field' : 'marker',
+ 'value' : value
+ };
+ var lightbox = M.util.add_lightbox(Y, section);
+ lightbox.show();
+ this.send_request(data, lightbox);
+ }
+ }, {
+ NAME : 'course-section-toolbox',
+ ATTRS : {
+ courseid : {
+ 'value' : 0
+ },
+ format : {
+ 'value' : 'topics'
+ }
+ }
+ });
+
+ M.course = M.course || {};
+
+ M.course.init_resource_toolbox = function(config) {
+ return new RESOURCETOOLBOX(config);
+ };
+
+ M.course.init_section_toolbox = function(config) {
+ return new SECTIONTOOLBOX(config);
+ };
+
+},
+'@VERSION@', {
+ requires : ['base', 'node', 'io', 'moodle-course-coursebase']
+}
+);