Example #1
0
 /**
  * Parse all callbacks and builds the tree.
  *
  * @param integer   $user ID of the user for which the profile is displayed.
  * @param bool      $iscurrentuser true if the profile being viewed is of current user, else false.
  * @param \stdClass $course Course object
  *
  * @return tree Fully build tree to be rendered on my profile page.
  */
 public static function build_tree($user, $iscurrentuser, $course = null)
 {
     global $CFG;
     $tree = new tree();
     //print_r($user);
     // Add core nodes.
     require_once $CFG->libdir . "/myprofilelib.php";
     core_myprofile_navigation($tree, $user, $iscurrentuser, $course);
     // Core components.
     $components = \core_component::get_core_subsystems();
     foreach ($components as $component => $directory) {
         if (empty($directory)) {
             continue;
         }
         $file = $directory . "/lib.php";
         if (is_readable($file)) {
             require_once $file;
             $function = "core_" . $component . "_myprofile_navigation";
             //print_r($function);
             if (function_exists($function)) {
                 //echo "Current function: ".$function."<br>";
                 $function($tree, $user, $iscurrentuser, $course);
             }
         }
     }
     // Plugins.
     $pluginswithfunction = get_plugins_with_function('myprofile_navigation', 'lib.php');
     foreach ($pluginswithfunction as $plugins) {
         foreach ($plugins as $function) {
             $function($tree, $user, $iscurrentuser, $course);
         }
     }
     $tree->sort_categories();
     return $tree;
 }
Example #2
0
 /**
  * This function loads all of the front page settings into the settings navigation.
  * This function is called when the user is on the front page, or $COURSE==$SITE
  * @param bool $forceopen (optional)
  * @return navigation_node
  */
 protected function load_front_page_settings($forceopen = false)
 {
     global $SITE, $CFG;
     $course = clone $SITE;
     $coursecontext = context_course::instance($course->id);
     // Course context
     $frontpage = $this->add(get_string('frontpagesettings'), null, self::TYPE_SETTING, null, 'frontpage');
     if ($forceopen) {
         $frontpage->force_open();
     }
     $frontpage->id = 'frontpagesettings';
     if ($this->page->user_allowed_editing()) {
         // Add the turn on/off settings
         $url = new moodle_url('/course/view.php', array('id' => $course->id, 'sesskey' => sesskey()));
         if ($this->page->user_is_editing()) {
             $url->param('edit', 'off');
             $editstring = get_string('turneditingoff');
         } else {
             $url->param('edit', 'on');
             $editstring = get_string('turneditingon');
         }
         $frontpage->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', ''));
     }
     if (has_capability('moodle/course:update', $coursecontext)) {
         // Add the course settings link
         $url = new moodle_url('/admin/settings.php', array('section' => 'frontpagesettings'));
         $frontpage->add(get_string('editsettings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', ''));
     }
     // add enrol nodes
     enrol_add_course_navigation($frontpage, $course);
     // Manage filters
     if (has_capability('moodle/filter:manage', $coursecontext) && count(filter_get_available_in_context($coursecontext)) > 0) {
         $url = new moodle_url('/filter/manage.php', array('contextid' => $coursecontext->id));
         $frontpage->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/filter', ''));
     }
     // View course reports.
     if (has_capability('moodle/site:viewreports', $coursecontext)) {
         // Basic capability for listing of reports.
         $frontpagenav = $frontpage->add(get_string('reports'), null, self::TYPE_CONTAINER, null, 'frontpagereports', new pix_icon('i/stats', ''));
         $coursereports = core_component::get_plugin_list('coursereport');
         foreach ($coursereports as $report => $dir) {
             $libfile = $CFG->dirroot . '/course/report/' . $report . '/lib.php';
             if (file_exists($libfile)) {
                 require_once $libfile;
                 $reportfunction = $report . '_report_extend_navigation';
                 if (function_exists($report . '_report_extend_navigation')) {
                     $reportfunction($frontpagenav, $course, $coursecontext);
                 }
             }
         }
         $reports = get_plugin_list_with_function('report', 'extend_navigation_course', 'lib.php');
         foreach ($reports as $reportfunction) {
             $reportfunction($frontpagenav, $course, $coursecontext);
         }
     }
     // Backup this course
     if (has_capability('moodle/backup:backupcourse', $coursecontext)) {
         $url = new moodle_url('/backup/backup.php', array('id' => $course->id));
         $frontpage->add(get_string('backup'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/backup', ''));
     }
     // Restore to this course
     if (has_capability('moodle/restore:restorecourse', $coursecontext)) {
         $url = new moodle_url('/backup/restorefile.php', array('contextid' => $coursecontext->id));
         $frontpage->add(get_string('restore'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/restore', ''));
     }
     // Questions
     require_once $CFG->libdir . '/questionlib.php';
     question_extend_settings_navigation($frontpage, $coursecontext)->trim_if_empty();
     // Manage files
     if ($course->legacyfiles == 2 and has_capability('moodle/course:managefiles', $this->context)) {
         //hiden in new installs
         $url = new moodle_url('/files/index.php', array('contextid' => $coursecontext->id));
         $frontpage->add(get_string('sitelegacyfiles'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/folder', ''));
     }
     // Let plugins hook into frontpage navigation.
     $pluginsfunction = get_plugins_with_function('extend_navigation_frontpage', 'lib.php');
     foreach ($pluginsfunction as $plugintype => $plugins) {
         foreach ($plugins as $pluginfunction) {
             $pluginfunction($frontpage, $course, $coursecontext);
         }
     }
     return $frontpage;
 }
Example #3
0
/**
 * Get a list of all the plugins of a given type that define a certain API function
 * in a certain file. The plugin component names and function names are returned.
 *
 * @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
 * @param string $function the part of the name of the function after the
 *      frankenstyle prefix. e.g 'hook' if you are looking for functions with
 *      names like report_courselist_hook.
 * @param string $file the name of file within the plugin that defines the
 *      function. Defaults to lib.php.
 * @return array with frankenstyle plugin names as keys (e.g. 'report_courselist', 'mod_forum')
 *      and the function names as values (e.g. 'report_courselist_hook', 'forum_hook').
 */
function get_plugin_list_with_function($plugintype, $function, $file = 'lib.php')
{
    global $CFG;
    // We don't include here as all plugin types files would be included.
    $plugins = get_plugins_with_function($function, $file, false);
    if (empty($plugins[$plugintype])) {
        return array();
    }
    $allplugins = core_component::get_plugin_list($plugintype);
    // Reformat the array and include the files.
    $pluginfunctions = array();
    foreach ($plugins[$plugintype] as $pluginname => $functionname) {
        // Check that it has not been removed and the file is still available.
        if (!empty($allplugins[$pluginname])) {
            $filepath = $allplugins[$pluginname] . DIRECTORY_SEPARATOR . $file;
            if (file_exists($filepath)) {
                include_once $filepath;
                $pluginfunctions[$plugintype . '_' . $pluginname] = $functionname;
            }
        }
    }
    return $pluginfunctions;
}
Example #4
0
 /**
  * Allow plugins to provide some content to be rendered in the navbar.
  * The plugin must define a PLUGIN_render_navbar_output function that returns
  * the HTML they wish to add to the navbar.
  *
  * @return string HTML for the navbar
  */
 public function navbar_plugin_output()
 {
     $output = '';
     if ($pluginsfunction = get_plugins_with_function('render_navbar_output')) {
         foreach ($pluginsfunction as $plugintype => $plugins) {
             foreach ($plugins as $pluginfunction) {
                 $output .= $pluginfunction($this);
             }
         }
     }
     return $output;
 }
Example #5
0
/**
 * Delete a block, and associated data.
 *
 * @param object $instance a row from the block_instances table
 * @param bool $nolongerused legacy parameter. Not used, but kept for backwards compatibility.
 * @param bool $skipblockstables for internal use only. Makes @see blocks_delete_all_for_context() more efficient.
 */
function blocks_delete_instance($instance, $nolongerused = false, $skipblockstables = false)
{
    global $DB;
    // Allow plugins to use this block before we completely delete it.
    if ($pluginsfunction = get_plugins_with_function('pre_block_delete')) {
        foreach ($pluginsfunction as $plugintype => $plugins) {
            foreach ($plugins as $pluginfunction) {
                $pluginfunction($instance);
            }
        }
    }
    if ($block = block_instance($instance->blockname, $instance)) {
        $block->instance_delete();
    }
    context_helper::delete_instance(CONTEXT_BLOCK, $instance->id);
    if (!$skipblockstables) {
        $DB->delete_records('block_positions', array('blockinstanceid' => $instance->id));
        $DB->delete_records('block_instances', array('id' => $instance->id));
        $DB->delete_records_list('user_preferences', 'name', array('block' . $instance->id . 'hidden', 'docked_block_instance_' . $instance->id));
    }
}
Example #6
0
/**
 * This function will handle the whole deletion process of a module. This includes calling
 * the modules delete_instance function, deleting files, events, grades, conditional data,
 * the data in the course_module and course_sections table and adding a module deletion
 * event to the DB.
 *
 * @param int $cmid the course module id
 * @since Moodle 2.5
 */
function course_delete_module($cmid)
{
    global $CFG, $DB;
    require_once $CFG->libdir . '/gradelib.php';
    require_once $CFG->libdir . '/questionlib.php';
    require_once $CFG->dirroot . '/blog/lib.php';
    require_once $CFG->dirroot . '/calendar/lib.php';
    // Get the course module.
    if (!($cm = $DB->get_record('course_modules', array('id' => $cmid)))) {
        return true;
    }
    // Get the module context.
    $modcontext = context_module::instance($cm->id);
    // Get the course module name.
    $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module), MUST_EXIST);
    // Get the file location of the delete_instance function for this module.
    $modlib = "{$CFG->dirroot}/mod/{$modulename}/lib.php";
    // Include the file required to call the delete_instance function for this module.
    if (file_exists($modlib)) {
        require_once $modlib;
    } else {
        throw new moodle_exception('cannotdeletemodulemissinglib', '', '', null, "Cannot delete this module as the file mod/{$modulename}/lib.php is missing.");
    }
    $deleteinstancefunction = $modulename . '_delete_instance';
    // Ensure the delete_instance function exists for this module.
    if (!function_exists($deleteinstancefunction)) {
        throw new moodle_exception('cannotdeletemodulemissingfunc', '', '', null, "Cannot delete this module as the function {$modulename}_delete_instance is missing in mod/{$modulename}/lib.php.");
    }
    // Allow plugins to use this course module before we completely delete it.
    if ($pluginsfunction = get_plugins_with_function('pre_course_module_delete')) {
        foreach ($pluginsfunction as $plugintype => $plugins) {
            foreach ($plugins as $pluginfunction) {
                $pluginfunction($cm);
            }
        }
    }
    // Delete activity context questions and question categories.
    question_delete_activity($cm);
    // Call the delete_instance function, if it returns false throw an exception.
    if (!$deleteinstancefunction($cm->instance)) {
        throw new moodle_exception('cannotdeletemoduleinstance', '', '', null, "Cannot delete the module {$modulename} (instance).");
    }
    // Remove all module files in case modules forget to do that.
    $fs = get_file_storage();
    $fs->delete_area_files($modcontext->id);
    // Delete events from calendar.
    if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $modulename))) {
        foreach ($events as $event) {
            $calendarevent = calendar_event::load($event->id);
            $calendarevent->delete();
        }
    }
    // 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_completion_criteria', array('moduleinstance' => $cm->id, 'course' => $cm->course, 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
    // Delete all tag instances associated with the instance of this module.
    core_tag_tag::delete_instances('mod_' . $modulename, null, $modcontext->id);
    core_tag_tag::remove_all_item_tags('core', 'course_modules', $cm->id);
    // Notify the competency subsystem.
    \core_competency\api::hook_course_module_deleted($cm);
    // Delete the context.
    context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
    // Delete the module from the course_modules table.
    $DB->delete_records('course_modules', array('id' => $cm->id));
    // Delete module from that section.
    if (!delete_mod_from_section($cm->id, $cm->section)) {
        throw new moodle_exception('cannotdeletemodulefromsection', '', '', null, "Cannot delete the module {$modulename} (instance) from section.");
    }
    // Trigger event for course module delete action.
    $event = \core\event\course_module_deleted::create(array('courseid' => $cm->course, 'context' => $modcontext, 'objectid' => $cm->id, 'other' => array('modulename' => $modulename, 'instanceid' => $cm->instance)));
    $event->add_record_snapshot('course_modules', $cm);
    $event->trigger();
    rebuild_course_cache($cm->course, true);
}
Example #7
0
 public function test_async_section_deletion_hook_not_implemented()
 {
     // If no plugins advocate async removal, then normal synchronous removal will take place.
     // Only proceed if we are sure that no plugin is going to advocate async removal of a module. I.e. no plugin returns
     // 'true' from the 'course_module_adhoc_deletion_recommended' hook.
     // In the case of core, only recyclebin implements this hook, and it will only return true if enabled, so disable it.
     global $DB, $USER;
     $this->resetAfterTest(true);
     $this->setAdminUser();
     set_config('coursebinenable', false, 'tool_recyclebin');
     // Non-core plugins might implement the 'course_module_adhoc_deletion_recommended' hook and spoil this test.
     // If at least one plugin still returns true, then skip this test.
     if ($pluginsfunction = get_plugins_with_function('course_module_background_deletion_recommended')) {
         foreach ($pluginsfunction as $plugintype => $plugins) {
             foreach ($plugins as $pluginfunction) {
                 if ($pluginfunction()) {
                     $this->markTestSkipped();
                 }
             }
         }
     }
     // Create course, module and context.
     $generator = $this->getDataGenerator();
     $course = $generator->create_course(['numsections' => 4, 'format' => 'topics'], ['createsections' => true]);
     $assign0 = $generator->create_module('assign', ['course' => $course, 'section' => 2]);
     $assign1 = $generator->create_module('assign', ['course' => $course, 'section' => 2]);
     // Delete empty section. No difference from normal, synchronous behaviour.
     $this->assertTrue(course_delete_section($course, 4, false, true));
     $this->assertEquals(3, course_get_format($course)->get_course()->numsections);
     // Delete section in the middle (2).
     $section = $DB->get_record('course_sections', ['course' => $course->id, 'section' => '2']);
     // For event comparison.
     $this->assertFalse(course_delete_section($course, 2, false, true));
     // Non-empty section, no forcedelete, so no change.
     $sink = $this->redirectEvents();
     // To capture the event.
     $this->assertTrue(course_delete_section($course, 2, true, true));
     // Now, confirm that:
     // a) The section's modules have deleted and;
     // b) the section has been deleted and;
     // c) course_section_deleted event has been fired and;
     // d) course_module_deleted events have both been fired.
     // Confirm modules have been deleted.
     list($insql, $assignids) = $DB->get_in_or_equal([$assign0->cmid, $assign1->cmid]);
     $cmcount = $DB->count_records_select('course_modules', 'id ' . $insql, $assignids);
     $this->assertEmpty($cmcount);
     // Confirm the section has been deleted.
     $this->assertEquals(2, course_get_format($course)->get_course()->numsections);
     // Confirm the course_section_deleted event has been generated.
     $events = $sink->get_events();
     $event = array_pop($events);
     $sink->close();
     $this->assertInstanceOf('\\core\\event\\course_section_deleted', $event);
     $this->assertEquals($section->id, $event->objectid);
     $this->assertEquals($USER->id, $event->userid);
     $this->assertEquals('course_sections', $event->objecttable);
     $this->assertEquals(null, $event->get_url());
     $this->assertEquals($section, $event->get_record_snapshot('course_sections', $section->id));
     // Confirm that the course_module_deleted events have both been generated.
     $count = 0;
     while (!empty($events)) {
         $event = array_pop($events);
         if ($event instanceof \core\event\course_module_deleted && in_array($event->objectid, [$assign0->cmid, $assign1->cmid])) {
             $count++;
         }
     }
     $this->assertEquals(2, $count);
 }
Example #8
0
    /**
     * Recursively delete category including all subcategories and courses
     *
     * Function {@link coursecat::can_delete_full()} MUST be called prior
     * to calling this function because there is no capability check
     * inside this function
     *
     * @param boolean $showfeedback display some notices
     * @return array return deleted courses
     * @throws moodle_exception
     */
    public function delete_full($showfeedback = true) {
        global $CFG, $DB;

        require_once($CFG->libdir.'/gradelib.php');
        require_once($CFG->libdir.'/questionlib.php');
        require_once($CFG->dirroot.'/cohort/lib.php');

        // Make sure we won't timeout when deleting a lot of courses.
        $settimeout = core_php_time_limit::raise();

        // Allow plugins to use this category before we completely delete it.
        if ($pluginsfunction = get_plugins_with_function('pre_course_category_delete')) {
            $category = $this->get_db_record();
            foreach ($pluginsfunction as $plugintype => $plugins) {
                foreach ($plugins as $pluginfunction) {
                    $pluginfunction($category);
                }
            }
        }

        $deletedcourses = array();

        // Get children. Note, we don't want to use cache here because it would be rebuilt too often.
        $children = $DB->get_records('course_categories', array('parent' => $this->id), 'sortorder ASC');
        foreach ($children as $record) {
            $coursecat = new coursecat($record);
            $deletedcourses += $coursecat->delete_full($showfeedback);
        }

        if ($courses = $DB->get_records('course', array('category' => $this->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($this);

        // Now delete anything that may depend on course category context.
        grade_course_category_delete($this->id, 0, $showfeedback);
        if (!question_delete_course_category($this, 0, $showfeedback)) {
            throw new moodle_exception('cannotdeletecategoryquestions', '', '', $this->get_formatted_name());
        }

        // Finally delete the category and it's context.
        $DB->delete_records('course_categories', array('id' => $this->id));

        $coursecatcontext = context_coursecat::instance($this->id);
        $coursecatcontext->delete();

        cache_helper::purge_by_event('changesincoursecat');

        // Trigger a course category deleted event.
        /* @var \core\event\course_category_deleted $event */
        $event = \core\event\course_category_deleted::create(array(
            'objectid' => $this->id,
            'context' => $coursecatcontext,
            'other' => array('name' => $this->name)
        ));
        $event->set_coursecat($this);
        $event->trigger();

        // If we deleted $CFG->defaultrequestcategory, make it point somewhere else.
        if ($this->id == $CFG->defaultrequestcategory) {
            set_config('defaultrequestcategory', $DB->get_field('course_categories', 'MIN(id)', array('parent' => 0)));
        }
        return $deletedcourses;
    }
Example #9
0
 /**
  * Plugins can extend the coursemodule settings form.
  */
 protected function plugin_extend_coursemodule_standard_elements()
 {
     $callbacks = get_plugins_with_function('coursemodule_standard_elements', 'lib.php');
     foreach ($callbacks as $type => $plugins) {
         foreach ($plugins as $plugin => $pluginfunction) {
             // We have exposed all the important properties with public getters - and the callback can manipulate the mform
             // directly.
             $pluginfunction($this, $this->_form);
         }
     }
 }
Example #10
0
/**
 * Hook for plugins to take action when a module is created or updated.
 *
 * @param stdClass $moduleinfo the module info
 * @param stdClass $course the course of the module
 *
 * @return stdClass moduleinfo updated by plugins.
 */
function plugin_extend_coursemodule_edit_post_actions($moduleinfo, $course)
{
    $callbacks = get_plugins_with_function('coursemodule_edit_post_actions', 'lib.php');
    foreach ($callbacks as $type => $plugins) {
        foreach ($plugins as $plugin => $pluginfunction) {
            $moduleinfo = $pluginfunction($moduleinfo, $course);
        }
    }
    return $moduleinfo;
}
Example #11
0
 /**
  * Returns if scale used anywhere - activities, grade items, outcomes, etc.
  *
  * @return bool
  */
 public function is_used()
 {
     global $DB;
     global $CFG;
     // count grade items excluding the
     $params = array($this->id);
     $sql = "SELECT COUNT(id) FROM {grade_items} WHERE scaleid = ? AND outcomeid IS NULL";
     if ($DB->count_records_sql($sql, $params)) {
         return true;
     }
     // count outcomes
     $sql = "SELECT COUNT(id) FROM {grade_outcomes} WHERE scaleid = ?";
     if ($DB->count_records_sql($sql, $params)) {
         return true;
     }
     // Ask the competency subsystem.
     if (\core_competency\api::is_scale_used_anywhere($this->id)) {
         return true;
     }
     // Ask all plugins if the scale is used anywhere.
     $pluginsfunction = get_plugins_with_function('scale_used_anywhere');
     foreach ($pluginsfunction as $plugintype => $plugins) {
         foreach ($plugins as $pluginfunction) {
             if ($pluginfunction($this->id)) {
                 return true;
             }
         }
     }
     return false;
 }
Example #12
0
/**
 * This method will delete a course section and may delete all modules inside it.
 *
 * No permissions are checked here, use {@link course_can_delete_section()} to
 * check if section can actually be deleted.
 *
 * @param int|stdClass $course
 * @param int|stdClass|section_info $section
 * @param bool $forcedeleteifnotempty if set to false section will not be deleted if it has modules in it.
 * @param bool $async whether or not to try to delete the section using an adhoc task. Async also depends on a plugin hook.
 * @return bool whether section was deleted
 */
function course_delete_section($course, $section, $forcedeleteifnotempty = true, $async = false)
{
    global $DB;
    // Prepare variables.
    $courseid = is_object($course) ? $course->id : (int) $course;
    $sectionnum = is_object($section) ? $section->section : (int) $section;
    $section = $DB->get_record('course_sections', array('course' => $courseid, 'section' => $sectionnum));
    if (!$section) {
        // No section exists, can't proceed.
        return false;
    }
    // Check the 'course_module_background_deletion_recommended' hook first.
    // Only use asynchronous deletion if at least one plugin returns true and if async deletion has been requested.
    // Both are checked because plugins should not be allowed to dictate the deletion behaviour, only support/decline it.
    // It's up to plugins to handle things like whether or not they are enabled.
    if ($async && ($pluginsfunction = get_plugins_with_function('course_module_background_deletion_recommended'))) {
        foreach ($pluginsfunction as $plugintype => $plugins) {
            foreach ($plugins as $pluginfunction) {
                if ($pluginfunction()) {
                    return course_delete_section_async($section, $forcedeleteifnotempty);
                }
            }
        }
    }
    $format = course_get_format($course);
    $sectionname = $format->get_section_name($section);
    // Delete section.
    $result = $format->delete_section($section, $forcedeleteifnotempty);
    // Trigger an event for course section deletion.
    if ($result) {
        $context = context_course::instance($courseid);
        $event = \core\event\course_section_deleted::create(array('objectid' => $section->id, 'courseid' => $courseid, 'context' => $context, 'other' => array('sectionnum' => $section->section, 'sectionname' => $sectionname)));
        $event->add_record_snapshot('course_sections', $section);
        $event->trigger();
    }
    return $result;
}