__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

www-data@216.73.216.10: ~ $
<?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/>.

/**
 * Collects information and methods about feedback completion (either complete.php or show_entries.php)
 *
 * @package   mod_feedback
 * @copyright 2016 Marina Glancy
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class mod_feedback_completion extends mod_feedback_structure {
    /** @var stdClass */
    protected $completed;
    /** @var stdClass */
    protected $completedtmp = null;
    /** @var stdClass[] */
    protected $valuestmp = null;
    /** @var stdClass[] */
    protected $values = null;
    /** @var bool */
    protected $iscompleted = false;
    /** @var mod_feedback_complete_form the form used for completing the feedback */
    protected $form = null;
    /** @var bool true when the feedback has been completed during the request */
    protected $justcompleted = false;
    /** @var int the next page the user should jump after processing the form */
    protected $jumpto = null;


    /**
     * Constructor
     *
     * @param stdClass $feedback feedback object
     * @param cm_info $cm course module object corresponding to the $feedback
     *     (at least one of $feedback or $cm is required)
     * @param int $courseid current course (for site feedbacks only)
     * @param bool $iscompleted has feedback been already completed? If yes either completedid or userid must be specified.
     * @param int $completedid id in the table feedback_completed, may be omitted if userid is specified
     *     but it is highly recommended because the same user may have multiple responses to the same feedback
     *     for different courses
     * @param int $nonanonymouseuserid - Return only anonymous results or specified user's results.
     *     If null only anonymous replies will be returned and the $completedid is mandatory.
     *     If specified only non-anonymous replies of $nonanonymouseuserid will be returned.
     * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
     */
    public function __construct($feedback, $cm, $courseid, $iscompleted = false, $completedid = null,
                                $nonanonymouseuserid = null, $userid = 0) {
        global $DB;

        parent::__construct($feedback, $cm, $courseid, 0, $userid);
        // Make sure courseid is always set for site feedback.
        if ($this->feedback->course == SITEID && !$this->courseid) {
            $this->courseid = SITEID;
        }
        if ($iscompleted) {
            // Retrieve information about the completion.
            $this->iscompleted = true;
            $params = array('feedback' => $this->feedback->id);
            if (!$nonanonymouseuserid && !$completedid) {
                throw new coding_exception('Either $completedid or $nonanonymouseuserid must be specified for completed feedbacks');
            }
            if ($completedid) {
                $params['id'] = $completedid;
            }
            if ($nonanonymouseuserid) {
                // We must respect the anonymousity of the reply that the user saw when they were completing the feedback,
                // not the current state that may have been changed later by the teacher.
                $params['anonymous_response'] = FEEDBACK_ANONYMOUS_NO;
                $params['userid'] = $nonanonymouseuserid;
            }
            $this->completed = $DB->get_record('feedback_completed', $params, '*', MUST_EXIST);

            if (!groups_user_groups_visible($this->feedback->course, $this->completed->userid, $cm)) {
                throw new moodle_exception('nopermissiontoshow');
            }

            $this->courseid = $this->completed->courseid;
        }
    }

    /**
     * Returns a record from 'feedback_completed' table
     * @return stdClass
     */
    public function get_completed() {
        return $this->completed;
    }

    /**
     * Check if the feedback was just completed.
     *
     * @return bool true if the feedback was just completed.
     * @since  Moodle 3.3
     */
    public function just_completed() {
        return $this->justcompleted;
    }

    /**
     * Return the jumpto property.
     *
     * @return int the next page to jump.
     * @since  Moodle 3.3
     */
    public function get_jumpto() {
        return $this->jumpto;
    }

    /**
     * Returns the temporary completion record for the current user or guest session
     *
     * @return stdClass|false record from feedback_completedtmp or false if not found
     */
    public function get_current_completed_tmp() {
        global $DB, $USER;
        if ($this->completedtmp === null) {
            $params = array('feedback' => $this->get_feedback()->id);
            if ($courseid = $this->get_courseid()) {
                $params['courseid'] = $courseid;
            }
            if ((isloggedin() || $USER->id != $this->userid) && !isguestuser($this->userid)) {
                $params['userid'] = $this->userid;
            } else {
                $params['guestid'] = sesskey();
            }
            $this->completedtmp = $DB->get_record('feedback_completedtmp', $params);
        }
        return $this->completedtmp;
    }

    /**
     * Can the current user see the item, if dependency is met?
     *
     * @param stdClass $item
     * @return bool whether user can see item or not,
     *     true if there is no dependency or dependency is met,
     *     false if dependent question is visible or broken
     *        and further it is either not answered or the dependency is not met,
     *     null if dependency is broken.
     */
    protected function can_see_item($item) {
        if (empty($item->dependitem)) {
            return true;
        }
        if ($this->dependency_has_error($item)) {
            return null;
        }
        $allitems = $this->get_items();
        $ditem = $allitems[$item->dependitem];
        $itemobj = feedback_get_item_class($ditem->typ);
        if ($this->iscompleted) {
            $value = $this->get_values($ditem);
        } else {
            $value = $this->get_values_tmp($ditem);
        }
        if ($value === null) {
            // Cyclic dependencies are no problem here, since they will throw an dependency error above.
            if ($this->can_see_item($ditem) === false) {
                return false;
            }
            return null;
        }
        $check = $itemobj->compare_value($ditem, $value, $item->dependvalue) ? true : false;
        if ($check) {
            return $this->can_see_item($ditem);
        }
        return false;
    }

    /**
     * Dependency condition has an error
     * @param stdClass $item
     * @return bool
     */
    protected function dependency_has_error($item) {
        if (empty($item->dependitem)) {
            // No dependency - no error.
            return false;
        }
        $allitems = $this->get_items();
        if (!array_key_exists($item->dependitem, $allitems)) {
            // Looks like dependent item has been removed.
            return true;
        }
        $itemids = array_keys($allitems);
        $index1 = array_search($item->dependitem, $itemids);
        $index2 = array_search($item->id, $itemids);
        if ($index1 >= $index2) {
            // Dependent item is after the current item in the feedback.
            return true;
        }
        for ($i = $index1 + 1; $i < $index2; $i++) {
            if ($allitems[$itemids[$i]]->typ === 'pagebreak') {
                return false;
            }
        }
        // There are no page breaks between dependent items.
        return true;
    }

    /**
     * Returns a value stored for this item in the feedback (temporary or not, depending on the mode)
     * @param stdClass $item
     * @return string
     */
    public function get_item_value($item) {
        if ($this->iscompleted) {
            return $this->get_values($item);
        } else {
            return $this->get_values_tmp($item);
        }
    }

    /**
     * Retrieves responses from an unfinished attempt.
     *
     * @return array the responses (from the feedback_valuetmp table)
     * @since  Moodle 3.3
     */
    public function get_unfinished_responses() {
        global $DB;
        $responses = array();

        $completedtmp = $this->get_current_completed_tmp();
        if ($completedtmp) {
            $responses = $DB->get_records('feedback_valuetmp', ['completed' => $completedtmp->id]);
        }
        return $responses;
    }

    /**
     * Returns all temporary values for this feedback or just a value for an item
     * @param stdClass $item
     * @return array
     */
    protected function get_values_tmp($item = null) {
        global $DB;
        if ($this->valuestmp === null) {
            $this->valuestmp = array();
            $responses = $this->get_unfinished_responses();
            foreach ($responses as $r) {
                $this->valuestmp[$r->item] = $r->value;
            }
        }
        if ($item) {
            return array_key_exists($item->id, $this->valuestmp) ? $this->valuestmp[$item->id] : null;
        }
        return $this->valuestmp;
    }

    /**
     * Retrieves responses from an finished attempt.
     *
     * @return array the responses (from the feedback_value table)
     * @since  Moodle 3.3
     */
    public function get_finished_responses() {
        global $DB;
        $responses = array();

        if ($this->completed) {
            $responses = $DB->get_records('feedback_value', ['completed' => $this->completed->id]);
        }
        return $responses;
    }

    /**
     * Returns all completed values for this feedback or just a value for an item
     * @param stdClass $item
     * @return array
     */
    protected function get_values($item = null) {
        global $DB;
        if ($this->values === null) {
            $this->values = array();
            $responses = $this->get_finished_responses();
            foreach ($responses as $r) {
                $this->values[$r->item] = $r->value;
            }
        }
        if ($item) {
            return array_key_exists($item->id, $this->values) ? $this->values[$item->id] : null;
        }
        return $this->values;
    }

    /**
     * Splits the feedback items into pages
     *
     * Items that we definitely know at this stage as not applicable are excluded.
     * Items that are dependent on something that has not yet been answered are
     * still present, as well as items with broken dependencies.
     *
     * @return array array of arrays of items
     */
    public function get_pages() {
        $pages = [[]]; // The first page always exists.
        $items = $this->get_items();
        foreach ($items as $item) {
            if ($item->typ === 'pagebreak') {
                $pages[] = [];
            } else if ($this->can_see_item($item) !== false) {
                $pages[count($pages) - 1][] = $item;
            }
        }
        return $pages;
    }

    /**
     * Returns the last page that has items with the value (i.e. not label) which have been answered
     * as well as the first page that has items with the values that have not been answered.
     *
     * Either of the two return values may be null if there are no answered page or there are no
     * unanswered pages left respectively.
     *
     * Two pages may not be directly following each other because there may be empty pages
     * or pages with information texts only between them
     *
     * @return array array of two elements [$lastcompleted, $firstincompleted]
     */
    protected function get_last_completed_page() {
        $completed = [];
        $incompleted = [];
        $pages = $this->get_pages();
        foreach ($pages as $pageidx => $pageitems) {
            foreach ($pageitems as $item) {
                if ($item->hasvalue) {
                    if ($this->get_values_tmp($item) !== null) {
                        $completed[$pageidx] = true;
                    } else {
                        $incompleted[$pageidx] = true;
                    }
                }
            }
        }
        $completed = array_keys($completed);
        $incompleted = array_keys($incompleted);
        // If some page has both completed and incompleted items it is considered incompleted.
        $completed = array_diff($completed, $incompleted);
        // If the completed page follows an incompleted page, it does not count.
        $firstincompleted = $incompleted ? min($incompleted) : null;
        if ($firstincompleted !== null) {
            $completed = array_filter($completed, function($a) use ($firstincompleted) {
                return $a < $firstincompleted;
            });
        }
        $lastcompleted = $completed ? max($completed) : null;
        return [$lastcompleted, $firstincompleted];
    }

    /**
     * Get the next page for the feedback
     *
     * This is normally $gopage+1 but may be bigger if there are empty pages or
     * pages without visible questions.
     *
     * This method can only be called when questions on the current page are
     * already answered, otherwise it may be inaccurate.
     *
     * @param int $gopage current page
     * @param bool $strictcheck when gopage is the user-input value, make sure we do not jump over unanswered questions
     * @return int|null the index of the next page or null if this is the last page
     */
    public function get_next_page($gopage, $strictcheck = true) {
        if ($strictcheck) {
            list($lastcompleted, $firstincompleted) = $this->get_last_completed_page();
            if ($firstincompleted !== null && $firstincompleted <= $gopage) {
                return $firstincompleted;
            }
        }
        $pages = $this->get_pages();
        for ($pageidx = $gopage + 1; $pageidx < count($pages); $pageidx++) {
            if (!empty($pages[$pageidx])) {
                return $pageidx;
            }
        }
        // No further pages in the feedback have any visible items.
        return null;
    }

    /**
     * Get the previous page for the feedback
     *
     * This is normally $gopage-1 but may be smaller if there are empty pages or
     * pages without visible questions.
     *
     * @param int $gopage current page
     * @param bool $strictcheck when gopage is the user-input value, make sure we do not jump over unanswered questions
     * @return int|null the index of the next page or null if this is the first page with items
     */
    public function get_previous_page($gopage, $strictcheck = true) {
        if (!$gopage) {
            // If we are already on the first (0) page, there is definitely no previous page.
            return null;
        }
        $pages = $this->get_pages();
        $rv = null;
        // Iterate through previous pages and find the closest one that has any items on it.
        for ($pageidx = $gopage - 1; $pageidx >= 0; $pageidx--) {
            if (!empty($pages[$pageidx])) {
                $rv = $pageidx;
                break;
            }
        }
        if ($rv === null) {
            // We are on the very first page that has items.
            return null;
        }
        if ($rv > 0 && $strictcheck) {
            // Check if this page is actually not past than first incompleted page.
            list($lastcompleted, $firstincompleted) = $this->get_last_completed_page();
            if ($firstincompleted !== null && $firstincompleted < $rv) {
                return $firstincompleted;
            }
        }
        return $rv;
    }

    /**
     * Page index to resume the feedback
     *
     * When user abandones answering feedback and then comes back to it we should send him
     * to the first page after the last page he fully completed.
     * @return int
     */
    public function get_resume_page() {
        list($lastcompleted, $firstincompleted) = $this->get_last_completed_page();
        return $lastcompleted === null ? 0 : $this->get_next_page($lastcompleted, false);
    }

    /**
     * Creates a new record in the 'feedback_completedtmp' table for the current user/guest session
     *
     * @return stdClass record from feedback_completedtmp or false if not found
     */
    protected function create_current_completed_tmp() {
        global $DB, $USER;
        $record = (object)['feedback' => $this->feedback->id];
        if ($this->get_courseid()) {
            $record->courseid = $this->get_courseid();
        }
        if ((isloggedin() || $USER->id != $this->userid) && !isguestuser($this->userid)) {
            $record->userid = $this->userid;
        } else {
            $record->guestid = sesskey();
        }
        $record->timemodified = time();
        $record->anonymous_response = $this->feedback->anonymous;
        $id = $DB->insert_record('feedback_completedtmp', $record);
        $this->completedtmp = $DB->get_record('feedback_completedtmp', ['id' => $id]);
        $this->valuestmp = null;
        return $this->completedtmp;
    }

    /**
     * If user has already completed the feedback, create the temproray values from last completed attempt
     *
     * @return stdClass record from feedback_completedtmp or false if not found
     */
    public function create_completed_tmp_from_last_completed() {
        if (!$this->get_current_completed_tmp()) {
            $lastcompleted = $this->find_last_completed();
            if ($lastcompleted) {
                $this->completedtmp = feedback_set_tmp_values($lastcompleted);
            }
        }
        return $this->completedtmp;
    }

    /**
     * Saves unfinished response to the temporary table
     *
     * This is called when user proceeds to the next/previous page in the complete form
     * and also right after the form submit.
     * After the form submit the {@link save_response()} is called to
     * move response from temporary table to completion table.
     *
     * @param stdClass $data data from the form mod_feedback_complete_form
     */
    public function save_response_tmp($data) {
        global $DB;
        if (!$completedtmp = $this->get_current_completed_tmp()) {
            $completedtmp = $this->create_current_completed_tmp();
        } else {
            $currentime = time();
            $DB->update_record('feedback_completedtmp',
                    ['id' => $completedtmp->id, 'timemodified' => $currentime]);
            $completedtmp->timemodified = $currentime;
        }

        // Find all existing values.
        $existingvalues = $DB->get_records_menu('feedback_valuetmp',
                ['completed' => $completedtmp->id], '', 'item, id');

        // Loop through all feedback items and save the ones that are present in $data.
        $allitems = $this->get_items();
        foreach ($allitems as $item) {
            if (!$item->hasvalue) {
                continue;
            }
            $keyname = $item->typ . '_' . $item->id;
            if (!isset($data->$keyname)) {
                // This item is either on another page or dependency was not met - nothing to save.
                continue;
            }

            $newvalue = ['item' => $item->id, 'completed' => $completedtmp->id, 'course_id' => $completedtmp->courseid];

            // Convert the value to string that can be stored in 'feedback_valuetmp' or 'feedback_value'.
            $itemobj = feedback_get_item_class($item->typ);
            $newvalue['value'] = $itemobj->create_value($data->$keyname);

            // Update or insert the value in the 'feedback_valuetmp' table.
            if (array_key_exists($item->id, $existingvalues)) {
                $newvalue['id'] = $existingvalues[$item->id];
                $DB->update_record('feedback_valuetmp', $newvalue);
            } else {
                $DB->insert_record('feedback_valuetmp', $newvalue);
            }
        }

        // Reset valuestmp cache.
        $this->valuestmp = null;
    }

    /**
     * Saves the response
     *
     * The form data has already been stored in the temporary table in
     * {@link save_response_tmp()}. This function copies the values
     * from the temporary table to the completion table.
     * It is also responsible for sending email notifications when applicable.
     */
    public function save_response() {
        global $SESSION, $DB, $USER;

        $feedbackcompleted = $this->find_last_completed();
        // If no record is found, change false to null for safe use in feedback_save_tmp_values.
        $feedbackcompleted = !$feedbackcompleted ? null : $feedbackcompleted;
        $feedbackcompletedtmp = $this->get_current_completed_tmp();

        // Save values.
        $completedid = feedback_save_tmp_values($feedbackcompletedtmp, $feedbackcompleted);
        $this->completed = $DB->get_record('feedback_completed', array('id' => $completedid));

        // Send email.
        if ($this->feedback->anonymous == FEEDBACK_ANONYMOUS_NO) {
            feedback_send_email($this->cm, $this->feedback, $this->cm->get_course(), $this->userid, $this->completed);
        } else {
            feedback_send_email_anonym($this->cm, $this->feedback, $this->cm->get_course());
        }

        unset($SESSION->feedback->is_started);

        // Update completion state.
        $completion = new completion_info($this->cm->get_course());
        if ((isloggedin() || $USER->id != $this->userid) && $completion->is_enabled($this->cm) &&
                $this->cm->completion == COMPLETION_TRACKING_AUTOMATIC && $this->feedback->completionsubmit) {
            $completion->update_state($this->cm, COMPLETION_COMPLETE, $this->userid);
        }
    }

    /**
     * Retrieves the last completion record for the current user
     *
     * @return stdClass record from feedback_completed or false if not found
     */
    public function find_last_completed() {
        global $DB, $USER;
        if ((!isloggedin() && $USER->id == $this->userid) || isguestuser($this->userid)) {
            // Not possible to retrieve completed feedback for guests.
            return false;
        }
        if ($this->is_anonymous()) {
            // Not possible to retrieve completed anonymous feedback.
            return false;
        }
        $params = array('feedback' => $this->feedback->id,
            'userid' => $this->userid,
            'anonymous_response' => FEEDBACK_ANONYMOUS_NO
        );
        if ($this->get_courseid()) {
            $params['courseid'] = $this->get_courseid();
        }
        $this->completed = $DB->get_record('feedback_completed', $params);
        return $this->completed;
    }

    /**
     * Checks if user has capability to submit the feedback
     *
     * There is an exception for fully anonymous feedbacks when guests can complete
     * feedback without the proper capability.
     *
     * This should be followed by checking {@link can_submit()} because even if
     * user has capablity to complete, they may have already submitted feedback
     * and can not re-submit
     *
     * @return bool
     */
    public function can_complete() {
        global $CFG, $USER;

        $context = context_module::instance($this->cm->id);
        if (has_capability('mod/feedback:complete', $context, $this->userid)) {
            return true;
        }

        if (!empty($CFG->feedback_allowfullanonymous)
                    AND $this->feedback->course == SITEID
                    AND $this->feedback->anonymous == FEEDBACK_ANONYMOUS_YES
                    AND ((!isloggedin() && $USER->id == $this->userid) || isguestuser($this->userid))) {
            // Guests are allowed to complete fully anonymous feedback without having 'mod/feedback:complete' capability.
            return true;
        }

        return false;
    }

    /**
     * Checks if user is prevented from re-submission.
     *
     * This must be called after {@link can_complete()}
     *
     * @return bool
     */
    public function can_submit() {
        if ($this->get_feedback()->multiple_submit == 0 ) {
            if ($this->is_already_submitted()) {
                return false;
            }
        }
        return true;
    }

    /**
     * Trigger module viewed event.
     *
     * @since Moodle 3.3
     */
    public function trigger_module_viewed() {
        $event = \mod_feedback\event\course_module_viewed::create_from_record($this->feedback, $this->cm, $this->cm->get_course());
        $event->trigger();
    }

    /**
     * Mark activity viewed for completion-tracking.
     *
     * @since Moodle 3.3
     */
    public function set_module_viewed() {
        global $CFG;
        require_once($CFG->libdir . '/completionlib.php');

        $completion = new completion_info($this->cm->get_course());
        $completion->set_module_viewed($this->cm, $this->userid);
    }

    /**
     * Process a page jump via the mod_feedback_complete_form.
     *
     * This function initializes the form and process the submission.
     *
     * @param  int $gopage         the current page
     * @param  int $gopreviouspage if the user chose to go to the previous page
     * @return string the url to redirect the user (if any)
     * @since  Moodle 3.3
     */
    public function process_page($gopage, $gopreviouspage = false) {
        global $CFG, $PAGE, $SESSION;

        $urltogo = null;

        // Save the form for later during the request.
        $this->create_completed_tmp_from_last_completed();
        $this->form = new mod_feedback_complete_form(mod_feedback_complete_form::MODE_COMPLETE,
            $this, 'feedback_complete_form', array('gopage' => $gopage));

        if ($this->form->is_cancelled()) {
            // Form was cancelled - return to the course page.
            $urltogo = new moodle_url('/mod/feedback/view.php', ['id' => $this->get_cm()->id]);
        } else if ($this->form->is_submitted() &&
                ($this->form->is_validated() || $gopreviouspage)) {
            // Form was submitted (skip validation for "Previous page" button).
            $data = $this->form->get_submitted_data();
            if (!isset($SESSION->feedback->is_started) OR !$SESSION->feedback->is_started == true) {
                throw new \moodle_exception('error', '', $CFG->wwwroot.'/course/view.php?id='.$this->courseid);
            }
            $this->save_response_tmp($data);
            if (!empty($data->savevalues) || !empty($data->gonextpage)) {
                if (($nextpage = $this->get_next_page($gopage)) !== null) {
                    if ($PAGE->has_set_url()) {
                        $urltogo = new moodle_url($PAGE->url, array('gopage' => $nextpage));
                    }
                    $this->jumpto = $nextpage;
                } else {
                    $this->save_response();
                    if (!$this->get_feedback()->page_after_submit) {
                        \core\notification::success(get_string('entries_saved', 'feedback'));
                    }
                    $this->justcompleted = true;
                }
            } else if (!empty($gopreviouspage)) {
                $prevpage = intval($this->get_previous_page($gopage));
                if ($PAGE->has_set_url()) {
                    $urltogo = new moodle_url($PAGE->url, array('gopage' => $prevpage));
                }
                $this->jumpto = $prevpage;
            }
        }
        return $urltogo;
    }

    /**
     * Render the form with the questions.
     *
     * @return string the form rendered
     * @since Moodle 3.3
     */
    public function render_items() {
        global $SESSION;

        // Print the items.
        $SESSION->feedback->is_started = true;
        return $this->form->render();
    }
}

Filemanager

Name Type Size Permission Actions
analytics Folder 0777
completion Folder 0777
event Folder 0777
external Folder 0777
form Folder 0777
navigation Folder 0777
output Folder 0777
privacy Folder 0777
search Folder 0777
complete_form.php File 21.75 KB 0777
completion.php File 27.75 KB 0777
course_map_form.php File 1.53 KB 0777
course_select_form.php File 3.27 KB 0777
dates.php File 2.25 KB 0777
external.php File 57.06 KB 0777
observer.php File 1.52 KB 0777
responses_anon_table.php File 4.47 KB 0777
responses_table.php File 24.76 KB 0777
structure.php File 13.49 KB 0777
templates_table.php File 4 KB 0777
Filemanager