Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 * Search area for mod_data activity entries. 19 * 20 * @package mod_data 21 * @copyright 2016 Devang Gaur 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace mod_data\search; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 require_once($CFG->dirroot . '/mod/data/lib.php'); 30 require_once($CFG->dirroot . '/lib/grouplib.php'); 31 32 /** 33 * Search area for mod_data activity entries. 34 * 35 * @package mod_data 36 * @copyright 2016 Devang Gaur 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 class entry extends \core_search\base_mod { 40 41 /** 42 * @var array Internal quick static cache. 43 */ 44 protected $entriesdata = array(); 45 46 /** 47 * Returns recordset containing required data for indexing database entries. 48 * 49 * @param int $modifiedfrom timestamp 50 * @param \context|null $context Optional context to restrict scope of returned results 51 * @return moodle_recordset|null Recordset (or null if no results) 52 */ 53 public function get_document_recordset($modifiedfrom = 0, \context $context = null) { 54 global $DB; 55 56 list ($contextjoin, $contextparams) = $this->get_context_restriction_sql( 57 $context, 'data', 'd', SQL_PARAMS_NAMED); 58 if ($contextjoin === null) { 59 return null; 60 } 61 62 $sql = "SELECT dr.*, d.course 63 FROM {data_records} dr 64 JOIN {data} d ON d.id = dr.dataid 65 $contextjoin 66 WHERE dr.timemodified >= :timemodified"; 67 return $DB->get_recordset_sql($sql, 68 array_merge($contextparams, ['timemodified' => $modifiedfrom])); 69 } 70 71 /** 72 * Returns the documents associated with this glossary entry id. 73 * 74 * @param stdClass $entry glossary entry. 75 * @param array $options 76 * @return \core_search\document 77 */ 78 public function get_document($entry, $options = array()) { 79 try { 80 $cm = $this->get_cm('data', $entry->dataid, $entry->course); 81 $context = \context_module::instance($cm->id); 82 } catch (\dml_missing_record_exception $ex) { 83 // Notify it as we run here as admin, we should see everything. 84 debugging('Error retrieving mod_data ' . $entry->id . ' document, not all required data is available: ' . 85 $ex->getMessage(), DEBUG_DEVELOPER); 86 return false; 87 } catch (\dml_exception $ex) { 88 // Notify it as we run here as admin, we should see everything. 89 debugging('Error retrieving mod_data' . $entry->id . ' document: ' . $ex->getMessage(), DEBUG_DEVELOPER); 90 return false; 91 } 92 93 // Prepare associative array with data from DB. 94 $doc = \core_search\document_factory::instance($entry->id, $this->componentname, $this->areaname); 95 $doc->set('contextid', $context->id); 96 $doc->set('courseid', $entry->course); 97 $doc->set('userid', $entry->userid); 98 if ($entry->groupid > 0) { 99 $doc->set('groupid', $entry->groupid); 100 } 101 $doc->set('owneruserid', \core_search\manager::NO_OWNER_ID); 102 $doc->set('modified', $entry->timemodified); 103 104 $indexfields = $this->get_fields_for_entries($entry); 105 106 if (count($indexfields) < 2) { 107 return false; 108 } 109 110 // All fields should be already returned as plain text by data_field_base::get_content_value. 111 $doc->set('title', $indexfields[0]); 112 $doc->set('content', $indexfields[1]); 113 114 if (isset($indexfields[2])) { 115 $doc->set('description1', $indexfields[2]); 116 } 117 118 if (isset($indexfields[3])) { 119 $doc->set('description2', $indexfields[3]); 120 } 121 122 return $doc; 123 } 124 125 /** 126 * Whether the user can access the document or not. 127 * 128 * @throws \dml_missing_record_exception 129 * @throws \dml_exception 130 * @param int $id Glossary entry id 131 * @return bool 132 */ 133 public function check_access($id) { 134 global $DB, $USER; 135 136 if (isguestuser()) { 137 return \core_search\manager::ACCESS_DENIED; 138 } 139 140 $now = time(); 141 142 $sql = "SELECT dr.*, d.* 143 FROM {data_records} dr 144 JOIN {data} d ON d.id = dr.dataid 145 WHERE dr.id = ?"; 146 147 $entry = $DB->get_record_sql($sql, array( $id ), IGNORE_MISSING); 148 149 if (!$entry) { 150 return \core_search\manager::ACCESS_DELETED; 151 } 152 153 if (($entry->timeviewfrom && $now < $entry->timeviewfrom) || ($entry->timeviewto && $now > $entry->timeviewto)) { 154 return \core_search\manager::ACCESS_DENIED; 155 } 156 157 $cm = $this->get_cm('data', $entry->dataid, $entry->course); 158 $context = \context_module::instance($cm->id); 159 160 $canmanageentries = has_capability('mod/data:manageentries', $context); 161 162 if (!has_capability('mod/data:viewentry', $context)) { 163 return \core_search\manager::ACCESS_DENIED; 164 } 165 166 $numberofentriesindb = $DB->count_records('data_records', array('dataid' => $entry->dataid)); 167 $requiredentriestoview = $entry->requiredentriestoview; 168 169 if ($requiredentriestoview && ($requiredentriestoview > $numberofentriesindb) && 170 ($USER->id != $entry->userid) && !$canmanageentries) { 171 return \core_search\manager::ACCESS_DENIED; 172 } 173 174 if ($entry->approval && !$entry->approved && ($entry->userid != $USER->id) && !$canmanageentries) { 175 return \core_search\manager::ACCESS_DENIED; 176 } 177 178 $currentgroup = groups_get_activity_group($cm, true); 179 $groupmode = groups_get_activity_groupmode($cm); 180 181 if (($groupmode == 1) && ($entry->groupid != $currentgroup) && !$canmanageentries) { 182 return \core_search\manager::ACCESS_DENIED; 183 } 184 185 return \core_search\manager::ACCESS_GRANTED; 186 } 187 188 /** 189 * Link to database entry. 190 * 191 * @param \core_search\document $doc 192 * @return \moodle_url 193 */ 194 public function get_doc_url(\core_search\document $doc) { 195 $entry = $this->get_entry($doc->get('itemid')); 196 return new \moodle_url('/mod/data/view.php', array( 'd' => $entry->dataid, 'rid' => $entry->id )); 197 } 198 199 /** 200 * Link to the database activity. 201 * 202 * @param \core_search\document $doc 203 * @return \moodle_url 204 */ 205 public function get_context_url(\core_search\document $doc) { 206 $entry = $this->get_entry($doc->get('itemid')); 207 return new \moodle_url('/mod/data/view.php', array('d' => $entry->dataid)); 208 } 209 210 /** 211 * Returns true if this area uses file indexing. 212 * 213 * @return bool 214 */ 215 public function uses_file_indexing() { 216 return true; 217 } 218 219 /** 220 * Add the database entries attachments. 221 * 222 * @param \core_search\document $doc 223 * @return void 224 */ 225 public function attach_files($doc) { 226 global $DB; 227 228 $entryid = $doc->get('itemid'); 229 230 try { 231 $entry = $this->get_entry($entryid); 232 } catch (\dml_missing_record_exception $e) { 233 debugging('Could not get record to attach files to '.$doc->get('id'), DEBUG_DEVELOPER); 234 return; 235 } 236 237 $cm = $this->get_cm('data', $entry->dataid, $doc->get('courseid')); 238 $context = \context_module::instance($cm->id); 239 240 // Get the files and attach them. 241 $fs = get_file_storage(); 242 $files = $fs->get_area_files($context->id, 'mod_data', 'content', $entryid, 'filename', false); 243 foreach ($files as $file) { 244 $doc->add_stored_file($file); 245 } 246 } 247 248 /** 249 * Get database entry data 250 * 251 * @throws \dml_exception 252 * @param int $entryid 253 * @return stdClass 254 */ 255 protected function get_entry($entryid) { 256 global $DB; 257 258 if (empty($this->entriesdata[$entryid])) { 259 $this->entriesdata[$entryid] = $DB->get_record('data_records', array( 'id' => $entryid ), '*', MUST_EXIST); 260 } 261 262 return $this->entriesdata[$entryid]; 263 } 264 265 /** 266 * get_fields_for_entries 267 * 268 * @param StdClass $entry 269 * @return array 270 */ 271 protected function get_fields_for_entries($entry) { 272 global $DB; 273 274 $indexfields = array(); 275 $validfieldtypes = array('text', 'textarea', 'menu', 'radiobutton', 'checkbox', 'multimenu', 'url'); 276 277 $sql = "SELECT dc.*, df.name AS fldname, 278 df.type AS fieldtype, df.required 279 FROM {data_content} dc, {data_fields} df 280 WHERE dc.fieldid = df.id 281 AND dc.recordid = :recordid"; 282 283 $contents = $DB->get_records_sql($sql, array('recordid' => $entry->id)); 284 $filteredcontents = array(); 285 286 $template = $DB->get_record_sql('SELECT addtemplate FROM {data} WHERE id = ?', array($entry->dataid)); 287 $template = $template->addtemplate; 288 289 // Filtering out the data_content records having invalid fieldtypes. 290 foreach ($contents as $content) { 291 if (in_array($content->fieldtype, $validfieldtypes)) { 292 $filteredcontents[] = $content; 293 } 294 } 295 296 foreach ($filteredcontents as $content) { 297 $classname = $this->get_field_class_name($content->fieldtype); 298 if (!$classname) { 299 $content->addtemplateposition = -1; 300 continue; 301 } 302 $content->priority = $classname::get_priority(); 303 $content->addtemplateposition = strpos($template, '[['.$content->fldname.']]'); 304 } 305 306 $orderqueue = new \SPLPriorityQueue(); 307 308 // Filtering out contents which belong to fields that aren't present in the addtemplate of the database activity instance. 309 foreach ($filteredcontents as $content) { 310 311 if ($content->addtemplateposition >= 0) { 312 $orderqueue->insert($content, $content->addtemplateposition); 313 } 314 } 315 316 $filteredcontents = array(); 317 318 while ($orderqueue->valid()) { 319 $filteredcontents[] = $orderqueue->extract(); 320 } 321 322 // SPLPriorityQueue sorts according to descending order of the priority (here, addtemplateposition). 323 $filteredcontents = array_reverse($filteredcontents); 324 325 // Using a CUSTOM SPLPriorityQueure instance to sort out the filtered contents according to these rules : 326 // 1. Priorities in $fieldtypepriorities 327 // 2. Compulsory fieldtypes are to be given the top priority. 328 $contentqueue = new sortedcontentqueue($filteredcontents); 329 330 foreach ($filteredcontents as $key => $content) { 331 $contentqueue->insert($content, $key); 332 } 333 334 while ($contentqueue->valid()) { 335 336 $content = $contentqueue->extract(); 337 $classname = $this->get_field_class_name($content->fieldtype); 338 $indexfields[] = $classname::get_content_value($content); 339 } 340 341 // Limited to 4 fields as a document only has 4 content fields. 342 if (count($indexfields) > 4) { 343 $indexfields[3] = implode(' ', array_slice($indexfields, 3)); 344 } 345 return $indexfields; 346 } 347 348 /** 349 * Returns the class name for the given field type and includes it. 350 * 351 * @param string $fieldtype 352 * @return string|null It will return the class name or null if the field type is not available. 353 */ 354 protected function get_field_class_name($fieldtype) { 355 global $CFG; 356 357 $fieldtype = trim($fieldtype); 358 359 $fieldpath = $CFG->dirroot . '/mod/data/field/' . $fieldtype . '/field.class.php'; 360 if (!file_exists($fieldpath)) { 361 return null; 362 } 363 364 require_once($fieldpath); 365 return 'data_field_' . $fieldtype; 366 } 367 368 /** 369 * Confirms that data entries support group restrictions. 370 * 371 * @return bool True 372 */ 373 public function supports_group_restriction() { 374 return true; 375 } 376 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body