Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
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 all content fields which have files in them. 241 $contentssql = " 242 SELECT con.* 243 FROM {data_content} con 244 JOIN {files} fil 245 ON fil.component = :component 246 AND fil.filearea = :filearea 247 AND fil.itemid = con.id 248 WHERE con.recordid = :recordid 249 "; 250 $contents = $DB->get_recordset_sql($contentssql, [ 251 'recordid' => $entryid, 252 'component' => 'mod_data', 253 'filearea' => 'content', 254 ]); 255 foreach ($contents as $content) { 256 // Get the files and attach them. 257 $fs = get_file_storage(); 258 $files = $fs->get_area_files($context->id, 'mod_data', 'content', $content->id, 'filename', false); 259 foreach ($files as $file) { 260 $doc->add_stored_file($file); 261 } 262 } 263 $contents->close(); 264 } 265 266 /** 267 * Get database entry data 268 * 269 * @throws \dml_exception 270 * @param int $entryid 271 * @return stdClass 272 */ 273 protected function get_entry($entryid) { 274 global $DB; 275 276 if (empty($this->entriesdata[$entryid])) { 277 $this->entriesdata[$entryid] = $DB->get_record('data_records', array( 'id' => $entryid ), '*', MUST_EXIST); 278 } 279 280 return $this->entriesdata[$entryid]; 281 } 282 283 /** 284 * get_fields_for_entries 285 * 286 * @param StdClass $entry 287 * @return array 288 */ 289 protected function get_fields_for_entries($entry) { 290 global $DB; 291 292 $indexfields = array(); 293 $validfieldtypes = array('text', 'textarea', 'menu', 'radiobutton', 'checkbox', 'multimenu', 'url'); 294 295 $sql = "SELECT dc.*, df.name AS fldname, 296 df.type AS fieldtype, df.required 297 FROM {data_content} dc, {data_fields} df 298 WHERE dc.fieldid = df.id 299 AND dc.recordid = :recordid"; 300 301 $contents = $DB->get_records_sql($sql, array('recordid' => $entry->id)); 302 $filteredcontents = array(); 303 304 $template = $DB->get_record_sql('SELECT addtemplate FROM {data} WHERE id = ?', array($entry->dataid)); 305 $template = $template->addtemplate; 306 307 // Filtering out the data_content records having invalid fieldtypes. 308 foreach ($contents as $content) { 309 if (in_array($content->fieldtype, $validfieldtypes)) { 310 $filteredcontents[] = $content; 311 } 312 } 313 314 foreach ($filteredcontents as $content) { 315 $classname = $this->get_field_class_name($content->fieldtype); 316 if (!$classname) { 317 $content->addtemplateposition = -1; 318 continue; 319 } 320 $content->priority = $classname::get_priority(); 321 $content->addtemplateposition = strpos($template, '[['.$content->fldname.']]'); 322 } 323 324 $orderqueue = new \SPLPriorityQueue(); 325 326 // Filtering out contents which belong to fields that aren't present in the addtemplate of the database activity instance. 327 foreach ($filteredcontents as $content) { 328 329 if ($content->addtemplateposition >= 0) { 330 $orderqueue->insert($content, $content->addtemplateposition); 331 } 332 } 333 334 $filteredcontents = array(); 335 336 while ($orderqueue->valid()) { 337 $filteredcontents[] = $orderqueue->extract(); 338 } 339 340 // SPLPriorityQueue sorts according to descending order of the priority (here, addtemplateposition). 341 $filteredcontents = array_reverse($filteredcontents); 342 343 // Using a CUSTOM SPLPriorityQueure instance to sort out the filtered contents according to these rules : 344 // 1. Priorities in $fieldtypepriorities 345 // 2. Compulsory fieldtypes are to be given the top priority. 346 $contentqueue = new sortedcontentqueue($filteredcontents); 347 348 foreach ($filteredcontents as $key => $content) { 349 $contentqueue->insert($content, $key); 350 } 351 352 while ($contentqueue->valid()) { 353 354 $content = $contentqueue->extract(); 355 $classname = $this->get_field_class_name($content->fieldtype); 356 $indexfields[] = $classname::get_content_value($content); 357 } 358 359 // Limited to 4 fields as a document only has 4 content fields. 360 if (count($indexfields) > 4) { 361 $indexfields[3] = implode(' ', array_slice($indexfields, 3)); 362 } 363 return $indexfields; 364 } 365 366 /** 367 * Returns the class name for the given field type and includes it. 368 * 369 * @param string $fieldtype 370 * @return string|null It will return the class name or null if the field type is not available. 371 */ 372 protected function get_field_class_name($fieldtype) { 373 global $CFG; 374 375 $fieldtype = trim($fieldtype); 376 377 $fieldpath = $CFG->dirroot . '/mod/data/field/' . $fieldtype . '/field.class.php'; 378 if (!file_exists($fieldpath)) { 379 return null; 380 } 381 382 require_once($fieldpath); 383 return 'data_field_' . $fieldtype; 384 } 385 386 /** 387 * Confirms that data entries support group restrictions. 388 * 389 * @return bool True 390 */ 391 public function supports_group_restriction() { 392 return true; 393 } 394 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body