Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
block_course_overview_uwmoodle.php 14.87 KiB
<?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 overview block for UW Moodle
 *
 * This block improves on the course_summary block by presenting courses sorted by term.
 * It also allows showing/hiding of terms and course news summaries.
 *
 * The block is dependent on the UW enrollment plugins.
 *
 * @author Matt Petro
 */

require_once($CFG->dirroot.'/lib/weblib.php');

class block_course_overview_uwmoodle extends block_base {
    /**
     * block initializations
     */
    public function init() {
        $this->title   = get_string('pluginname', 'block_course_overview_uwmoodle');
    }

    const TERM_OTHER = 0;  // UW "term_code" for non-timetable classes

    /**
     * block contents
     *
     * @return object
     */
    public function get_content() {
        global $USER, $CFG, $PAGE, $OUTPUT, $DB;
        if($this->content !== NULL) {
            return $this->content;
        }

        ob_start();

        $courses = enrol_get_my_courses('id, shortname, modinfo, sectioncache', 'visible DESC,sortorder ASC', 0);
        $site = get_site();
        $course = $site; //just in case we need the old global $course hack

        // Keep track of hideable items for initializing javascript
        $yuiitems = array();

        // Process GET parameters.  Generally AJAX will be used instead of these.
        $uwmterm = optional_param('uwmterm', null, PARAM_INT);
        $uwmcourse = optional_param('uwmcourse', null, PARAM_INT);
        $uwmshow = optional_param('uwmshow', 1, PARAM_INT);
        if (!is_null($uwmterm)) {
            set_user_preference('block_course_overview_uwmoodle-show-term-'.$uwmterm, $uwmshow);
        }
        if (!is_null($uwmcourse)) {
            set_user_preference('block_course_overview_uwmoodle-show-coursenews-'.$uwmcourse, $uwmshow);
        }

        $currentterm = get_config('block_course_overview_uwmoodle', 'currentterm');
        if (!$currentterm) {
            $currentterm = self::TERM_OTHER;
        }

        // Find the termcode for each course
        if (!empty($courses)) {
            $courseids = array();
            foreach ($courses as $course) {
                $courseids[] = $course->id;
            }
            list ($insql, $inparams) = $DB->get_in_or_equal($courseids);
            $select = "enrol='wisc' AND courseid $insql";
            $enrols = $DB->get_records_select('enrol', $select, $inparams, '', 'id,courseid,customchar1');
            foreach ($enrols as $enrol) {
                if (empty($courses[$enrol->courseid]->term) || $courses[$enrol->courseid]->term < $enrol->customchar1) {
                    $courses[$enrol->courseid]->term = $enrol->customchar1;
                }
            }
        }
        // Sort courses by course shortname
        uasort($courses, function($a,$b) { if ($a->shortname == $b->shortname) {return 0;} return ($a->shortname > $b->shortname)? 1 : -1;} );

        // Organize courses into terms, maintaining shortname sorting inside each term
        $terms = array();
        foreach ($courses as $course) {
            if (!empty($course->term)) {
                $terms[$course->term][] = $course;
            } else {
                $terms[self::TERM_OTHER][] = $course;
            }
        }
        // Sort terms, descending
        // TERM_OTHER courses get ordered right after current term
        $sortfcn = function($a,$b) use ($currentterm) {
            if ($a == self::TERM_OTHER) { $a = $currentterm - 0.5; }
            if ($b == self::TERM_OTHER) { $b = $currentterm - 0.5; }
            if ($a == $b) {return 0;}
            return ($a > $b)? -1 : 1;
        };
        uksort($terms, $sortfcn);

        // Update lastaccess for news summary
        foreach ($courses as $c) {
            if (isset($USER->lastcourseaccess[$c->id])) {
                $courses[$c->id]->lastaccess = $USER->lastcourseaccess[$c->id];
            } else {
                $courses[$c->id]->lastaccess = 0;
            }
        }

        if (empty($courses)) {
            echo get_string('nocourses','my');
        } else {
            // Get modinfo -- copied from print_overview()
            $htmlarray = array();
            if ($modules = $DB->get_records('modules')) {
                foreach ($modules as $mod) {
                    if (file_exists($CFG->dirroot.'/mod/'.$mod->name.'/lib.php')) {
                        include_once($CFG->dirroot.'/mod/'.$mod->name.'/lib.php');
                        $fname = $mod->name.'_print_overview';
                        if (function_exists($fname)) {
                            $fname($courses,$htmlarray);
                        }
                    }
                }
            }

            // display courses
            echo '<div class="courselist">';
            echo '<ul class="treelist">';
            foreach ($terms as $termcode=>$termcourses) {
                $yuiitems[] = "term-$termcode";
                $termname = $this->get_term_name($termcode);

                $defaultshowterm = ($termcode == $currentterm || $termcode == self::TERM_OTHER)? 1 : 0;
                $showterm = get_user_preferences("block_course_overview_uwmoodle-show-term-$termcode", $defaultshowterm);
                if (!$showterm) {
                    $class = 'collapsed';
                    $ariaexpanded = "aria-expanded='false'";
                } else {
                    $class = '';
                    $ariaexpanded = "aria-expanded='true'";
                }
                if ($termcode == $currentterm) {
                    $termclass = 'currentterm';
                } else if ($termcode == self::TERM_OTHER) {
                    $termclass = 'miscterm';
                } else {
                    $termclass = 'noncurrentterm';
                }
                $aria = "$ariaexpanded aria-controls='uwm-{$this->instance->id}-term-$termcode-list' role='button'";
                $showhidetermurl = $PAGE->url->out_as_local_url(true, array('uwmterm' => $termcode, 'uwmshow' => ($showterm)? 0 : 1 ));

                echo "<li class='$class'>";
                echo "<h3 class='$termclass'>";
                // The data-ajax attribute prevents the Moodle mobile theme from overriding the AJAX behavior
                echo "<a class='showhide' data-ajax='false' $aria href='$showhidetermurl' id='uwm-{$this->instance->id}-term-$termcode'><span class='term treeitem'>$termname</span></a>";
                echo "</h3>";
                echo "<ul class='treelist' id='uwm-{$this->instance->id}-term-$termcode-list' role='region'>";
                foreach ($termcourses as $course) {
                    $fullname = format_string($course->fullname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id)));
                    $hasnews = !empty($htmlarray[$course->id]);
                    $attributes = array('title' => s($fullname));
                    if (empty($course->visible)) {
                        $attributes['class'] = 'dimmed';
                    }

                    $newsclass = '';
                    $showhidehtml = '';
                    if ($hasnews) {
                        $defaultshownews = ($termcode == $currentterm || $termcode == self::TERM_OTHER)? 1 : 0;
                        $shownews = get_user_preferences('block_course_overview_uwmoodle-show-coursenews-'.$course->id, 1);
                        $yuiitems[] = "coursenews-$course->id";
                        $showhidenewsurl = $PAGE->url->out_as_local_url(true, array('uwmcourse' => $course->id, 'uwmshow' => ($shownews)? 0 : 1));
                        if (!$shownews) {
                            $newsclass = 'collapsed';
                            $showhidetext = "Show";
                            $ariaexpanded = "aria-expanded='false'";
                        } else {
                            $newsclass = '';
                            $showhidetext = "Hide";
                            $ariaexpanded = "aria-expanded='true'";
                        }
                        $aria = "$ariaexpanded aria-controls='uwm-{$this->instance->id}-coursenews-{$course->id}-list' role='button'";
                        // The data-ajax attribute prevents the Moodle mobile theme from overriding the AJAX behavior
                        $showhidehtml =  "<div class='shownews'><a data-ajax='false' $aria href='$showhidenewsurl' id='uwm-{$this->instance->id}-coursenews-$course->id' >";
                        $showhidehtml .= "<span class='coursenews treeitem'><span class='showhidetext'>$showhidetext</span> news";
                        $showhidehtml .= "</span></a></div>";

                    }
                    echo "<li class='$newsclass'>";
                    echo $OUTPUT->box_start("coursebox");
                    echo $OUTPUT->heading(html_writer::link(
                            new moodle_url('/course/view.php', array('id' => $course->id)), $fullname, $attributes) . $showhidehtml, 3);
                    if ($hasnews) {
                        echo "<ul class='treelist' id='uwm-{$this->instance->id}-coursenews-{$course->id}-list' role='region'>";
                        foreach ($htmlarray[$course->id] as $modname => $html) {
                            echo "<li>$html</li>";
                        }
                        echo "</ul>";
                    }
                    echo $OUTPUT->box_end();
                    echo "</li>";
                }
                echo "</ul>";
                echo '</li>';
            }
            echo '</ul>';
            echo "</div>";

        }

        // Initialize YUI module if needed
        if (!empty($yuiitems)) {
            $arguments = array(
                    'id'             => $this->instance->id,
                    'items'          => implode(' ', $yuiitems),
            );
            $PAGE->requires->yui_module('moodle-block_course_overview_uwmoodle-showhide', 'M.block_course_overview_uwmoodle.initialize', array($arguments));

            foreach ($yuiitems as $item) {
                user_preference_allow_ajax_update('block_course_overview_uwmoodle-show-'.$item, PARAM_INT);
            }
        }

        // Get and end output buffer
        $content = ob_get_contents();
        ob_end_clean();

        $this->content = new stdClass();
        $this->content->footer = '';
        $this->content->text = $content;

        // Show "more courses" link if appropriate
        if (has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM)) || empty($CFG->block_course_list_hideallcourseslink)) {
            $this->content->footer = "<a href=\"$CFG->wwwroot/course/index.php\">".get_string("fulllistofcourses")."</a> ...";
        }

        return $this->content;
    }

    /**
     * Return a string representing the term (e.g. "Fall 2010")
     * This function doesn't make any remote calls.
     *
     * @param string $termCode
     * @return string $termName
     */
    public function get_term_name($termCode) {
        $termCode = (string)$termCode;

        $c = substr($termCode,0,1);
        $yy = substr($termCode,1,2);
        $year = 1900+100*$c+$yy;
        $semester = substr($termCode,3,1);
        switch($semester) {
            case 2:
                $name = sprintf("Fall %d courses", $year-1);
                break;
            case 3:
                $name = sprintf("Winter %d courses", $year);
                break;
            case 4:
                $name = sprintf("Spring %d courses", $year);
                break;
            case 6:
                $name = sprintf("Summer %d courses", $year);
                break;
            default:
                $name = "Miscellaneous courses";
        }
        return $name;
    }

    /**
     * Get the current term from the datastore, and update user show/hide preferences when it changes.
     *
     * @return boolean true on success
     */
    public function update_current_term() {
        global $CFG, $DB;

        $oldcurrentterm = get_config('block_course_overview_uwmoodle', 'currentterm');
        try {
            $currentterm = $this->get_current_term();
        } catch (Exception $e) {
            mtrace("Error fetching current term: ".$e->getMessage());
            return false;
        }
        if ($currentterm === false) {
            mtrace("No current term found");
            return false;
        }

        if ($currentterm === $oldcurrentterm) {
            // no change, so return
            return true;
        }
        mtrace('new current term: '.$currentterm);

        // New term, so clear user preferences for both current and previous term
        if ($oldcurrentterm) {
            $DB->delete_records('user_preferences', array('name'=> 'block_course_overview_uwmoodle-show-term-'.$oldcurrentterm));
        }
        $DB->delete_records('user_preferences', array('name'=> 'block_course_overview_uwmoodle-show-term-'.$currentterm));

        set_config('currentterm', $currentterm, 'block_course_overview_uwmoodle');
        return true;
    }

    /**
     * Fetch current term from CHUB.  Throw exception on CHUB error.
     *
     * @return string|false  term_code or false if none found.
     */
    public function get_current_term() {
        global $CFG;

        require_once($CFG->dirroot.'/enrol/wisc/lib/datastore.php');
        $datastore = wisc_timetable_datastore::get_timetable_datastore();
        $terms = $datastore->getAvailableTerms();

        $now = time();
        $futureterms = array();
        foreach ($terms as $term) {
            // check that term hasn't ended and doesn't have an odd termCode
            // odd term codes (e.g. Winter) are never considered the current term
            if ($term->endDate > $now && ($term->termCode % 2 != 1)) {
                $futureterms[] = $term->termCode;
            }
        }
        if (!empty($futureterms)) {
            $currentterm = min($futureterms);
        } else {
            $currentterm = false;
        }
        return $currentterm;
    }

    /**
     * allow the block to have a configuration page
     *
     * @return boolean
     */
    public function has_config() {
        return false;
    }

    /**
     * locations where block can be displayed
     *
     * @return array
     */
    public function applicable_formats() {
        return array('my-index'=>true);
    }

    /**
     * Block cron to update currentterm
     *
     * @return boolean true on success
     */
    public function cron() {
        return $this->update_current_term();
    }
}