1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Contains class mod_feedback_structure 19 * 20 * @package mod_feedback 21 * @copyright 2016 Marina Glancy 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 /** 28 * Stores and manipulates the structure of the feedback or template (items, pages, etc.) 29 * 30 * @package mod_feedback 31 * @copyright 2016 Marina Glancy 32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 */ 34 class mod_feedback_structure { 35 /** @var stdClass record from 'feedback' table. 36 * Reliably has fields: id, course, timeopen, timeclose, anonymous, completionsubmit. 37 * For full object or to access any other field use $this->get_feedback() 38 */ 39 protected $feedback; 40 /** @var cm_info */ 41 protected $cm; 42 /** @var int course where the feedback is filled. For feedbacks that are NOT on the front page this is 0 */ 43 protected $courseid = 0; 44 /** @var int */ 45 protected $templateid; 46 /** @var array */ 47 protected $allitems; 48 /** @var array */ 49 protected $allcourses; 50 /** @var int */ 51 protected $userid; 52 53 /** 54 * Constructor 55 * 56 * @param stdClass $feedback feedback object, in case of the template 57 * this is the current feedback the template is accessed from 58 * @param stdClass|cm_info $cm course module object corresponding to the $feedback 59 * (at least one of $feedback or $cm is required) 60 * @param int $courseid current course (for site feedbacks only) 61 * @param int $templateid template id if this class represents the template structure 62 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default). 63 */ 64 public function __construct($feedback, $cm, $courseid = 0, $templateid = null, $userid = 0) { 65 global $USER; 66 67 if ((empty($feedback->id) || empty($feedback->course)) && (empty($cm->instance) || empty($cm->course))) { 68 throw new coding_exception('Either $feedback or $cm must be passed to constructor'); 69 } 70 $this->feedback = $feedback ?: (object)['id' => $cm->instance, 'course' => $cm->course]; 71 $this->cm = ($cm && $cm instanceof cm_info) ? $cm : 72 get_fast_modinfo($this->feedback->course)->instances['feedback'][$this->feedback->id]; 73 $this->templateid = $templateid; 74 $this->courseid = ($this->feedback->course == SITEID) ? $courseid : 0; 75 76 if (empty($userid)) { 77 $this->userid = $USER->id; 78 } else { 79 $this->userid = $userid; 80 } 81 82 if (!$feedback) { 83 // If feedback object was not specified, populate object with fields required for the most of methods. 84 // These fields were added to course module cache in feedback_get_coursemodule_info(). 85 // Full instance record can be retrieved by calling mod_feedback_structure::get_feedback(). 86 $customdata = ($this->cm->customdata ?: []) + ['timeopen' => 0, 'timeclose' => 0, 'anonymous' => 0]; 87 $this->feedback->timeopen = $customdata['timeopen']; 88 $this->feedback->timeclose = $customdata['timeclose']; 89 $this->feedback->anonymous = $customdata['anonymous']; 90 $this->feedback->completionsubmit = empty($this->cm->customdata['customcompletionrules']['completionsubmit']) ? 0 : 1; 91 } 92 } 93 94 /** 95 * Current feedback 96 * @return stdClass 97 */ 98 public function get_feedback() { 99 global $DB; 100 if (!isset($this->feedback->publish_stats) || !isset($this->feedback->name)) { 101 // Make sure the full object is retrieved. 102 $this->feedback = $DB->get_record('feedback', ['id' => $this->feedback->id], '*', MUST_EXIST); 103 } 104 return $this->feedback; 105 } 106 107 /** 108 * Current course module 109 * @return stdClass 110 */ 111 public function get_cm() { 112 return $this->cm; 113 } 114 115 /** 116 * Id of the current course (for site feedbacks only) 117 * @return stdClass 118 */ 119 public function get_courseid() { 120 return $this->courseid; 121 } 122 123 /** 124 * Template id 125 * @return int 126 */ 127 public function get_templateid() { 128 return $this->templateid; 129 } 130 131 /** 132 * Is this feedback open (check timeopen and timeclose) 133 * @return bool 134 */ 135 public function is_open() { 136 $checktime = time(); 137 return (!$this->feedback->timeopen || $this->feedback->timeopen <= $checktime) && 138 (!$this->feedback->timeclose || $this->feedback->timeclose >= $checktime); 139 } 140 141 /** 142 * Get all items in this feedback or this template 143 * @param bool $hasvalueonly only count items with a value. 144 * @return array of objects from feedback_item with an additional attribute 'itemnr' 145 */ 146 public function get_items($hasvalueonly = false) { 147 global $DB; 148 if ($this->allitems === null) { 149 if ($this->templateid) { 150 $this->allitems = $DB->get_records('feedback_item', ['template' => $this->templateid], 'position'); 151 } else { 152 $this->allitems = $DB->get_records('feedback_item', ['feedback' => $this->feedback->id], 'position'); 153 } 154 $idx = 1; 155 foreach ($this->allitems as $id => $item) { 156 $this->allitems[$id]->itemnr = $item->hasvalue ? ($idx++) : null; 157 } 158 } 159 if ($hasvalueonly && $this->allitems) { 160 return array_filter($this->allitems, function($item) { 161 return $item->hasvalue; 162 }); 163 } 164 return $this->allitems; 165 } 166 167 /** 168 * Is the items list empty? 169 * @return bool 170 */ 171 public function is_empty() { 172 $items = $this->get_items(); 173 $displayeditems = array_filter($items, function($item) { 174 return $item->typ !== 'pagebreak'; 175 }); 176 return !$displayeditems; 177 } 178 179 /** 180 * Is this feedback anonymous? 181 * @return bool 182 */ 183 public function is_anonymous() { 184 return $this->feedback->anonymous == FEEDBACK_ANONYMOUS_YES; 185 } 186 187 /** 188 * Returns the formatted text of the page after submit or null if it is not set 189 * 190 * @return string|null 191 */ 192 public function page_after_submit() { 193 global $CFG; 194 require_once($CFG->libdir . '/filelib.php'); 195 196 $pageaftersubmit = $this->get_feedback()->page_after_submit; 197 if (empty($pageaftersubmit)) { 198 return null; 199 } 200 $pageaftersubmitformat = $this->get_feedback()->page_after_submitformat; 201 202 $context = context_module::instance($this->get_cm()->id); 203 $output = file_rewrite_pluginfile_urls($pageaftersubmit, 204 'pluginfile.php', $context->id, 'mod_feedback', 'page_after_submit', 0); 205 206 return format_text($output, $pageaftersubmitformat, array('overflowdiv' => true)); 207 } 208 209 /** 210 * Checks if current user is able to view feedback on this course. 211 * 212 * @return bool 213 */ 214 public function can_view_analysis() { 215 global $USER; 216 217 $context = context_module::instance($this->cm->id); 218 if (has_capability('mod/feedback:viewreports', $context, $this->userid)) { 219 return true; 220 } 221 222 if (intval($this->get_feedback()->publish_stats) != 1 || 223 !has_capability('mod/feedback:viewanalysepage', $context, $this->userid)) { 224 return false; 225 } 226 227 if ((!isloggedin() && $USER->id == $this->userid) || isguestuser($this->userid)) { 228 // There is no tracking for the guests, assume that they can view analysis if condition above is satisfied. 229 return $this->feedback->course == SITEID; 230 } 231 232 return $this->is_already_submitted(true); 233 } 234 235 /** 236 * check for multiple_submit = false. 237 * if the feedback is global so the courseid must be given 238 * 239 * @param bool $anycourseid if true checks if this feedback was submitted in any course, otherwise checks $this->courseid . 240 * Applicable to frontpage feedbacks only 241 * @return bool true if the feedback already is submitted otherwise false 242 */ 243 public function is_already_submitted($anycourseid = false) { 244 global $DB, $USER; 245 246 if ((!isloggedin() && $USER->id == $this->userid) || isguestuser($this->userid)) { 247 return false; 248 } 249 250 $params = array('userid' => $this->userid, 'feedback' => $this->feedback->id); 251 if (!$anycourseid && $this->courseid) { 252 $params['courseid'] = $this->courseid; 253 } 254 return $DB->record_exists('feedback_completed', $params); 255 } 256 257 /** 258 * Check whether the feedback is mapped to the given courseid. 259 */ 260 public function check_course_is_mapped() { 261 global $DB; 262 if ($this->feedback->course != SITEID) { 263 return true; 264 } 265 if ($DB->get_records('feedback_sitecourse_map', array('feedbackid' => $this->feedback->id))) { 266 $params = array('feedbackid' => $this->feedback->id, 'courseid' => $this->courseid); 267 if (!$DB->get_record('feedback_sitecourse_map', $params)) { 268 return false; 269 } 270 } 271 // No mapping means any course is mapped. 272 return true; 273 } 274 275 /** 276 * If there are any new responses to the anonymous feedback, re-shuffle all 277 * responses and assign response number to each of them. 278 */ 279 public function shuffle_anonym_responses() { 280 global $DB; 281 $params = array('feedback' => $this->feedback->id, 282 'random_response' => 0, 283 'anonymous_response' => FEEDBACK_ANONYMOUS_YES); 284 285 if ($DB->count_records('feedback_completed', $params, 'random_response')) { 286 // Get all of the anonymous records, go through them and assign a response id. 287 unset($params['random_response']); 288 $feedbackcompleteds = $DB->get_records('feedback_completed', $params, 'id'); 289 shuffle($feedbackcompleteds); 290 $num = 1; 291 foreach ($feedbackcompleteds as $compl) { 292 $compl->random_response = $num++; 293 $DB->update_record('feedback_completed', $compl); 294 } 295 } 296 } 297 298 /** 299 * Counts records from {feedback_completed} table for a given feedback 300 * 301 * If $groupid or $this->courseid is set, the records are filtered by the group/course 302 * 303 * @param int $groupid 304 * @return mixed array of found completeds otherwise false 305 */ 306 public function count_completed_responses($groupid = 0) { 307 global $DB; 308 if (intval($groupid) > 0) { 309 $query = "SELECT COUNT(DISTINCT fbc.id) 310 FROM {feedback_completed} fbc, {groups_members} gm 311 WHERE fbc.feedback = :feedback 312 AND gm.groupid = :groupid 313 AND fbc.userid = gm.userid"; 314 } else if ($this->courseid) { 315 $query = "SELECT COUNT(fbc.id) 316 FROM {feedback_completed} fbc 317 WHERE fbc.feedback = :feedback 318 AND fbc.courseid = :courseid"; 319 } else { 320 $query = "SELECT COUNT(fbc.id) FROM {feedback_completed} fbc WHERE fbc.feedback = :feedback"; 321 } 322 $params = ['feedback' => $this->feedback->id, 'groupid' => $groupid, 'courseid' => $this->courseid]; 323 return $DB->get_field_sql($query, $params); 324 } 325 326 /** 327 * For the frontpage feedback returns the list of courses with at least one completed feedback 328 * 329 * @return array id=>name pairs of courses 330 */ 331 public function get_completed_courses() { 332 global $DB; 333 334 if ($this->get_feedback()->course != SITEID) { 335 return []; 336 } 337 338 if ($this->allcourses !== null) { 339 return $this->allcourses; 340 } 341 342 $courseselect = "SELECT fbc.courseid 343 FROM {feedback_completed} fbc 344 WHERE fbc.feedback = :feedbackid"; 345 346 $ctxselect = context_helper::get_preload_record_columns_sql('ctx'); 347 348 $sql = 'SELECT c.id, c.shortname, c.fullname, c.idnumber, c.visible, '. $ctxselect. ' 349 FROM {course} c 350 JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextcourse 351 WHERE c.id IN ('. $courseselect.') ORDER BY c.sortorder'; 352 $list = $DB->get_records_sql($sql, ['contextcourse' => CONTEXT_COURSE, 'feedbackid' => $this->get_feedback()->id]); 353 354 $this->allcourses = array(); 355 foreach ($list as $course) { 356 context_helper::preload_from_record($course); 357 if (!$course->visible && 358 !has_capability('moodle/course:viewhiddencourses', context_course::instance($course->id), $this->userid)) { 359 // Do not return courses that current user can not see. 360 continue; 361 } 362 $label = get_course_display_name_for_list($course); 363 $this->allcourses[$course->id] = $label; 364 } 365 return $this->allcourses; 366 } 367 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body