1 |
efrain |
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 |
* @package mod_data
|
|
|
19 |
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
20 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
21 |
*/
|
|
|
22 |
|
|
|
23 |
use mod_data\manager;
|
|
|
24 |
use mod_data\preset;
|
|
|
25 |
|
|
|
26 |
defined('MOODLE_INTERNAL') || die();
|
|
|
27 |
|
|
|
28 |
// Some constants
|
|
|
29 |
define ('DATA_MAX_ENTRIES', 50);
|
|
|
30 |
define ('DATA_PERPAGE_SINGLE', 1);
|
|
|
31 |
|
|
|
32 |
define ('DATA_FIRSTNAME', -1);
|
|
|
33 |
define ('DATA_LASTNAME', -2);
|
|
|
34 |
define ('DATA_APPROVED', -3);
|
|
|
35 |
define ('DATA_TIMEADDED', 0);
|
|
|
36 |
define ('DATA_TIMEMODIFIED', -4);
|
|
|
37 |
define ('DATA_TAGS', -5);
|
|
|
38 |
|
|
|
39 |
define ('DATA_CAP_EXPORT', 'mod/data:viewalluserpresets');
|
|
|
40 |
// Users having assigned the default role "Non-editing teacher" can export database records
|
|
|
41 |
// Using the mod/data capability "viewalluserpresets" existing in Moodle 1.9.x.
|
|
|
42 |
// In Moodle >= 2, new roles may be introduced and used instead.
|
|
|
43 |
|
|
|
44 |
define('DATA_PRESET_COMPONENT', 'mod_data');
|
|
|
45 |
define('DATA_PRESET_FILEAREA', 'site_presets');
|
|
|
46 |
define('DATA_PRESET_CONTEXT', SYSCONTEXTID);
|
|
|
47 |
|
|
|
48 |
define('DATA_EVENT_TYPE_OPEN', 'open');
|
|
|
49 |
define('DATA_EVENT_TYPE_CLOSE', 'close');
|
|
|
50 |
|
|
|
51 |
require_once(__DIR__ . '/deprecatedlib.php');
|
|
|
52 |
|
|
|
53 |
/**
|
|
|
54 |
* @package mod_data
|
|
|
55 |
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
56 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
57 |
*/
|
|
|
58 |
class data_field_base { // Base class for Database Field Types (see field/*/field.class.php)
|
|
|
59 |
|
|
|
60 |
/** @var string Subclasses must override the type with their name */
|
|
|
61 |
var $type = 'unknown';
|
|
|
62 |
/** @var object The database object that this field belongs to */
|
|
|
63 |
var $data = NULL;
|
|
|
64 |
/** @var object The field object itself, if we know it */
|
|
|
65 |
var $field = NULL;
|
|
|
66 |
/** @var int Width of the icon for this fieldtype */
|
|
|
67 |
var $iconwidth = 16;
|
|
|
68 |
/** @var int Width of the icon for this fieldtype */
|
|
|
69 |
var $iconheight = 16;
|
|
|
70 |
/** @var object course module or cmifno */
|
|
|
71 |
var $cm;
|
|
|
72 |
/** @var object activity context */
|
|
|
73 |
var $context;
|
|
|
74 |
/** @var priority for globalsearch indexing */
|
|
|
75 |
protected static $priority = self::NO_PRIORITY;
|
|
|
76 |
/** priority value for invalid fields regarding indexing */
|
|
|
77 |
const NO_PRIORITY = 0;
|
|
|
78 |
/** priority value for minimum priority */
|
|
|
79 |
const MIN_PRIORITY = 1;
|
|
|
80 |
/** priority value for low priority */
|
|
|
81 |
const LOW_PRIORITY = 2;
|
|
|
82 |
/** priority value for high priority */
|
|
|
83 |
const HIGH_PRIORITY = 3;
|
|
|
84 |
/** priority value for maximum priority */
|
|
|
85 |
const MAX_PRIORITY = 4;
|
|
|
86 |
|
|
|
87 |
/** @var bool whether the field is used in preview mode. */
|
|
|
88 |
protected $preview = false;
|
|
|
89 |
|
|
|
90 |
/**
|
|
|
91 |
* Constructor function
|
|
|
92 |
*
|
|
|
93 |
* @global object
|
|
|
94 |
* @uses CONTEXT_MODULE
|
|
|
95 |
* @param int $field
|
|
|
96 |
* @param int $data
|
|
|
97 |
* @param int $cm
|
|
|
98 |
*/
|
|
|
99 |
function __construct($field=0, $data=0, $cm=0) { // Field or data or both, each can be id or object
|
|
|
100 |
global $DB;
|
|
|
101 |
|
|
|
102 |
if (empty($field) && empty($data)) {
|
|
|
103 |
throw new \moodle_exception('missingfield', 'data');
|
|
|
104 |
}
|
|
|
105 |
|
|
|
106 |
if (!empty($field)) {
|
|
|
107 |
if (is_object($field)) {
|
|
|
108 |
$this->field = $field; // Programmer knows what they are doing, we hope
|
|
|
109 |
} else if (!$this->field = $DB->get_record('data_fields', array('id'=>$field))) {
|
|
|
110 |
throw new \moodle_exception('invalidfieldid', 'data');
|
|
|
111 |
}
|
|
|
112 |
if (empty($data)) {
|
|
|
113 |
if (!$this->data = $DB->get_record('data', array('id'=>$this->field->dataid))) {
|
|
|
114 |
throw new \moodle_exception('invalidid', 'data');
|
|
|
115 |
}
|
|
|
116 |
}
|
|
|
117 |
}
|
|
|
118 |
|
|
|
119 |
if (empty($this->data)) { // We need to define this properly
|
|
|
120 |
if (!empty($data)) {
|
|
|
121 |
if (is_object($data)) {
|
|
|
122 |
$this->data = $data; // Programmer knows what they are doing, we hope
|
|
|
123 |
} else if (!$this->data = $DB->get_record('data', array('id'=>$data))) {
|
|
|
124 |
throw new \moodle_exception('invalidid', 'data');
|
|
|
125 |
}
|
|
|
126 |
} else { // No way to define it!
|
|
|
127 |
throw new \moodle_exception('missingdata', 'data');
|
|
|
128 |
}
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
if ($cm) {
|
|
|
132 |
$this->cm = $cm;
|
|
|
133 |
} else {
|
|
|
134 |
$this->cm = get_coursemodule_from_instance('data', $this->data->id);
|
|
|
135 |
}
|
|
|
136 |
|
|
|
137 |
if (empty($this->field)) { // We need to define some default values
|
|
|
138 |
$this->define_default_field();
|
|
|
139 |
}
|
|
|
140 |
|
|
|
141 |
$this->context = context_module::instance($this->cm->id);
|
|
|
142 |
}
|
|
|
143 |
|
|
|
144 |
/**
|
|
|
145 |
* Return the field type name.
|
|
|
146 |
*
|
|
|
147 |
* @return string the filed type.
|
|
|
148 |
*/
|
|
|
149 |
public function get_name(): string {
|
|
|
150 |
return $this->field->name;
|
|
|
151 |
}
|
|
|
152 |
|
|
|
153 |
/**
|
|
|
154 |
* Return if the field type supports preview.
|
|
|
155 |
*
|
|
|
156 |
* Fields without a preview cannot be displayed in the preset preview.
|
|
|
157 |
*
|
|
|
158 |
* @return bool if the plugin supports preview.
|
|
|
159 |
*/
|
|
|
160 |
public function supports_preview(): bool {
|
|
|
161 |
return false;
|
|
|
162 |
}
|
|
|
163 |
|
|
|
164 |
/**
|
|
|
165 |
* Generate a fake data_content for this field to be used in preset previews.
|
|
|
166 |
*
|
|
|
167 |
* Data plugins must override this method and support_preview in order to enable
|
|
|
168 |
* preset preview for this field.
|
|
|
169 |
*
|
|
|
170 |
* @param int $recordid the fake record id
|
|
|
171 |
* @return stdClass the fake record
|
|
|
172 |
*/
|
|
|
173 |
public function get_data_content_preview(int $recordid): stdClass {
|
|
|
174 |
$message = get_string('nopreviewavailable', 'mod_data', $this->field->name);
|
|
|
175 |
return (object)[
|
|
|
176 |
'id' => 0,
|
|
|
177 |
'fieldid' => $this->field->id,
|
|
|
178 |
'recordid' => $recordid,
|
|
|
179 |
'content' => "<span class=\"nopreview\">$message</span>",
|
|
|
180 |
'content1' => null,
|
|
|
181 |
'content2' => null,
|
|
|
182 |
'content3' => null,
|
|
|
183 |
'content4' => null,
|
|
|
184 |
];
|
|
|
185 |
}
|
|
|
186 |
|
|
|
187 |
/**
|
|
|
188 |
* Set the field to preview mode.
|
|
|
189 |
*
|
|
|
190 |
* @param bool $preview the new preview value
|
|
|
191 |
*/
|
|
|
192 |
public function set_preview(bool $preview) {
|
|
|
193 |
$this->preview = $preview;
|
|
|
194 |
}
|
|
|
195 |
|
|
|
196 |
/**
|
|
|
197 |
* Get the field preview value.
|
|
|
198 |
*
|
|
|
199 |
* @return bool
|
|
|
200 |
*/
|
|
|
201 |
public function get_preview(): bool {
|
|
|
202 |
return $this->preview;
|
|
|
203 |
}
|
|
|
204 |
|
|
|
205 |
|
|
|
206 |
/**
|
|
|
207 |
* This field just sets up a default field object
|
|
|
208 |
*
|
|
|
209 |
* @return bool
|
|
|
210 |
*/
|
|
|
211 |
function define_default_field() {
|
|
|
212 |
global $OUTPUT;
|
|
|
213 |
if (empty($this->data->id)) {
|
|
|
214 |
echo $OUTPUT->notification('Programmer error: dataid not defined in field class');
|
|
|
215 |
}
|
|
|
216 |
$this->field = new stdClass();
|
|
|
217 |
$this->field->id = 0;
|
|
|
218 |
$this->field->dataid = $this->data->id;
|
|
|
219 |
$this->field->type = $this->type;
|
|
|
220 |
$this->field->param1 = '';
|
|
|
221 |
$this->field->param2 = '';
|
|
|
222 |
$this->field->param3 = '';
|
|
|
223 |
$this->field->name = '';
|
|
|
224 |
$this->field->description = '';
|
|
|
225 |
$this->field->required = false;
|
|
|
226 |
|
|
|
227 |
return true;
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
/**
|
|
|
231 |
* Set up the field object according to data in an object. Now is the time to clean it!
|
|
|
232 |
*
|
|
|
233 |
* @return bool
|
|
|
234 |
*/
|
|
|
235 |
function define_field($data) {
|
|
|
236 |
$this->field->type = $this->type;
|
|
|
237 |
$this->field->dataid = $this->data->id;
|
|
|
238 |
|
|
|
239 |
$this->field->name = trim($data->name);
|
|
|
240 |
$this->field->description = trim($data->description);
|
|
|
241 |
$this->field->required = !empty($data->required) ? 1 : 0;
|
|
|
242 |
|
|
|
243 |
if (isset($data->param1)) {
|
|
|
244 |
$this->field->param1 = trim($data->param1);
|
|
|
245 |
}
|
|
|
246 |
if (isset($data->param2)) {
|
|
|
247 |
$this->field->param2 = trim($data->param2);
|
|
|
248 |
}
|
|
|
249 |
if (isset($data->param3)) {
|
|
|
250 |
$this->field->param3 = trim($data->param3);
|
|
|
251 |
}
|
|
|
252 |
if (isset($data->param4)) {
|
|
|
253 |
$this->field->param4 = trim($data->param4);
|
|
|
254 |
}
|
|
|
255 |
if (isset($data->param5)) {
|
|
|
256 |
$this->field->param5 = trim($data->param5);
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
return true;
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
/**
|
|
|
263 |
* Insert a new field in the database
|
|
|
264 |
* We assume the field object is already defined as $this->field
|
|
|
265 |
*
|
|
|
266 |
* @global object
|
|
|
267 |
* @return bool
|
|
|
268 |
*/
|
|
|
269 |
function insert_field() {
|
|
|
270 |
global $DB, $OUTPUT;
|
|
|
271 |
|
|
|
272 |
if (empty($this->field)) {
|
|
|
273 |
echo $OUTPUT->notification('Programmer error: Field has not been defined yet! See define_field()');
|
|
|
274 |
return false;
|
|
|
275 |
}
|
|
|
276 |
|
|
|
277 |
$this->field->id = $DB->insert_record('data_fields',$this->field);
|
|
|
278 |
|
|
|
279 |
// Trigger an event for creating this field.
|
|
|
280 |
$event = \mod_data\event\field_created::create(array(
|
|
|
281 |
'objectid' => $this->field->id,
|
|
|
282 |
'context' => $this->context,
|
|
|
283 |
'other' => array(
|
|
|
284 |
'fieldname' => $this->field->name,
|
|
|
285 |
'dataid' => $this->data->id
|
|
|
286 |
)
|
|
|
287 |
));
|
|
|
288 |
$event->trigger();
|
|
|
289 |
|
|
|
290 |
return true;
|
|
|
291 |
}
|
|
|
292 |
|
|
|
293 |
|
|
|
294 |
/**
|
|
|
295 |
* Update a field in the database
|
|
|
296 |
*
|
|
|
297 |
* @global object
|
|
|
298 |
* @return bool
|
|
|
299 |
*/
|
|
|
300 |
function update_field() {
|
|
|
301 |
global $DB;
|
|
|
302 |
|
|
|
303 |
$DB->update_record('data_fields', $this->field);
|
|
|
304 |
|
|
|
305 |
// Trigger an event for updating this field.
|
|
|
306 |
$event = \mod_data\event\field_updated::create(array(
|
|
|
307 |
'objectid' => $this->field->id,
|
|
|
308 |
'context' => $this->context,
|
|
|
309 |
'other' => array(
|
|
|
310 |
'fieldname' => $this->field->name,
|
|
|
311 |
'dataid' => $this->data->id
|
|
|
312 |
)
|
|
|
313 |
));
|
|
|
314 |
$event->trigger();
|
|
|
315 |
|
|
|
316 |
return true;
|
|
|
317 |
}
|
|
|
318 |
|
|
|
319 |
/**
|
|
|
320 |
* Delete a field completely
|
|
|
321 |
*
|
|
|
322 |
* @global object
|
|
|
323 |
* @return bool
|
|
|
324 |
*/
|
|
|
325 |
function delete_field() {
|
|
|
326 |
global $DB;
|
|
|
327 |
|
|
|
328 |
if (!empty($this->field->id)) {
|
|
|
329 |
$manager = manager::create_from_instance($this->data);
|
|
|
330 |
|
|
|
331 |
// Get the field before we delete it.
|
|
|
332 |
$field = $DB->get_record('data_fields', array('id' => $this->field->id));
|
|
|
333 |
|
|
|
334 |
$this->delete_content();
|
|
|
335 |
$DB->delete_records('data_fields', array('id'=>$this->field->id));
|
|
|
336 |
|
|
|
337 |
// Trigger an event for deleting this field.
|
|
|
338 |
$event = \mod_data\event\field_deleted::create(array(
|
|
|
339 |
'objectid' => $this->field->id,
|
|
|
340 |
'context' => $this->context,
|
|
|
341 |
'other' => array(
|
|
|
342 |
'fieldname' => $this->field->name,
|
|
|
343 |
'dataid' => $this->data->id
|
|
|
344 |
)
|
|
|
345 |
));
|
|
|
346 |
|
|
|
347 |
if (!$manager->has_fields() && $manager->has_records()) {
|
|
|
348 |
$DB->delete_records('data_records', ['dataid' => $this->data->id]);
|
|
|
349 |
}
|
|
|
350 |
|
|
|
351 |
$event->add_record_snapshot('data_fields', $field);
|
|
|
352 |
$event->trigger();
|
|
|
353 |
}
|
|
|
354 |
|
|
|
355 |
return true;
|
|
|
356 |
}
|
|
|
357 |
|
|
|
358 |
/**
|
|
|
359 |
* Print the relevant form element in the ADD template for this field
|
|
|
360 |
*
|
|
|
361 |
* @global object
|
|
|
362 |
* @param int $recordid
|
|
|
363 |
* @return string
|
|
|
364 |
*/
|
|
|
365 |
function display_add_field($recordid=0, $formdata=null) {
|
|
|
366 |
global $DB, $OUTPUT;
|
|
|
367 |
|
|
|
368 |
if ($formdata) {
|
|
|
369 |
$fieldname = 'field_' . $this->field->id;
|
|
|
370 |
$content = $formdata->$fieldname;
|
|
|
371 |
} else if ($recordid) {
|
|
|
372 |
$content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid));
|
|
|
373 |
} else {
|
|
|
374 |
$content = '';
|
|
|
375 |
}
|
|
|
376 |
|
|
|
377 |
// beware get_field returns false for new, empty records MDL-18567
|
|
|
378 |
if ($content===false) {
|
|
|
379 |
$content='';
|
|
|
380 |
}
|
|
|
381 |
|
|
|
382 |
$str = '<div title="' . s($this->field->description) . '">';
|
|
|
383 |
$str .= '<label for="field_'.$this->field->id.'"><span class="accesshide">'.s($this->field->name).'</span>';
|
|
|
384 |
if ($this->field->required) {
|
|
|
385 |
$image = $OUTPUT->pix_icon('req', get_string('requiredelement', 'form'));
|
|
|
386 |
$str .= html_writer::div($image, 'inline-req');
|
|
|
387 |
}
|
|
|
388 |
$str .= '</label><input class="basefieldinput form-control d-inline mod-data-input" ' .
|
|
|
389 |
'type="text" name="field_' . $this->field->id . '" ' .
|
|
|
390 |
'id="field_' . $this->field->id . '" value="' . s($content) . '" />';
|
|
|
391 |
$str .= '</div>';
|
|
|
392 |
|
|
|
393 |
return $str;
|
|
|
394 |
}
|
|
|
395 |
|
|
|
396 |
/**
|
|
|
397 |
* Print the relevant form element to define the attributes for this field
|
|
|
398 |
* viewable by teachers only.
|
|
|
399 |
*
|
|
|
400 |
* @global object
|
|
|
401 |
* @global object
|
|
|
402 |
* @return void Output is echo'd
|
|
|
403 |
*/
|
|
|
404 |
function display_edit_field() {
|
|
|
405 |
global $CFG, $DB, $OUTPUT;
|
|
|
406 |
|
|
|
407 |
if (empty($this->field)) { // No field has been defined yet, try and make one
|
|
|
408 |
$this->define_default_field();
|
|
|
409 |
}
|
|
|
410 |
|
|
|
411 |
// Throw an exception if field type doen't exist. Anyway user should never access to edit a field with an unknown fieldtype.
|
|
|
412 |
if ($this->type === 'unknown') {
|
|
|
413 |
throw new \moodle_exception(get_string('missingfieldtype', 'data', (object)['name' => $this->field->name]));
|
|
|
414 |
}
|
|
|
415 |
|
|
|
416 |
echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
|
|
|
417 |
|
|
|
418 |
echo '<form id="editfield" action="'.$CFG->wwwroot.'/mod/data/field.php" method="post">'."\n";
|
|
|
419 |
echo '<input type="hidden" name="d" value="'.$this->data->id.'" />'."\n";
|
|
|
420 |
if (empty($this->field->id)) {
|
|
|
421 |
echo '<input type="hidden" name="mode" value="add" />'."\n";
|
|
|
422 |
} else {
|
|
|
423 |
echo '<input type="hidden" name="fid" value="'.$this->field->id.'" />'."\n";
|
|
|
424 |
echo '<input type="hidden" name="mode" value="update" />'."\n";
|
|
|
425 |
}
|
|
|
426 |
echo '<input type="hidden" name="type" value="'.$this->type.'" />'."\n";
|
|
|
427 |
echo '<input name="sesskey" value="'.sesskey().'" type="hidden" />'."\n";
|
|
|
428 |
|
|
|
429 |
echo $OUTPUT->heading($this->name(), 3);
|
|
|
430 |
|
|
|
431 |
$filepath = $CFG->dirroot . '/mod/data/field/' . $this->type . '/mod.html';
|
|
|
432 |
$templatename = 'datafield_' . $this->type . '/' . $this->type;
|
|
|
433 |
|
|
|
434 |
try {
|
|
|
435 |
$templatefilepath = \core\output\mustache_template_finder::get_template_filepath($templatename);
|
|
|
436 |
$templatefileexists = true;
|
|
|
437 |
} catch (moodle_exception $e) {
|
|
|
438 |
if (!file_exists($filepath)) {
|
|
|
439 |
// Neither file exists.
|
|
|
440 |
throw new \moodle_exception(get_string('missingfieldtype', 'data', (object)['name' => $this->field->name]));
|
|
|
441 |
}
|
|
|
442 |
$templatefileexists = false;
|
|
|
443 |
}
|
|
|
444 |
|
|
|
445 |
if ($templatefileexists) {
|
|
|
446 |
// Give out templated Bootstrap formatted form fields.
|
|
|
447 |
$data = $this->get_field_params();
|
|
|
448 |
echo $OUTPUT->render_from_template($templatename, $data);
|
|
|
449 |
} else {
|
|
|
450 |
// Fall back to display mod.html for backward compatibility.
|
|
|
451 |
require_once($filepath);
|
|
|
452 |
}
|
|
|
453 |
|
|
|
454 |
$actionbuttons = html_writer::start_div();
|
|
|
455 |
$actionbuttons .= html_writer::tag('input', null, [
|
|
|
456 |
'type' => 'submit',
|
|
|
457 |
'name' => 'cancel',
|
|
|
458 |
'value' => get_string('cancel'),
|
|
|
459 |
'class' => 'btn btn-secondary mx-1'
|
|
|
460 |
]);
|
|
|
461 |
$actionbuttons .= html_writer::tag('input', null, [
|
|
|
462 |
'type' => 'submit',
|
|
|
463 |
'value' => get_string('save'),
|
|
|
464 |
'class' => 'btn btn-primary mx-1'
|
|
|
465 |
]);
|
|
|
466 |
$actionbuttons .= html_writer::end_div();
|
|
|
467 |
|
|
|
468 |
$stickyfooter = new core\output\sticky_footer($actionbuttons);
|
|
|
469 |
echo $OUTPUT->render($stickyfooter);
|
|
|
470 |
|
|
|
471 |
echo '</form>';
|
|
|
472 |
|
|
|
473 |
echo $OUTPUT->box_end();
|
|
|
474 |
}
|
|
|
475 |
|
|
|
476 |
/**
|
|
|
477 |
* Validates params of fieldinput data. Overwrite to validate fieldtype specific data.
|
|
|
478 |
*
|
|
|
479 |
* You are expected to return an array like ['paramname' => 'Error message for paramname param'] if there is an error,
|
|
|
480 |
* return an empty array if everything is fine.
|
|
|
481 |
*
|
|
|
482 |
* @param stdClass $fieldinput The field input data to check
|
|
|
483 |
* @return array $errors if empty validation was fine, otherwise contains one or more error messages
|
|
|
484 |
*/
|
|
|
485 |
public function validate(stdClass $fieldinput): array {
|
|
|
486 |
return [];
|
|
|
487 |
}
|
|
|
488 |
|
|
|
489 |
/**
|
|
|
490 |
* Return the data_content of the field, or generate it if it is in preview mode.
|
|
|
491 |
*
|
|
|
492 |
* @param int $recordid the record id
|
|
|
493 |
* @return stdClass|bool the record data or false if none
|
|
|
494 |
*/
|
|
|
495 |
protected function get_data_content(int $recordid) {
|
|
|
496 |
global $DB;
|
|
|
497 |
if ($this->preview) {
|
|
|
498 |
return $this->get_data_content_preview($recordid);
|
|
|
499 |
}
|
|
|
500 |
return $DB->get_record(
|
|
|
501 |
'data_content',
|
|
|
502 |
['fieldid' => $this->field->id, 'recordid' => $recordid]
|
|
|
503 |
);
|
|
|
504 |
}
|
|
|
505 |
|
|
|
506 |
/**
|
|
|
507 |
* Display the content of the field in browse mode
|
|
|
508 |
*
|
|
|
509 |
* @global object
|
|
|
510 |
* @param int $recordid
|
|
|
511 |
* @param object $template
|
|
|
512 |
* @return bool|string
|
|
|
513 |
*/
|
|
|
514 |
function display_browse_field($recordid, $template) {
|
|
|
515 |
global $DB;
|
|
|
516 |
$content = $this->get_data_content($recordid);
|
|
|
517 |
if (!$content || !isset($content->content)) {
|
|
|
518 |
return '';
|
|
|
519 |
}
|
|
|
520 |
$options = new stdClass();
|
|
|
521 |
if ($this->field->param1 == '1') {
|
|
|
522 |
// We are autolinking this field, so disable linking within us.
|
|
|
523 |
$options->filter = false;
|
|
|
524 |
}
|
|
|
525 |
$options->para = false;
|
|
|
526 |
$str = format_text($content->content, $content->content1, $options);
|
|
|
527 |
return $str;
|
|
|
528 |
}
|
|
|
529 |
|
|
|
530 |
/**
|
|
|
531 |
* Update the content of one data field in the data_content table
|
|
|
532 |
* @global object
|
|
|
533 |
* @param int $recordid
|
|
|
534 |
* @param mixed $value
|
|
|
535 |
* @param string $name
|
|
|
536 |
* @return bool
|
|
|
537 |
*/
|
|
|
538 |
function update_content($recordid, $value, $name=''){
|
|
|
539 |
global $DB;
|
|
|
540 |
|
|
|
541 |
$content = new stdClass();
|
|
|
542 |
$content->fieldid = $this->field->id;
|
|
|
543 |
$content->recordid = $recordid;
|
|
|
544 |
$content->content = clean_param($value, PARAM_NOTAGS);
|
|
|
545 |
|
|
|
546 |
if ($oldcontent = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
|
|
|
547 |
$content->id = $oldcontent->id;
|
|
|
548 |
return $DB->update_record('data_content', $content);
|
|
|
549 |
} else {
|
|
|
550 |
return $DB->insert_record('data_content', $content);
|
|
|
551 |
}
|
|
|
552 |
}
|
|
|
553 |
|
|
|
554 |
/**
|
|
|
555 |
* Delete all content associated with the field
|
|
|
556 |
*
|
|
|
557 |
* @global object
|
|
|
558 |
* @param int $recordid
|
|
|
559 |
* @return bool
|
|
|
560 |
*/
|
|
|
561 |
function delete_content($recordid=0) {
|
|
|
562 |
global $DB;
|
|
|
563 |
|
|
|
564 |
if ($recordid) {
|
|
|
565 |
$conditions = array('fieldid'=>$this->field->id, 'recordid'=>$recordid);
|
|
|
566 |
} else {
|
|
|
567 |
$conditions = array('fieldid'=>$this->field->id);
|
|
|
568 |
}
|
|
|
569 |
|
|
|
570 |
$rs = $DB->get_recordset('data_content', $conditions);
|
|
|
571 |
if ($rs->valid()) {
|
|
|
572 |
$fs = get_file_storage();
|
|
|
573 |
foreach ($rs as $content) {
|
|
|
574 |
$fs->delete_area_files($this->context->id, 'mod_data', 'content', $content->id);
|
|
|
575 |
}
|
|
|
576 |
}
|
|
|
577 |
$rs->close();
|
|
|
578 |
|
|
|
579 |
return $DB->delete_records('data_content', $conditions);
|
|
|
580 |
}
|
|
|
581 |
|
|
|
582 |
/**
|
|
|
583 |
* Check if a field from an add form is empty
|
|
|
584 |
*
|
|
|
585 |
* @param mixed $value
|
|
|
586 |
* @param mixed $name
|
|
|
587 |
* @return bool
|
|
|
588 |
*/
|
|
|
589 |
function notemptyfield($value, $name) {
|
|
|
590 |
return !empty($value);
|
|
|
591 |
}
|
|
|
592 |
|
|
|
593 |
/**
|
|
|
594 |
* Just in case a field needs to print something before the whole form
|
|
|
595 |
*/
|
|
|
596 |
function print_before_form() {
|
|
|
597 |
}
|
|
|
598 |
|
|
|
599 |
/**
|
|
|
600 |
* Just in case a field needs to print something after the whole form
|
|
|
601 |
*/
|
|
|
602 |
function print_after_form() {
|
|
|
603 |
}
|
|
|
604 |
|
|
|
605 |
|
|
|
606 |
/**
|
|
|
607 |
* Returns the sortable field for the content. By default, it's just content
|
|
|
608 |
* but for some plugins, it could be content 1 - content4
|
|
|
609 |
*
|
|
|
610 |
* @return string
|
|
|
611 |
*/
|
|
|
612 |
function get_sort_field() {
|
|
|
613 |
return 'content';
|
|
|
614 |
}
|
|
|
615 |
|
|
|
616 |
/**
|
|
|
617 |
* Returns the SQL needed to refer to the column. Some fields may need to CAST() etc.
|
|
|
618 |
*
|
|
|
619 |
* @param string $fieldname
|
|
|
620 |
* @return string $fieldname
|
|
|
621 |
*/
|
|
|
622 |
function get_sort_sql($fieldname) {
|
|
|
623 |
return $fieldname;
|
|
|
624 |
}
|
|
|
625 |
|
|
|
626 |
/**
|
|
|
627 |
* Returns the name/type of the field
|
|
|
628 |
*
|
|
|
629 |
* @return string
|
|
|
630 |
*/
|
|
|
631 |
function name() {
|
|
|
632 |
return get_string('fieldtypelabel', "datafield_$this->type");
|
|
|
633 |
}
|
|
|
634 |
|
|
|
635 |
/**
|
|
|
636 |
* Prints the respective type icon
|
|
|
637 |
*
|
|
|
638 |
* @global object
|
|
|
639 |
* @return string
|
|
|
640 |
*/
|
|
|
641 |
function image() {
|
|
|
642 |
global $OUTPUT;
|
|
|
643 |
|
|
|
644 |
return $OUTPUT->pix_icon('field/' . $this->type, $this->type, 'data');
|
|
|
645 |
}
|
|
|
646 |
|
|
|
647 |
/**
|
|
|
648 |
* Per default, it is assumed that fields support text exporting.
|
|
|
649 |
* Override this (return false) on fields not supporting text exporting.
|
|
|
650 |
*
|
|
|
651 |
* @return bool true
|
|
|
652 |
*/
|
|
|
653 |
function text_export_supported() {
|
|
|
654 |
return true;
|
|
|
655 |
}
|
|
|
656 |
|
|
|
657 |
/**
|
|
|
658 |
* Per default, it is assumed that fields do not support file exporting. Override this (return true)
|
|
|
659 |
* on fields supporting file export. You will also have to implement export_file_value().
|
|
|
660 |
*
|
|
|
661 |
* @return bool true if field will export a file, false otherwise
|
|
|
662 |
*/
|
|
|
663 |
public function file_export_supported(): bool {
|
|
|
664 |
return false;
|
|
|
665 |
}
|
|
|
666 |
|
|
|
667 |
/**
|
|
|
668 |
* Per default, does not return a file (just null).
|
|
|
669 |
* Override this in fields class, if you want your field to export a file content.
|
|
|
670 |
* In case you are exporting a file value, export_text_value() should return the corresponding file name.
|
|
|
671 |
*
|
|
|
672 |
* @param stdClass $record
|
|
|
673 |
* @return null|string the file content as string or null, if no file content is being provided
|
|
|
674 |
*/
|
|
|
675 |
public function export_file_value(stdClass $record): null|string {
|
|
|
676 |
return null;
|
|
|
677 |
}
|
|
|
678 |
|
|
|
679 |
/**
|
|
|
680 |
* Per default, a field does not support the import of files.
|
|
|
681 |
*
|
|
|
682 |
* A field type can overwrite this function and return true. In this case it also has to implement the function
|
|
|
683 |
* import_file_value().
|
|
|
684 |
*
|
|
|
685 |
* @return false means file imports are not supported
|
|
|
686 |
*/
|
|
|
687 |
public function file_import_supported(): bool {
|
|
|
688 |
return false;
|
|
|
689 |
}
|
|
|
690 |
|
|
|
691 |
/**
|
|
|
692 |
* Returns a stored_file object for exporting a file of a given record.
|
|
|
693 |
*
|
|
|
694 |
* @param int $contentid content id
|
|
|
695 |
* @param string $filecontent the content of the file as string
|
|
|
696 |
* @param string $filename the filename the file should have
|
|
|
697 |
*/
|
|
|
698 |
public function import_file_value(int $contentid, string $filecontent, string $filename): void {
|
|
|
699 |
return;
|
|
|
700 |
}
|
|
|
701 |
|
|
|
702 |
/**
|
|
|
703 |
* Per default, return the record's text value only from the "content" field.
|
|
|
704 |
* Override this in fields class if necessary.
|
|
|
705 |
*
|
|
|
706 |
* @param stdClass $record
|
|
|
707 |
* @return string
|
|
|
708 |
*/
|
|
|
709 |
public function export_text_value(stdClass $record) {
|
|
|
710 |
if ($this->text_export_supported()) {
|
|
|
711 |
return $record->content;
|
|
|
712 |
}
|
|
|
713 |
return '';
|
|
|
714 |
}
|
|
|
715 |
|
|
|
716 |
/**
|
|
|
717 |
* @param string $relativepath
|
|
|
718 |
* @return bool false
|
|
|
719 |
*/
|
|
|
720 |
function file_ok($relativepath) {
|
|
|
721 |
return false;
|
|
|
722 |
}
|
|
|
723 |
|
|
|
724 |
/**
|
|
|
725 |
* Returns the priority for being indexed by globalsearch
|
|
|
726 |
*
|
|
|
727 |
* @return int
|
|
|
728 |
*/
|
|
|
729 |
public static function get_priority() {
|
|
|
730 |
return static::$priority;
|
|
|
731 |
}
|
|
|
732 |
|
|
|
733 |
/**
|
|
|
734 |
* Returns the presentable string value for a field content.
|
|
|
735 |
*
|
|
|
736 |
* The returned string should be plain text.
|
|
|
737 |
*
|
|
|
738 |
* @param stdClass $content
|
|
|
739 |
* @return string
|
|
|
740 |
*/
|
|
|
741 |
public static function get_content_value($content) {
|
|
|
742 |
return trim($content->content, "\r\n ");
|
|
|
743 |
}
|
|
|
744 |
|
|
|
745 |
/**
|
|
|
746 |
* Return the plugin configs for external functions,
|
|
|
747 |
* in some cases the configs will need formatting or be returned only if the current user has some capabilities enabled.
|
|
|
748 |
*
|
|
|
749 |
* @return array the list of config parameters
|
|
|
750 |
* @since Moodle 3.3
|
|
|
751 |
*/
|
|
|
752 |
public function get_config_for_external() {
|
|
|
753 |
// Return all the field configs to null (maybe there is a private key for a service or something similar there).
|
|
|
754 |
$configs = [];
|
|
|
755 |
for ($i = 1; $i <= 10; $i++) {
|
|
|
756 |
$configs["param$i"] = null;
|
|
|
757 |
}
|
|
|
758 |
return $configs;
|
|
|
759 |
}
|
|
|
760 |
|
|
|
761 |
/**
|
|
|
762 |
* Function to let field define their parameters.
|
|
|
763 |
*
|
|
|
764 |
* This method that should be overridden by the datafield plugins
|
|
|
765 |
* when they need to define their data.
|
|
|
766 |
*
|
|
|
767 |
* @return array
|
|
|
768 |
*/
|
|
|
769 |
protected function get_field_params(): array {
|
|
|
770 |
// Name and description of the field.
|
|
|
771 |
$data = [
|
|
|
772 |
'name' => $this->field->name,
|
|
|
773 |
'description' => $this->field->description,
|
|
|
774 |
];
|
|
|
775 |
|
|
|
776 |
// Whether the field is required.
|
|
|
777 |
if (isset($this->field->required)) {
|
|
|
778 |
$data['required'] = $this->field->required;
|
|
|
779 |
}
|
|
|
780 |
|
|
|
781 |
// Add all the field parameters.
|
|
|
782 |
for ($i = 1; $i <= 10; $i++) {
|
|
|
783 |
if (isset($this->field->{"param$i"})) {
|
|
|
784 |
$data["param$i"] = $this->field->{"param$i"};
|
|
|
785 |
}
|
|
|
786 |
}
|
|
|
787 |
|
|
|
788 |
return $data;
|
|
|
789 |
}
|
|
|
790 |
|
|
|
791 |
}
|
|
|
792 |
|
|
|
793 |
|
|
|
794 |
/**
|
|
|
795 |
* Given a template and a dataid, generate a default case template
|
|
|
796 |
*
|
|
|
797 |
* @param stdClass $data the mod_data record.
|
|
|
798 |
* @param string $template the template name
|
|
|
799 |
* @param int $recordid the entry record
|
|
|
800 |
* @param bool $form print a form instead of data
|
|
|
801 |
* @param bool $update if the function update the $data object or not
|
|
|
802 |
* @return string the template content or an empty string if no content is available (for instance, when database has no fields).
|
|
|
803 |
*/
|
|
|
804 |
function data_generate_default_template(&$data, $template, $recordid = 0, $form = false, $update = true) {
|
|
|
805 |
global $DB;
|
|
|
806 |
|
|
|
807 |
if (!$data || !$template) {
|
|
|
808 |
return '';
|
|
|
809 |
}
|
|
|
810 |
|
|
|
811 |
// These templates are empty by default (they have no content).
|
|
|
812 |
$emptytemplates = [
|
|
|
813 |
'csstemplate',
|
|
|
814 |
'jstemplate',
|
|
|
815 |
'listtemplateheader',
|
|
|
816 |
'listtemplatefooter',
|
|
|
817 |
'rsstitletemplate',
|
|
|
818 |
];
|
|
|
819 |
if (in_array($template, $emptytemplates)) {
|
|
|
820 |
return '';
|
|
|
821 |
}
|
|
|
822 |
|
|
|
823 |
$manager = manager::create_from_instance($data);
|
|
|
824 |
if (empty($manager->get_fields())) {
|
|
|
825 |
// No template will be returned if there are no fields.
|
|
|
826 |
return '';
|
|
|
827 |
}
|
|
|
828 |
|
|
|
829 |
$templateclass = \mod_data\template::create_default_template($manager, $template, $form);
|
|
|
830 |
$templatecontent = $templateclass->get_template_content();
|
|
|
831 |
|
|
|
832 |
if ($update) {
|
|
|
833 |
// Update the database instance.
|
|
|
834 |
$newdata = new stdClass();
|
|
|
835 |
$newdata->id = $data->id;
|
|
|
836 |
$newdata->{$template} = $templatecontent;
|
|
|
837 |
$DB->update_record('data', $newdata);
|
|
|
838 |
$data->{$template} = $templatecontent;
|
|
|
839 |
}
|
|
|
840 |
|
|
|
841 |
return $templatecontent;
|
|
|
842 |
}
|
|
|
843 |
|
|
|
844 |
/**
|
|
|
845 |
* Build the form elements to manage tags for a record.
|
|
|
846 |
*
|
|
|
847 |
* @param int|bool $recordid
|
|
|
848 |
* @param string[] $selected raw tag names
|
|
|
849 |
* @return string
|
|
|
850 |
*/
|
|
|
851 |
function data_generate_tag_form($recordid = false, $selected = []) {
|
|
|
852 |
global $CFG, $DB, $OUTPUT, $PAGE;
|
|
|
853 |
|
|
|
854 |
$tagtypestoshow = \core_tag_area::get_showstandard('mod_data', 'data_records');
|
|
|
855 |
$showstandard = ($tagtypestoshow != core_tag_tag::HIDE_STANDARD);
|
|
|
856 |
$typenewtags = ($tagtypestoshow != core_tag_tag::STANDARD_ONLY);
|
|
|
857 |
|
|
|
858 |
$str = html_writer::start_tag('div', array('class' => 'datatagcontrol'));
|
|
|
859 |
|
|
|
860 |
$namefield = empty($CFG->keeptagnamecase) ? 'name' : 'rawname';
|
|
|
861 |
|
|
|
862 |
$tagcollid = \core_tag_area::get_collection('mod_data', 'data_records');
|
|
|
863 |
$tags = [];
|
|
|
864 |
$selectedtags = [];
|
|
|
865 |
|
|
|
866 |
if ($showstandard) {
|
|
|
867 |
$tags += $DB->get_records_menu('tag', array('isstandard' => 1, 'tagcollid' => $tagcollid),
|
|
|
868 |
$namefield, 'id,' . $namefield . ' as fieldname');
|
|
|
869 |
}
|
|
|
870 |
|
|
|
871 |
if ($recordid) {
|
|
|
872 |
$selectedtags += core_tag_tag::get_item_tags_array('mod_data', 'data_records', $recordid);
|
|
|
873 |
}
|
|
|
874 |
|
|
|
875 |
if (!empty($selected)) {
|
|
|
876 |
list($sql, $params) = $DB->get_in_or_equal($selected, SQL_PARAMS_NAMED);
|
|
|
877 |
$params['tagcollid'] = $tagcollid;
|
|
|
878 |
$sql = "SELECT id, $namefield FROM {tag} WHERE tagcollid = :tagcollid AND rawname $sql";
|
|
|
879 |
$selectedtags += $DB->get_records_sql_menu($sql, $params);
|
|
|
880 |
}
|
|
|
881 |
|
|
|
882 |
$tags += $selectedtags;
|
|
|
883 |
|
|
|
884 |
$str .= '<select class="custom-select" name="tags[]" id="tags" multiple>';
|
|
|
885 |
foreach ($tags as $tagid => $tag) {
|
|
|
886 |
$selected = key_exists($tagid, $selectedtags) ? 'selected' : '';
|
|
|
887 |
$str .= "<option value='$tag' $selected>$tag</option>";
|
|
|
888 |
}
|
|
|
889 |
$str .= '</select>';
|
|
|
890 |
|
|
|
891 |
if (has_capability('moodle/tag:manage', context_system::instance()) && $showstandard) {
|
|
|
892 |
$url = new moodle_url('/tag/manage.php', array('tc' => core_tag_area::get_collection('mod_data',
|
|
|
893 |
'data_records')));
|
|
|
894 |
$str .= ' ' . $OUTPUT->action_link($url, get_string('managestandardtags', 'tag'));
|
|
|
895 |
}
|
|
|
896 |
|
|
|
897 |
$PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params = array(
|
|
|
898 |
'#tags',
|
|
|
899 |
$typenewtags,
|
|
|
900 |
'',
|
|
|
901 |
get_string('entertags', 'tag'),
|
|
|
902 |
false,
|
|
|
903 |
$showstandard,
|
|
|
904 |
get_string('noselection', 'form')
|
|
|
905 |
)
|
|
|
906 |
);
|
|
|
907 |
|
|
|
908 |
$str .= html_writer::end_tag('div');
|
|
|
909 |
|
|
|
910 |
return $str;
|
|
|
911 |
}
|
|
|
912 |
|
|
|
913 |
|
|
|
914 |
/**
|
|
|
915 |
* Search for a field name and replaces it with another one in all the
|
|
|
916 |
* form templates. Set $newfieldname as '' if you want to delete the
|
|
|
917 |
* field from the form.
|
|
|
918 |
*
|
|
|
919 |
* @global object
|
|
|
920 |
* @param object $data
|
|
|
921 |
* @param string $searchfieldname
|
|
|
922 |
* @param string $newfieldname
|
|
|
923 |
* @return bool
|
|
|
924 |
*/
|
|
|
925 |
function data_replace_field_in_templates($data, $searchfieldname, $newfieldname) {
|
|
|
926 |
global $DB;
|
|
|
927 |
|
|
|
928 |
$newdata = (object)['id' => $data->id];
|
|
|
929 |
$update = false;
|
|
|
930 |
$templates = ['listtemplate', 'singletemplate', 'asearchtemplate', 'addtemplate', 'rsstemplate'];
|
|
|
931 |
foreach ($templates as $templatename) {
|
|
|
932 |
if (empty($data->$templatename)) {
|
|
|
933 |
continue;
|
|
|
934 |
}
|
|
|
935 |
$search = [
|
|
|
936 |
'[[' . $searchfieldname . ']]',
|
|
|
937 |
'[[' . $searchfieldname . '#id]]',
|
|
|
938 |
'[[' . $searchfieldname . '#name]]',
|
|
|
939 |
'[[' . $searchfieldname . '#description]]',
|
|
|
940 |
];
|
|
|
941 |
if (empty($newfieldname)) {
|
|
|
942 |
$replace = ['', '', '', ''];
|
|
|
943 |
} else {
|
|
|
944 |
$replace = [
|
|
|
945 |
'[[' . $newfieldname . ']]',
|
|
|
946 |
'[[' . $newfieldname . '#id]]',
|
|
|
947 |
'[[' . $newfieldname . '#name]]',
|
|
|
948 |
'[[' . $newfieldname . '#description]]',
|
|
|
949 |
];
|
|
|
950 |
}
|
|
|
951 |
$newdata->{$templatename} = str_ireplace($search, $replace, $data->{$templatename} ?? '');
|
|
|
952 |
$update = true;
|
|
|
953 |
}
|
|
|
954 |
if (!$update) {
|
|
|
955 |
return true;
|
|
|
956 |
}
|
|
|
957 |
return $DB->update_record('data', $newdata);
|
|
|
958 |
}
|
|
|
959 |
|
|
|
960 |
|
|
|
961 |
/**
|
|
|
962 |
* Appends a new field at the end of the form template.
|
|
|
963 |
*
|
|
|
964 |
* @global object
|
|
|
965 |
* @param object $data
|
|
|
966 |
* @param string $newfieldname
|
|
|
967 |
* @return bool if the field has been added or not
|
|
|
968 |
*/
|
|
|
969 |
function data_append_new_field_to_templates($data, $newfieldname): bool {
|
|
|
970 |
global $DB, $OUTPUT;
|
|
|
971 |
|
|
|
972 |
$newdata = (object)['id' => $data->id];
|
|
|
973 |
$update = false;
|
|
|
974 |
$templates = ['singletemplate', 'addtemplate', 'rsstemplate'];
|
|
|
975 |
foreach ($templates as $templatename) {
|
|
|
976 |
if (empty($data->$templatename)
|
|
|
977 |
|| strpos($data->$templatename, "[[$newfieldname]]") !== false
|
|
|
978 |
|| strpos($data->$templatename, "##otherfields##") !== false
|
|
|
979 |
) {
|
|
|
980 |
continue;
|
|
|
981 |
}
|
|
|
982 |
$newdata->$templatename = $data->$templatename;
|
|
|
983 |
$fields = [[
|
|
|
984 |
'fieldname' => '[[' . $newfieldname . '#name]]',
|
|
|
985 |
'fieldcontent' => '[[' . $newfieldname . ']]',
|
|
|
986 |
]];
|
|
|
987 |
$newdata->$templatename .= $OUTPUT->render_from_template(
|
|
|
988 |
'mod_data/fields_otherfields',
|
|
|
989 |
['fields' => $fields, 'classes' => 'added_field']
|
|
|
990 |
);
|
|
|
991 |
$update = true;
|
|
|
992 |
}
|
|
|
993 |
if (!$update) {
|
|
|
994 |
return false;
|
|
|
995 |
}
|
|
|
996 |
return $DB->update_record('data', $newdata);
|
|
|
997 |
}
|
|
|
998 |
|
|
|
999 |
|
|
|
1000 |
/**
|
|
|
1001 |
* given a field name
|
|
|
1002 |
* this function creates an instance of the particular subfield class
|
|
|
1003 |
*
|
|
|
1004 |
* @global object
|
|
|
1005 |
* @param string $name
|
|
|
1006 |
* @param object $data
|
|
|
1007 |
* @return object|bool
|
|
|
1008 |
*/
|
|
|
1009 |
function data_get_field_from_name($name, $data){
|
|
|
1010 |
global $DB;
|
|
|
1011 |
|
|
|
1012 |
$field = $DB->get_record('data_fields', array('name'=>$name, 'dataid'=>$data->id));
|
|
|
1013 |
|
|
|
1014 |
if ($field) {
|
|
|
1015 |
return data_get_field($field, $data);
|
|
|
1016 |
} else {
|
|
|
1017 |
return false;
|
|
|
1018 |
}
|
|
|
1019 |
}
|
|
|
1020 |
|
|
|
1021 |
/**
|
|
|
1022 |
* given a field id
|
|
|
1023 |
* this function creates an instance of the particular subfield class
|
|
|
1024 |
*
|
|
|
1025 |
* @global object
|
|
|
1026 |
* @param int $fieldid
|
|
|
1027 |
* @param object $data
|
|
|
1028 |
* @return bool|object
|
|
|
1029 |
*/
|
|
|
1030 |
function data_get_field_from_id($fieldid, $data){
|
|
|
1031 |
global $DB;
|
|
|
1032 |
|
|
|
1033 |
$field = $DB->get_record('data_fields', array('id'=>$fieldid, 'dataid'=>$data->id));
|
|
|
1034 |
|
|
|
1035 |
if ($field) {
|
|
|
1036 |
return data_get_field($field, $data);
|
|
|
1037 |
} else {
|
|
|
1038 |
return false;
|
|
|
1039 |
}
|
|
|
1040 |
}
|
|
|
1041 |
|
|
|
1042 |
/**
|
|
|
1043 |
* given a field id
|
|
|
1044 |
* this function creates an instance of the particular subfield class
|
|
|
1045 |
*
|
|
|
1046 |
* @global object
|
|
|
1047 |
* @param string $type
|
|
|
1048 |
* @param object $data
|
|
|
1049 |
* @return object
|
|
|
1050 |
*/
|
|
|
1051 |
function data_get_field_new($type, $data) {
|
|
|
1052 |
global $CFG;
|
|
|
1053 |
|
|
|
1054 |
$type = clean_param($type, PARAM_ALPHA);
|
|
|
1055 |
$filepath = $CFG->dirroot.'/mod/data/field/'.$type.'/field.class.php';
|
|
|
1056 |
// It should never access this method if the subfield class doesn't exist.
|
|
|
1057 |
if (!file_exists($filepath)) {
|
|
|
1058 |
throw new \moodle_exception('invalidfieldtype', 'data');
|
|
|
1059 |
}
|
|
|
1060 |
require_once($filepath);
|
|
|
1061 |
$newfield = 'data_field_'.$type;
|
|
|
1062 |
$newfield = new $newfield(0, $data);
|
|
|
1063 |
return $newfield;
|
|
|
1064 |
}
|
|
|
1065 |
|
|
|
1066 |
/**
|
|
|
1067 |
* returns a subclass field object given a record of the field, used to
|
|
|
1068 |
* invoke plugin methods
|
|
|
1069 |
* input: $param $field - record from db
|
|
|
1070 |
*
|
|
|
1071 |
* @global object
|
|
|
1072 |
* @param stdClass $field the field record
|
|
|
1073 |
* @param stdClass $data the data instance
|
|
|
1074 |
* @param stdClass|null $cm optional course module data
|
|
|
1075 |
* @return data_field_base the field object instance or data_field_base if unkown type
|
|
|
1076 |
*/
|
|
|
1077 |
function data_get_field(stdClass $field, stdClass $data, ?stdClass $cm=null): data_field_base {
|
|
|
1078 |
global $CFG;
|
|
|
1079 |
if (!isset($field->type)) {
|
|
|
1080 |
return new data_field_base($field);
|
|
|
1081 |
}
|
|
|
1082 |
$field->type = clean_param($field->type, PARAM_ALPHA);
|
|
|
1083 |
$filepath = $CFG->dirroot.'/mod/data/field/'.$field->type.'/field.class.php';
|
|
|
1084 |
if (!file_exists($filepath)) {
|
|
|
1085 |
return new data_field_base($field);
|
|
|
1086 |
}
|
|
|
1087 |
require_once($filepath);
|
|
|
1088 |
$newfield = 'data_field_'.$field->type;
|
|
|
1089 |
$newfield = new $newfield($field, $data, $cm);
|
|
|
1090 |
return $newfield;
|
|
|
1091 |
}
|
|
|
1092 |
|
|
|
1093 |
|
|
|
1094 |
/**
|
|
|
1095 |
* Given record object (or id), returns true if the record belongs to the current user
|
|
|
1096 |
*
|
|
|
1097 |
* @global object
|
|
|
1098 |
* @global object
|
|
|
1099 |
* @param mixed $record record object or id
|
|
|
1100 |
* @return bool
|
|
|
1101 |
*/
|
|
|
1102 |
function data_isowner($record) {
|
|
|
1103 |
global $USER, $DB;
|
|
|
1104 |
|
|
|
1105 |
if (!isloggedin()) { // perf shortcut
|
|
|
1106 |
return false;
|
|
|
1107 |
}
|
|
|
1108 |
|
|
|
1109 |
if (!is_object($record)) {
|
|
|
1110 |
if (!$record = $DB->get_record('data_records', array('id'=>$record))) {
|
|
|
1111 |
return false;
|
|
|
1112 |
}
|
|
|
1113 |
}
|
|
|
1114 |
|
|
|
1115 |
return ($record->userid == $USER->id);
|
|
|
1116 |
}
|
|
|
1117 |
|
|
|
1118 |
/**
|
|
|
1119 |
* has a user reached the max number of entries?
|
|
|
1120 |
*
|
|
|
1121 |
* @param object $data
|
|
|
1122 |
* @return bool
|
|
|
1123 |
*/
|
|
|
1124 |
function data_atmaxentries($data){
|
|
|
1125 |
if (!$data->maxentries){
|
|
|
1126 |
return false;
|
|
|
1127 |
|
|
|
1128 |
} else {
|
|
|
1129 |
return (data_numentries($data) >= $data->maxentries);
|
|
|
1130 |
}
|
|
|
1131 |
}
|
|
|
1132 |
|
|
|
1133 |
/**
|
|
|
1134 |
* returns the number of entries already made by this user
|
|
|
1135 |
*
|
|
|
1136 |
* @global object
|
|
|
1137 |
* @global object
|
|
|
1138 |
* @param object $data
|
|
|
1139 |
* @return int
|
|
|
1140 |
*/
|
|
|
1141 |
function data_numentries($data, $userid=null) {
|
|
|
1142 |
global $USER, $DB;
|
|
|
1143 |
if ($userid === null) {
|
|
|
1144 |
$userid = $USER->id;
|
|
|
1145 |
}
|
|
|
1146 |
$sql = 'SELECT COUNT(*) FROM {data_records} WHERE dataid=? AND userid=?';
|
|
|
1147 |
return $DB->count_records_sql($sql, array($data->id, $userid));
|
|
|
1148 |
}
|
|
|
1149 |
|
|
|
1150 |
/**
|
|
|
1151 |
* function that takes in a dataid and adds a record
|
|
|
1152 |
* this is used everytime an add template is submitted
|
|
|
1153 |
*
|
|
|
1154 |
* @global object
|
|
|
1155 |
* @global object
|
|
|
1156 |
* @param object $data
|
|
|
1157 |
* @param int $groupid
|
|
|
1158 |
* @param int $userid
|
|
|
1159 |
* @param bool $approved If specified, and the user has the capability to approve entries, then this value
|
|
|
1160 |
* will be used as the approved status of the new record
|
|
|
1161 |
* @return bool
|
|
|
1162 |
*/
|
|
|
1163 |
function data_add_record($data, $groupid = 0, $userid = null, bool $approved = true) {
|
|
|
1164 |
global $USER, $DB;
|
|
|
1165 |
|
|
|
1166 |
$cm = get_coursemodule_from_instance('data', $data->id);
|
|
|
1167 |
$context = context_module::instance($cm->id);
|
|
|
1168 |
|
|
|
1169 |
$record = new stdClass();
|
|
|
1170 |
$record->userid = $userid ?? $USER->id;
|
|
|
1171 |
$record->dataid = $data->id;
|
|
|
1172 |
$record->groupid = $groupid;
|
|
|
1173 |
$record->timecreated = $record->timemodified = time();
|
|
|
1174 |
if (has_capability('mod/data:approve', $context)) {
|
|
|
1175 |
$record->approved = $approved;
|
|
|
1176 |
} else {
|
|
|
1177 |
$record->approved = 0;
|
|
|
1178 |
}
|
|
|
1179 |
$record->id = $DB->insert_record('data_records', $record);
|
|
|
1180 |
|
|
|
1181 |
// Trigger an event for creating this record.
|
|
|
1182 |
$event = \mod_data\event\record_created::create(array(
|
|
|
1183 |
'objectid' => $record->id,
|
|
|
1184 |
'context' => $context,
|
|
|
1185 |
'other' => array(
|
|
|
1186 |
'dataid' => $data->id
|
|
|
1187 |
)
|
|
|
1188 |
));
|
|
|
1189 |
$event->trigger();
|
|
|
1190 |
|
|
|
1191 |
$course = get_course($cm->course);
|
|
|
1192 |
data_update_completion_state($data, $course, $cm);
|
|
|
1193 |
|
|
|
1194 |
return $record->id;
|
|
|
1195 |
}
|
|
|
1196 |
|
|
|
1197 |
/**
|
|
|
1198 |
* check the multple existence any tag in a template
|
|
|
1199 |
*
|
|
|
1200 |
* check to see if there are 2 or more of the same tag being used.
|
|
|
1201 |
*
|
|
|
1202 |
* @global object
|
|
|
1203 |
* @param int $dataid,
|
|
|
1204 |
* @param string $template
|
|
|
1205 |
* @return bool
|
|
|
1206 |
*/
|
|
|
1207 |
function data_tags_check($dataid, $template) {
|
|
|
1208 |
global $DB, $OUTPUT;
|
|
|
1209 |
|
|
|
1210 |
// first get all the possible tags
|
|
|
1211 |
$fields = $DB->get_records('data_fields', array('dataid'=>$dataid));
|
|
|
1212 |
// then we generate strings to replace
|
|
|
1213 |
$tagsok = true; // let's be optimistic
|
|
|
1214 |
foreach ($fields as $field){
|
|
|
1215 |
$pattern="/\[\[" . preg_quote($field->name, '/') . "\]\]/i";
|
|
|
1216 |
if (preg_match_all($pattern, $template, $dummy)>1){
|
|
|
1217 |
$tagsok = false;
|
|
|
1218 |
echo $OUTPUT->notification('[['.$field->name.']] - '.get_string('multipletags','data'));
|
|
|
1219 |
}
|
|
|
1220 |
}
|
|
|
1221 |
// else return true
|
|
|
1222 |
return $tagsok;
|
|
|
1223 |
}
|
|
|
1224 |
|
|
|
1225 |
/**
|
|
|
1226 |
* Adds an instance of a data
|
|
|
1227 |
*
|
|
|
1228 |
* @param stdClass $data
|
|
|
1229 |
* @param mod_data_mod_form $mform
|
|
|
1230 |
* @return int intance id
|
|
|
1231 |
*/
|
|
|
1232 |
function data_add_instance($data, $mform = null) {
|
|
|
1233 |
global $DB, $CFG;
|
|
|
1234 |
require_once($CFG->dirroot.'/mod/data/locallib.php');
|
|
|
1235 |
|
|
|
1236 |
if (empty($data->assessed)) {
|
|
|
1237 |
$data->assessed = 0;
|
|
|
1238 |
}
|
|
|
1239 |
|
|
|
1240 |
if (empty($data->ratingtime) || empty($data->assessed)) {
|
|
|
1241 |
$data->assesstimestart = 0;
|
|
|
1242 |
$data->assesstimefinish = 0;
|
|
|
1243 |
}
|
|
|
1244 |
|
|
|
1245 |
$data->timemodified = time();
|
|
|
1246 |
|
|
|
1247 |
$data->id = $DB->insert_record('data', $data);
|
|
|
1248 |
|
|
|
1249 |
// Add calendar events if necessary.
|
|
|
1250 |
data_set_events($data);
|
|
|
1251 |
if (!empty($data->completionexpected)) {
|
|
|
1252 |
\core_completion\api::update_completion_date_event($data->coursemodule, 'data', $data->id, $data->completionexpected);
|
|
|
1253 |
}
|
|
|
1254 |
|
|
|
1255 |
data_grade_item_update($data);
|
|
|
1256 |
|
|
|
1257 |
return $data->id;
|
|
|
1258 |
}
|
|
|
1259 |
|
|
|
1260 |
/**
|
|
|
1261 |
* updates an instance of a data
|
|
|
1262 |
*
|
|
|
1263 |
* @global object
|
|
|
1264 |
* @param object $data
|
|
|
1265 |
* @return bool
|
|
|
1266 |
*/
|
|
|
1267 |
function data_update_instance($data) {
|
|
|
1268 |
global $DB, $CFG;
|
|
|
1269 |
require_once($CFG->dirroot.'/mod/data/locallib.php');
|
|
|
1270 |
|
|
|
1271 |
$data->timemodified = time();
|
|
|
1272 |
if (!empty($data->instance)) {
|
|
|
1273 |
$data->id = $data->instance;
|
|
|
1274 |
}
|
|
|
1275 |
|
|
|
1276 |
if (empty($data->assessed)) {
|
|
|
1277 |
$data->assessed = 0;
|
|
|
1278 |
}
|
|
|
1279 |
|
|
|
1280 |
if (empty($data->ratingtime) or empty($data->assessed)) {
|
|
|
1281 |
$data->assesstimestart = 0;
|
|
|
1282 |
$data->assesstimefinish = 0;
|
|
|
1283 |
}
|
|
|
1284 |
|
|
|
1285 |
if (empty($data->notification)) {
|
|
|
1286 |
$data->notification = 0;
|
|
|
1287 |
}
|
|
|
1288 |
|
|
|
1289 |
$DB->update_record('data', $data);
|
|
|
1290 |
|
|
|
1291 |
// Add calendar events if necessary.
|
|
|
1292 |
data_set_events($data);
|
|
|
1293 |
$completionexpected = (!empty($data->completionexpected)) ? $data->completionexpected : null;
|
|
|
1294 |
\core_completion\api::update_completion_date_event($data->coursemodule, 'data', $data->id, $completionexpected);
|
|
|
1295 |
|
|
|
1296 |
data_grade_item_update($data);
|
|
|
1297 |
|
|
|
1298 |
return true;
|
|
|
1299 |
|
|
|
1300 |
}
|
|
|
1301 |
|
|
|
1302 |
/**
|
|
|
1303 |
* deletes an instance of a data
|
|
|
1304 |
*
|
|
|
1305 |
* @global object
|
|
|
1306 |
* @param int $id
|
|
|
1307 |
* @return bool
|
|
|
1308 |
*/
|
|
|
1309 |
function data_delete_instance($id) { // takes the dataid
|
|
|
1310 |
global $DB, $CFG;
|
|
|
1311 |
|
|
|
1312 |
if (!$data = $DB->get_record('data', array('id'=>$id))) {
|
|
|
1313 |
return false;
|
|
|
1314 |
}
|
|
|
1315 |
|
|
|
1316 |
$cm = get_coursemodule_from_instance('data', $data->id);
|
|
|
1317 |
$context = context_module::instance($cm->id);
|
|
|
1318 |
|
|
|
1319 |
// Delete all information related to fields.
|
|
|
1320 |
$fields = $DB->get_records('data_fields', ['dataid' => $id]);
|
|
|
1321 |
foreach ($fields as $field) {
|
|
|
1322 |
$todelete = data_get_field($field, $data, $cm);
|
|
|
1323 |
$todelete->delete_field();
|
|
|
1324 |
}
|
|
|
1325 |
|
|
|
1326 |
// Remove old calendar events.
|
|
|
1327 |
$events = $DB->get_records('event', array('modulename' => 'data', 'instance' => $id));
|
|
|
1328 |
foreach ($events as $event) {
|
|
|
1329 |
$event = calendar_event::load($event);
|
|
|
1330 |
$event->delete();
|
|
|
1331 |
}
|
|
|
1332 |
|
|
|
1333 |
// cleanup gradebook
|
|
|
1334 |
data_grade_item_delete($data);
|
|
|
1335 |
|
|
|
1336 |
// Delete the instance itself
|
|
|
1337 |
// We must delete the module record after we delete the grade item.
|
|
|
1338 |
$result = $DB->delete_records('data', array('id'=>$id));
|
|
|
1339 |
|
|
|
1340 |
return $result;
|
|
|
1341 |
}
|
|
|
1342 |
|
|
|
1343 |
/**
|
|
|
1344 |
* returns a summary of data activity of this user
|
|
|
1345 |
*
|
|
|
1346 |
* @global object
|
|
|
1347 |
* @param object $course
|
|
|
1348 |
* @param object $user
|
|
|
1349 |
* @param object $mod
|
|
|
1350 |
* @param object $data
|
|
|
1351 |
* @return object|null
|
|
|
1352 |
*/
|
|
|
1353 |
function data_user_outline($course, $user, $mod, $data) {
|
|
|
1354 |
global $DB, $CFG;
|
|
|
1355 |
require_once("$CFG->libdir/gradelib.php");
|
|
|
1356 |
|
|
|
1357 |
$grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id);
|
|
|
1358 |
if (empty($grades->items[0]->grades)) {
|
|
|
1359 |
$grade = false;
|
|
|
1360 |
} else {
|
|
|
1361 |
$grade = reset($grades->items[0]->grades);
|
|
|
1362 |
}
|
|
|
1363 |
|
|
|
1364 |
|
|
|
1365 |
if ($countrecords = $DB->count_records('data_records', array('dataid'=>$data->id, 'userid'=>$user->id))) {
|
|
|
1366 |
$result = new stdClass();
|
|
|
1367 |
$result->info = get_string('numrecords', 'data', $countrecords);
|
|
|
1368 |
$lastrecord = $DB->get_record_sql('SELECT id,timemodified FROM {data_records}
|
|
|
1369 |
WHERE dataid = ? AND userid = ?
|
|
|
1370 |
ORDER BY timemodified DESC', array($data->id, $user->id), true);
|
|
|
1371 |
$result->time = $lastrecord->timemodified;
|
|
|
1372 |
if ($grade) {
|
|
|
1373 |
if (!$grade->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
|
|
|
1374 |
$result->info .= ', ' . get_string('gradenoun') . ': ' . $grade->str_long_grade;
|
|
|
1375 |
} else {
|
|
|
1376 |
$result->info = get_string('gradenoun') . ': ' . get_string('hidden', 'grades');
|
|
|
1377 |
}
|
|
|
1378 |
}
|
|
|
1379 |
return $result;
|
|
|
1380 |
} else if ($grade) {
|
|
|
1381 |
$result = (object) [
|
|
|
1382 |
'time' => grade_get_date_for_user_grade($grade, $user),
|
|
|
1383 |
];
|
|
|
1384 |
if (!$grade->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
|
|
|
1385 |
$result->info = get_string('gradenoun') . ': ' . $grade->str_long_grade;
|
|
|
1386 |
} else {
|
|
|
1387 |
$result->info = get_string('gradenoun') . ': ' . get_string('hidden', 'grades');
|
|
|
1388 |
}
|
|
|
1389 |
|
|
|
1390 |
return $result;
|
|
|
1391 |
}
|
|
|
1392 |
return NULL;
|
|
|
1393 |
}
|
|
|
1394 |
|
|
|
1395 |
/**
|
|
|
1396 |
* Prints all the records uploaded by this user
|
|
|
1397 |
*
|
|
|
1398 |
* @global object
|
|
|
1399 |
* @param object $course
|
|
|
1400 |
* @param object $user
|
|
|
1401 |
* @param object $mod
|
|
|
1402 |
* @param object $data
|
|
|
1403 |
*/
|
|
|
1404 |
function data_user_complete($course, $user, $mod, $data) {
|
|
|
1405 |
global $DB, $CFG, $OUTPUT;
|
|
|
1406 |
require_once("$CFG->libdir/gradelib.php");
|
|
|
1407 |
|
|
|
1408 |
$grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id);
|
|
|
1409 |
if (!empty($grades->items[0]->grades)) {
|
|
|
1410 |
$grade = reset($grades->items[0]->grades);
|
|
|
1411 |
if (!$grade->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
|
|
|
1412 |
echo $OUTPUT->container(get_string('gradenoun') . ': ' . $grade->str_long_grade);
|
|
|
1413 |
if ($grade->str_feedback) {
|
|
|
1414 |
echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
|
|
|
1415 |
}
|
|
|
1416 |
} else {
|
|
|
1417 |
echo $OUTPUT->container(get_string('gradenoun') . ': ' . get_string('hidden', 'grades'));
|
|
|
1418 |
}
|
|
|
1419 |
}
|
|
|
1420 |
$records = $DB->get_records(
|
|
|
1421 |
'data_records',
|
|
|
1422 |
['dataid' => $data->id, 'userid' => $user->id],
|
|
|
1423 |
'timemodified DESC'
|
|
|
1424 |
);
|
|
|
1425 |
if ($records) {
|
|
|
1426 |
$manager = manager::create_from_instance($data);
|
|
|
1427 |
$parser = $manager->get_template('singletemplate');
|
|
|
1428 |
echo $parser->parse_entries($records);
|
|
|
1429 |
}
|
|
|
1430 |
}
|
|
|
1431 |
|
|
|
1432 |
/**
|
|
|
1433 |
* Return grade for given user or all users.
|
|
|
1434 |
*
|
|
|
1435 |
* @global object
|
|
|
1436 |
* @param object $data
|
|
|
1437 |
* @param int $userid optional user id, 0 means all users
|
|
|
1438 |
* @return array array of grades, false if none
|
|
|
1439 |
*/
|
|
|
1440 |
function data_get_user_grades($data, $userid=0) {
|
|
|
1441 |
global $CFG;
|
|
|
1442 |
|
|
|
1443 |
require_once($CFG->dirroot.'/rating/lib.php');
|
|
|
1444 |
|
|
|
1445 |
$ratingoptions = new stdClass;
|
|
|
1446 |
$ratingoptions->component = 'mod_data';
|
|
|
1447 |
$ratingoptions->ratingarea = 'entry';
|
|
|
1448 |
$ratingoptions->modulename = 'data';
|
|
|
1449 |
$ratingoptions->moduleid = $data->id;
|
|
|
1450 |
|
|
|
1451 |
$ratingoptions->userid = $userid;
|
|
|
1452 |
$ratingoptions->aggregationmethod = $data->assessed;
|
|
|
1453 |
$ratingoptions->scaleid = $data->scale;
|
|
|
1454 |
$ratingoptions->itemtable = 'data_records';
|
|
|
1455 |
$ratingoptions->itemtableusercolumn = 'userid';
|
|
|
1456 |
|
|
|
1457 |
$rm = new rating_manager();
|
|
|
1458 |
return $rm->get_user_grades($ratingoptions);
|
|
|
1459 |
}
|
|
|
1460 |
|
|
|
1461 |
/**
|
|
|
1462 |
* Update activity grades
|
|
|
1463 |
*
|
|
|
1464 |
* @category grade
|
|
|
1465 |
* @param object $data
|
|
|
1466 |
* @param int $userid specific user only, 0 means all
|
|
|
1467 |
* @param bool $nullifnone
|
|
|
1468 |
*/
|
|
|
1469 |
function data_update_grades($data, $userid=0, $nullifnone=true) {
|
|
|
1470 |
global $CFG, $DB;
|
|
|
1471 |
require_once($CFG->libdir.'/gradelib.php');
|
|
|
1472 |
|
|
|
1473 |
if (!$data->assessed) {
|
|
|
1474 |
data_grade_item_update($data);
|
|
|
1475 |
|
|
|
1476 |
} else if ($grades = data_get_user_grades($data, $userid)) {
|
|
|
1477 |
data_grade_item_update($data, $grades);
|
|
|
1478 |
|
|
|
1479 |
} else if ($userid and $nullifnone) {
|
|
|
1480 |
$grade = new stdClass();
|
|
|
1481 |
$grade->userid = $userid;
|
|
|
1482 |
$grade->rawgrade = NULL;
|
|
|
1483 |
data_grade_item_update($data, $grade);
|
|
|
1484 |
|
|
|
1485 |
} else {
|
|
|
1486 |
data_grade_item_update($data);
|
|
|
1487 |
}
|
|
|
1488 |
}
|
|
|
1489 |
|
|
|
1490 |
/**
|
|
|
1491 |
* Update/create grade item for given data
|
|
|
1492 |
*
|
|
|
1493 |
* @category grade
|
|
|
1494 |
* @param stdClass $data A database instance with extra cmidnumber property
|
|
|
1495 |
* @param mixed $grades Optional array/object of grade(s); 'reset' means reset grades in gradebook
|
|
|
1496 |
* @return object grade_item
|
|
|
1497 |
*/
|
|
|
1498 |
function data_grade_item_update($data, $grades=NULL) {
|
|
|
1499 |
global $CFG;
|
|
|
1500 |
require_once($CFG->libdir.'/gradelib.php');
|
|
|
1501 |
|
|
|
1502 |
$params = array('itemname'=>$data->name, 'idnumber'=>$data->cmidnumber);
|
|
|
1503 |
|
|
|
1504 |
if (!$data->assessed or $data->scale == 0) {
|
|
|
1505 |
$params['gradetype'] = GRADE_TYPE_NONE;
|
|
|
1506 |
|
|
|
1507 |
} else if ($data->scale > 0) {
|
|
|
1508 |
$params['gradetype'] = GRADE_TYPE_VALUE;
|
|
|
1509 |
$params['grademax'] = $data->scale;
|
|
|
1510 |
$params['grademin'] = 0;
|
|
|
1511 |
|
|
|
1512 |
} else if ($data->scale < 0) {
|
|
|
1513 |
$params['gradetype'] = GRADE_TYPE_SCALE;
|
|
|
1514 |
$params['scaleid'] = -$data->scale;
|
|
|
1515 |
}
|
|
|
1516 |
|
|
|
1517 |
if ($grades === 'reset') {
|
|
|
1518 |
$params['reset'] = true;
|
|
|
1519 |
$grades = NULL;
|
|
|
1520 |
}
|
|
|
1521 |
|
|
|
1522 |
return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, $grades, $params);
|
|
|
1523 |
}
|
|
|
1524 |
|
|
|
1525 |
/**
|
|
|
1526 |
* Delete grade item for given data
|
|
|
1527 |
*
|
|
|
1528 |
* @category grade
|
|
|
1529 |
* @param object $data object
|
|
|
1530 |
* @return object grade_item
|
|
|
1531 |
*/
|
|
|
1532 |
function data_grade_item_delete($data) {
|
|
|
1533 |
global $CFG;
|
|
|
1534 |
require_once($CFG->libdir.'/gradelib.php');
|
|
|
1535 |
|
|
|
1536 |
return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, NULL, array('deleted'=>1));
|
|
|
1537 |
}
|
|
|
1538 |
|
|
|
1539 |
// junk functions
|
|
|
1540 |
/**
|
|
|
1541 |
* takes a list of records, the current data, a search string,
|
|
|
1542 |
* and mode to display prints the translated template
|
|
|
1543 |
*
|
|
|
1544 |
* @deprecated since Moodle 4.1 MDL-75146 - please do not use this function any more.
|
|
|
1545 |
* @todo MDL-75189 Final deprecation in Moodle 4.5.
|
|
|
1546 |
* @param string $templatename the template name
|
|
|
1547 |
* @param array $records the entries records
|
|
|
1548 |
* @param stdClass $data the database instance object
|
|
|
1549 |
* @param string $search the current search term
|
|
|
1550 |
* @param int $page page number for pagination
|
|
|
1551 |
* @param bool $return if the result should be returned (true) or printed (false)
|
|
|
1552 |
* @param moodle_url|null $jumpurl a moodle_url by which to jump back to the record list (can be null)
|
|
|
1553 |
* @return mixed string with all parsed entries or nothing if $return is false
|
|
|
1554 |
*/
|
|
|
1555 |
function data_print_template($templatename, $records, $data, $search='', $page=0, $return=false, moodle_url $jumpurl=null) {
|
|
|
1556 |
debugging(
|
|
|
1557 |
'data_print_template is deprecated. Use mod_data\\manager::get_template and mod_data\\template::parse_entries instead',
|
|
|
1558 |
DEBUG_DEVELOPER
|
|
|
1559 |
);
|
|
|
1560 |
|
|
|
1561 |
$options = [
|
|
|
1562 |
'search' => $search,
|
|
|
1563 |
'page' => $page,
|
|
|
1564 |
];
|
|
|
1565 |
if ($jumpurl) {
|
|
|
1566 |
$options['baseurl'] = $jumpurl;
|
|
|
1567 |
}
|
|
|
1568 |
$manager = manager::create_from_instance($data);
|
|
|
1569 |
$parser = $manager->get_template($templatename, $options);
|
|
|
1570 |
$content = $parser->parse_entries($records);
|
|
|
1571 |
if ($return) {
|
|
|
1572 |
return $content;
|
|
|
1573 |
}
|
|
|
1574 |
echo $content;
|
|
|
1575 |
}
|
|
|
1576 |
|
|
|
1577 |
/**
|
|
|
1578 |
* Return rating related permissions
|
|
|
1579 |
*
|
|
|
1580 |
* @param string $contextid the context id
|
|
|
1581 |
* @param string $component the component to get rating permissions for
|
|
|
1582 |
* @param string $ratingarea the rating area to get permissions for
|
|
|
1583 |
* @return array an associative array of the user's rating permissions
|
|
|
1584 |
*/
|
|
|
1585 |
function data_rating_permissions($contextid, $component, $ratingarea) {
|
|
|
1586 |
$context = context::instance_by_id($contextid, MUST_EXIST);
|
|
|
1587 |
if ($component != 'mod_data' || $ratingarea != 'entry') {
|
|
|
1588 |
return null;
|
|
|
1589 |
}
|
|
|
1590 |
return array(
|
|
|
1591 |
'view' => has_capability('mod/data:viewrating',$context),
|
|
|
1592 |
'viewany' => has_capability('mod/data:viewanyrating',$context),
|
|
|
1593 |
'viewall' => has_capability('mod/data:viewallratings',$context),
|
|
|
1594 |
'rate' => has_capability('mod/data:rate',$context)
|
|
|
1595 |
);
|
|
|
1596 |
}
|
|
|
1597 |
|
|
|
1598 |
/**
|
|
|
1599 |
* Validates a submitted rating
|
|
|
1600 |
* @param array $params submitted data
|
|
|
1601 |
* context => object the context in which the rated items exists [required]
|
|
|
1602 |
* itemid => int the ID of the object being rated
|
|
|
1603 |
* scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
|
|
|
1604 |
* rating => int the submitted rating
|
|
|
1605 |
* rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
|
|
|
1606 |
* aggregation => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [required]
|
|
|
1607 |
* @return boolean true if the rating is valid. Will throw rating_exception if not
|
|
|
1608 |
*/
|
|
|
1609 |
function data_rating_validate($params) {
|
|
|
1610 |
global $DB, $USER;
|
|
|
1611 |
|
|
|
1612 |
// Check the component is mod_data
|
|
|
1613 |
if ($params['component'] != 'mod_data') {
|
|
|
1614 |
throw new rating_exception('invalidcomponent');
|
|
|
1615 |
}
|
|
|
1616 |
|
|
|
1617 |
// Check the ratingarea is entry (the only rating area in data module)
|
|
|
1618 |
if ($params['ratingarea'] != 'entry') {
|
|
|
1619 |
throw new rating_exception('invalidratingarea');
|
|
|
1620 |
}
|
|
|
1621 |
|
|
|
1622 |
// Check the rateduserid is not the current user .. you can't rate your own entries
|
|
|
1623 |
if ($params['rateduserid'] == $USER->id) {
|
|
|
1624 |
throw new rating_exception('nopermissiontorate');
|
|
|
1625 |
}
|
|
|
1626 |
|
|
|
1627 |
$datasql = "SELECT d.id as dataid, d.scale, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
|
|
|
1628 |
FROM {data_records} r
|
|
|
1629 |
JOIN {data} d ON r.dataid = d.id
|
|
|
1630 |
WHERE r.id = :itemid";
|
|
|
1631 |
$dataparams = array('itemid'=>$params['itemid']);
|
|
|
1632 |
if (!$info = $DB->get_record_sql($datasql, $dataparams)) {
|
|
|
1633 |
//item doesn't exist
|
|
|
1634 |
throw new rating_exception('invaliditemid');
|
|
|
1635 |
}
|
|
|
1636 |
|
|
|
1637 |
if ($info->scale != $params['scaleid']) {
|
|
|
1638 |
//the scale being submitted doesnt match the one in the database
|
|
|
1639 |
throw new rating_exception('invalidscaleid');
|
|
|
1640 |
}
|
|
|
1641 |
|
|
|
1642 |
//check that the submitted rating is valid for the scale
|
|
|
1643 |
|
|
|
1644 |
// lower limit
|
|
|
1645 |
if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) {
|
|
|
1646 |
throw new rating_exception('invalidnum');
|
|
|
1647 |
}
|
|
|
1648 |
|
|
|
1649 |
// upper limit
|
|
|
1650 |
if ($info->scale < 0) {
|
|
|
1651 |
//its a custom scale
|
|
|
1652 |
$scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
|
|
|
1653 |
if ($scalerecord) {
|
|
|
1654 |
$scalearray = explode(',', $scalerecord->scale);
|
|
|
1655 |
if ($params['rating'] > count($scalearray)) {
|
|
|
1656 |
throw new rating_exception('invalidnum');
|
|
|
1657 |
}
|
|
|
1658 |
} else {
|
|
|
1659 |
throw new rating_exception('invalidscaleid');
|
|
|
1660 |
}
|
|
|
1661 |
} else if ($params['rating'] > $info->scale) {
|
|
|
1662 |
//if its numeric and submitted rating is above maximum
|
|
|
1663 |
throw new rating_exception('invalidnum');
|
|
|
1664 |
}
|
|
|
1665 |
|
|
|
1666 |
if ($info->approval && !$info->approved) {
|
|
|
1667 |
//database requires approval but this item isnt approved
|
|
|
1668 |
throw new rating_exception('nopermissiontorate');
|
|
|
1669 |
}
|
|
|
1670 |
|
|
|
1671 |
// check the item we're rating was created in the assessable time window
|
|
|
1672 |
if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) {
|
|
|
1673 |
if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
|
|
|
1674 |
throw new rating_exception('notavailable');
|
|
|
1675 |
}
|
|
|
1676 |
}
|
|
|
1677 |
|
|
|
1678 |
$course = $DB->get_record('course', array('id'=>$info->course), '*', MUST_EXIST);
|
|
|
1679 |
$cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
|
|
|
1680 |
$context = context_module::instance($cm->id);
|
|
|
1681 |
|
|
|
1682 |
// if the supplied context doesnt match the item's context
|
|
|
1683 |
if ($context->id != $params['context']->id) {
|
|
|
1684 |
throw new rating_exception('invalidcontext');
|
|
|
1685 |
}
|
|
|
1686 |
|
|
|
1687 |
// Make sure groups allow this user to see the item they're rating
|
|
|
1688 |
$groupid = $info->groupid;
|
|
|
1689 |
if ($groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used
|
|
|
1690 |
if (!groups_group_exists($groupid)) { // Can't find group
|
|
|
1691 |
throw new rating_exception('cannotfindgroup');//something is wrong
|
|
|
1692 |
}
|
|
|
1693 |
|
|
|
1694 |
if (!groups_is_member($groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
|
|
|
1695 |
// do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
|
|
|
1696 |
throw new rating_exception('notmemberofgroup');
|
|
|
1697 |
}
|
|
|
1698 |
}
|
|
|
1699 |
|
|
|
1700 |
return true;
|
|
|
1701 |
}
|
|
|
1702 |
|
|
|
1703 |
/**
|
|
|
1704 |
* Can the current user see ratings for a given itemid?
|
|
|
1705 |
*
|
|
|
1706 |
* @param array $params submitted data
|
|
|
1707 |
* contextid => int contextid [required]
|
|
|
1708 |
* component => The component for this module - should always be mod_data [required]
|
|
|
1709 |
* ratingarea => object the context in which the rated items exists [required]
|
|
|
1710 |
* itemid => int the ID of the object being rated [required]
|
|
|
1711 |
* scaleid => int scale id [optional]
|
|
|
1712 |
* @return bool
|
|
|
1713 |
* @throws coding_exception
|
|
|
1714 |
* @throws rating_exception
|
|
|
1715 |
*/
|
|
|
1716 |
function mod_data_rating_can_see_item_ratings($params) {
|
|
|
1717 |
global $DB;
|
|
|
1718 |
|
|
|
1719 |
// Check the component is mod_data.
|
|
|
1720 |
if (!isset($params['component']) || $params['component'] != 'mod_data') {
|
|
|
1721 |
throw new rating_exception('invalidcomponent');
|
|
|
1722 |
}
|
|
|
1723 |
|
|
|
1724 |
// Check the ratingarea is entry (the only rating area in data).
|
|
|
1725 |
if (!isset($params['ratingarea']) || $params['ratingarea'] != 'entry') {
|
|
|
1726 |
throw new rating_exception('invalidratingarea');
|
|
|
1727 |
}
|
|
|
1728 |
|
|
|
1729 |
if (!isset($params['itemid'])) {
|
|
|
1730 |
throw new rating_exception('invaliditemid');
|
|
|
1731 |
}
|
|
|
1732 |
|
|
|
1733 |
$datasql = "SELECT d.id as dataid, d.course, r.groupid
|
|
|
1734 |
FROM {data_records} r
|
|
|
1735 |
JOIN {data} d ON r.dataid = d.id
|
|
|
1736 |
WHERE r.id = :itemid";
|
|
|
1737 |
$dataparams = array('itemid' => $params['itemid']);
|
|
|
1738 |
if (!$info = $DB->get_record_sql($datasql, $dataparams)) {
|
|
|
1739 |
// Item doesn't exist.
|
|
|
1740 |
throw new rating_exception('invaliditemid');
|
|
|
1741 |
}
|
|
|
1742 |
|
|
|
1743 |
// User can see ratings of all participants.
|
|
|
1744 |
if ($info->groupid == 0) {
|
|
|
1745 |
return true;
|
|
|
1746 |
}
|
|
|
1747 |
|
|
|
1748 |
$course = $DB->get_record('course', array('id' => $info->course), '*', MUST_EXIST);
|
|
|
1749 |
$cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
|
|
|
1750 |
|
|
|
1751 |
// Make sure groups allow this user to see the item they're rating.
|
|
|
1752 |
return groups_group_visible($info->groupid, $course, $cm);
|
|
|
1753 |
}
|
|
|
1754 |
|
|
|
1755 |
|
|
|
1756 |
/**
|
|
|
1757 |
* function that takes in the current data, number of items per page,
|
|
|
1758 |
* a search string and prints a preference box in view.php
|
|
|
1759 |
*
|
|
|
1760 |
* This preference box prints a searchable advanced search template if
|
|
|
1761 |
* a) A template is defined
|
|
|
1762 |
* b) The advanced search checkbox is checked.
|
|
|
1763 |
*
|
|
|
1764 |
* @global object
|
|
|
1765 |
* @global object
|
|
|
1766 |
* @param object $data
|
|
|
1767 |
* @param int $perpage
|
|
|
1768 |
* @param string $search
|
|
|
1769 |
* @param string $sort
|
|
|
1770 |
* @param string $order
|
|
|
1771 |
* @param array $search_array
|
|
|
1772 |
* @param int $advanced
|
|
|
1773 |
* @param string $mode
|
|
|
1774 |
* @return void
|
|
|
1775 |
*/
|
|
|
1776 |
function data_print_preference_form($data, $perpage, $search, $sort='', $order='ASC', $search_array = '', $advanced = 0, $mode= ''){
|
|
|
1777 |
global $DB, $PAGE, $OUTPUT;
|
|
|
1778 |
|
|
|
1779 |
$cm = get_coursemodule_from_instance('data', $data->id);
|
|
|
1780 |
$context = context_module::instance($cm->id);
|
|
|
1781 |
echo '<div class="datapreferences my-5">';
|
|
|
1782 |
echo '<form id="options" action="view.php" method="get">';
|
|
|
1783 |
echo '<div class="d-flex">';
|
|
|
1784 |
echo '<div>';
|
|
|
1785 |
echo '<input type="hidden" name="d" value="'.$data->id.'" />';
|
|
|
1786 |
if ($mode =='asearch') {
|
|
|
1787 |
$advanced = 1;
|
|
|
1788 |
echo '<input type="hidden" name="mode" value="list" />';
|
|
|
1789 |
}
|
|
|
1790 |
echo '<label for="pref_perpage">'.get_string('pagesize','data').'</label> ';
|
|
|
1791 |
$pagesizes = array(2=>2,3=>3,4=>4,5=>5,6=>6,7=>7,8=>8,9=>9,10=>10,15=>15,
|
|
|
1792 |
20=>20,30=>30,40=>40,50=>50,100=>100,200=>200,300=>300,400=>400,500=>500,1000=>1000);
|
|
|
1793 |
echo html_writer::select($pagesizes, 'perpage', $perpage, false, array('id' => 'pref_perpage',
|
|
|
1794 |
'class' => 'custom-select mr-1'));
|
|
|
1795 |
|
|
|
1796 |
if ($advanced) {
|
|
|
1797 |
$regsearchclass = 'search_none';
|
|
|
1798 |
$advancedsearchclass = 'search_inline';
|
|
|
1799 |
} else {
|
|
|
1800 |
$regsearchclass = 'search_inline';
|
|
|
1801 |
$advancedsearchclass = 'search_none';
|
|
|
1802 |
}
|
|
|
1803 |
echo '<div id="reg_search" class="' . $regsearchclass . ' mr-1" >';
|
|
|
1804 |
echo '<label for="pref_search" class="mr-1">' . get_string('search') . '</label><input type="text" ' .
|
|
|
1805 |
'class="form-control d-inline-block align-middle w-auto mr-1" size="16" name="search" id= "pref_search" value="' . s($search) . '" /></div>';
|
|
|
1806 |
echo '<label for="pref_sortby">'.get_string('sortby').'</label> ';
|
|
|
1807 |
// foreach field, print the option
|
|
|
1808 |
echo '<select name="sort" id="pref_sortby" class="custom-select mr-1">';
|
|
|
1809 |
if ($fields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'name')) {
|
|
|
1810 |
echo '<optgroup label="'.get_string('fields', 'data').'">';
|
|
|
1811 |
foreach ($fields as $field) {
|
|
|
1812 |
if ($field->id == $sort) {
|
|
|
1813 |
echo '<option value="'.$field->id.'" selected="selected">'.s($field->name).'</option>';
|
|
|
1814 |
} else {
|
|
|
1815 |
echo '<option value="'.$field->id.'">'.s($field->name).'</option>';
|
|
|
1816 |
}
|
|
|
1817 |
}
|
|
|
1818 |
echo '</optgroup>';
|
|
|
1819 |
}
|
|
|
1820 |
$options = array();
|
|
|
1821 |
$options[DATA_TIMEADDED] = get_string('timeadded', 'data');
|
|
|
1822 |
$options[DATA_TIMEMODIFIED] = get_string('timemodified', 'data');
|
|
|
1823 |
$options[DATA_FIRSTNAME] = get_string('authorfirstname', 'data');
|
|
|
1824 |
$options[DATA_LASTNAME] = get_string('authorlastname', 'data');
|
|
|
1825 |
if ($data->approval and has_capability('mod/data:approve', $context)) {
|
|
|
1826 |
$options[DATA_APPROVED] = get_string('approved', 'data');
|
|
|
1827 |
}
|
|
|
1828 |
echo '<optgroup label="'.get_string('other', 'data').'">';
|
|
|
1829 |
foreach ($options as $key => $name) {
|
|
|
1830 |
if ($key == $sort) {
|
|
|
1831 |
echo '<option value="'.$key.'" selected="selected">'.$name.'</option>';
|
|
|
1832 |
} else {
|
|
|
1833 |
echo '<option value="'.$key.'">'.$name.'</option>';
|
|
|
1834 |
}
|
|
|
1835 |
}
|
|
|
1836 |
echo '</optgroup>';
|
|
|
1837 |
echo '</select>';
|
|
|
1838 |
echo '<label for="pref_order" class="accesshide">'.get_string('order').'</label>';
|
|
|
1839 |
echo '<select id="pref_order" name="order" class="custom-select mr-1">';
|
|
|
1840 |
if ($order == 'ASC') {
|
|
|
1841 |
echo '<option value="ASC" selected="selected">'.get_string('ascending','data').'</option>';
|
|
|
1842 |
} else {
|
|
|
1843 |
echo '<option value="ASC">'.get_string('ascending','data').'</option>';
|
|
|
1844 |
}
|
|
|
1845 |
if ($order == 'DESC') {
|
|
|
1846 |
echo '<option value="DESC" selected="selected">'.get_string('descending','data').'</option>';
|
|
|
1847 |
} else {
|
|
|
1848 |
echo '<option value="DESC">'.get_string('descending','data').'</option>';
|
|
|
1849 |
}
|
|
|
1850 |
echo '</select>';
|
|
|
1851 |
|
|
|
1852 |
if ($advanced) {
|
|
|
1853 |
$checked = ' checked="checked" ';
|
|
|
1854 |
}
|
|
|
1855 |
else {
|
|
|
1856 |
$checked = '';
|
|
|
1857 |
}
|
|
|
1858 |
$PAGE->requires->js('/mod/data/data.js');
|
|
|
1859 |
echo '<input type="hidden" name="advanced" value="0" />';
|
|
|
1860 |
echo '<input type="hidden" name="filter" value="1" />';
|
|
|
1861 |
echo '<input type="checkbox" id="advancedcheckbox" name="advanced" value="1" ' . $checked . ' ' .
|
|
|
1862 |
'onchange="showHideAdvSearch(this.checked);" class="mx-1" />' .
|
|
|
1863 |
'<label for="advancedcheckbox">' . get_string('advancedsearch', 'data') . '</label>';
|
|
|
1864 |
echo '</div>';
|
|
|
1865 |
echo '<div id="advsearch-save-sec" class="ml-auto '. $regsearchclass . '">';
|
|
|
1866 |
echo '<input type="submit" class="btn btn-secondary" value="' . get_string('savesettings', 'data') . '" />';
|
|
|
1867 |
echo '</div>';
|
|
|
1868 |
echo '</div>';
|
|
|
1869 |
echo '<div>';
|
|
|
1870 |
|
|
|
1871 |
echo '<br />';
|
|
|
1872 |
echo '<div class="' . $advancedsearchclass . '" id="data_adv_form">';
|
|
|
1873 |
echo '<table class="boxaligncenter">';
|
|
|
1874 |
|
|
|
1875 |
// print ASC or DESC
|
|
|
1876 |
echo '<tr><td colspan="2"> </td></tr>';
|
|
|
1877 |
$i = 0;
|
|
|
1878 |
|
|
|
1879 |
// Determine if we are printing all fields for advanced search, or the template for advanced search
|
|
|
1880 |
// If a template is not defined, use the deafault template and display all fields.
|
|
|
1881 |
$asearchtemplate = $data->asearchtemplate;
|
|
|
1882 |
if (empty($asearchtemplate)) {
|
|
|
1883 |
$asearchtemplate = data_generate_default_template($data, 'asearchtemplate', 0, false, false);
|
|
|
1884 |
}
|
|
|
1885 |
|
|
|
1886 |
static $fields = array();
|
|
|
1887 |
static $dataid = null;
|
|
|
1888 |
|
|
|
1889 |
if (empty($dataid)) {
|
|
|
1890 |
$dataid = $data->id;
|
|
|
1891 |
} else if ($dataid != $data->id) {
|
|
|
1892 |
$fields = array();
|
|
|
1893 |
}
|
|
|
1894 |
|
|
|
1895 |
if (empty($fields)) {
|
|
|
1896 |
$fieldrecords = $DB->get_records('data_fields', array('dataid'=>$data->id));
|
|
|
1897 |
foreach ($fieldrecords as $fieldrecord) {
|
|
|
1898 |
$fields[]= data_get_field($fieldrecord, $data);
|
|
|
1899 |
}
|
|
|
1900 |
}
|
|
|
1901 |
|
|
|
1902 |
// Replacing tags
|
|
|
1903 |
$patterns = array();
|
|
|
1904 |
$replacement = array();
|
|
|
1905 |
|
|
|
1906 |
// Then we generate strings to replace for normal tags
|
|
|
1907 |
$otherfields = [];
|
|
|
1908 |
foreach ($fields as $field) {
|
|
|
1909 |
$fieldname = $field->field->name;
|
|
|
1910 |
$fieldname = preg_quote($fieldname, '/');
|
|
|
1911 |
$searchfield = data_get_field_from_id($field->field->id, $data);
|
|
|
1912 |
|
|
|
1913 |
if ($searchfield->type === 'unknown') {
|
|
|
1914 |
continue;
|
|
|
1915 |
}
|
|
|
1916 |
if (!empty($search_array[$field->field->id]->data)) {
|
|
|
1917 |
$searchinput = $searchfield->display_search_field($search_array[$field->field->id]->data);
|
|
|
1918 |
} else {
|
|
|
1919 |
$searchinput = $searchfield->display_search_field();
|
|
|
1920 |
}
|
|
|
1921 |
$patterns[] = "/\[\[$fieldname\]\]/i";
|
|
|
1922 |
$replacement[] = $searchinput;
|
|
|
1923 |
// Extra field information.
|
|
|
1924 |
$patterns[] = "/\[\[$fieldname#name\]\]/i";
|
|
|
1925 |
$replacement[] = $field->field->name;
|
|
|
1926 |
$patterns[] = "/\[\[$fieldname#description\]\]/i";
|
|
|
1927 |
$replacement[] = $field->field->description;
|
|
|
1928 |
// Other fields.
|
|
|
1929 |
if (strpos($asearchtemplate, "[[" . $field->field->name . "]]") === false) {
|
|
|
1930 |
$otherfields[] = [
|
|
|
1931 |
'fieldname' => $searchfield->field->name,
|
|
|
1932 |
'fieldcontent' => $searchinput,
|
|
|
1933 |
];
|
|
|
1934 |
}
|
|
|
1935 |
}
|
|
|
1936 |
$patterns[] = "/##otherfields##/";
|
|
|
1937 |
if (!empty($otherfields)) {
|
|
|
1938 |
$replacement[] = $OUTPUT->render_from_template(
|
|
|
1939 |
'mod_data/fields_otherfields',
|
|
|
1940 |
['fields' => $otherfields]
|
|
|
1941 |
);
|
|
|
1942 |
} else {
|
|
|
1943 |
$replacement[] = '';
|
|
|
1944 |
}
|
|
|
1945 |
|
|
|
1946 |
$fn = !empty($search_array[DATA_FIRSTNAME]->data) ? $search_array[DATA_FIRSTNAME]->data : '';
|
|
|
1947 |
$ln = !empty($search_array[DATA_LASTNAME]->data) ? $search_array[DATA_LASTNAME]->data : '';
|
|
|
1948 |
$patterns[] = '/##firstname##/';
|
|
|
1949 |
$replacement[] = '<label class="accesshide" for="u_fn">' . get_string('authorfirstname', 'data') . '</label>' .
|
|
|
1950 |
'<input type="text" class="form-control" size="16" id="u_fn" name="u_fn" value="' . s($fn) . '" />';
|
|
|
1951 |
$patterns[] = '/##lastname##/';
|
|
|
1952 |
$replacement[] = '<label class="accesshide" for="u_ln">' . get_string('authorlastname', 'data') . '</label>' .
|
|
|
1953 |
'<input type="text" class="form-control" size="16" id="u_ln" name="u_ln" value="' . s($ln) . '" />';
|
|
|
1954 |
|
|
|
1955 |
if (core_tag_tag::is_enabled('mod_data', 'data_records')) {
|
|
|
1956 |
$patterns[] = "/##tags##/";
|
|
|
1957 |
$selectedtags = isset($search_array[DATA_TAGS]->rawtagnames) ? $search_array[DATA_TAGS]->rawtagnames : [];
|
|
|
1958 |
$replacement[] = data_generate_tag_form(false, $selectedtags);
|
|
|
1959 |
}
|
|
|
1960 |
|
|
|
1961 |
// actual replacement of the tags
|
|
|
1962 |
|
|
|
1963 |
$options = new stdClass();
|
|
|
1964 |
$options->para=false;
|
|
|
1965 |
$options->noclean=true;
|
|
|
1966 |
echo '<tr><td>';
|
|
|
1967 |
echo preg_replace($patterns, $replacement, format_text($asearchtemplate, FORMAT_HTML, $options));
|
|
|
1968 |
echo '</td></tr>';
|
|
|
1969 |
|
|
|
1970 |
echo '<tr><td colspan="4"><br/>' .
|
|
|
1971 |
'<input type="submit" class="btn btn-primary mr-1" value="' . get_string('savesettings', 'data') . '" />' .
|
|
|
1972 |
'<input type="submit" class="btn btn-secondary" name="resetadv" value="' . get_string('resetsettings', 'data') . '" />' .
|
|
|
1973 |
'</td></tr>';
|
|
|
1974 |
echo '</table>';
|
|
|
1975 |
echo '</div>';
|
|
|
1976 |
echo '</form>';
|
|
|
1977 |
echo '</div>';
|
|
|
1978 |
echo '<hr/>';
|
|
|
1979 |
}
|
|
|
1980 |
|
|
|
1981 |
/**
|
|
|
1982 |
* @global object
|
|
|
1983 |
* @global object
|
|
|
1984 |
* @param object $data
|
|
|
1985 |
* @param object $record
|
|
|
1986 |
* @param bool $print if the result must be printed or returner.
|
|
|
1987 |
* @return void Output echo'd
|
|
|
1988 |
*/
|
|
|
1989 |
function data_print_ratings($data, $record, bool $print = true) {
|
|
|
1990 |
global $OUTPUT;
|
|
|
1991 |
$result = '';
|
|
|
1992 |
if (!empty($record->rating)){
|
|
|
1993 |
$result = $OUTPUT->render($record->rating);
|
|
|
1994 |
}
|
|
|
1995 |
if (!$print) {
|
|
|
1996 |
return $result;
|
|
|
1997 |
}
|
|
|
1998 |
echo $result;
|
|
|
1999 |
}
|
|
|
2000 |
|
|
|
2001 |
/**
|
|
|
2002 |
* List the actions that correspond to a view of this module.
|
|
|
2003 |
* This is used by the participation report.
|
|
|
2004 |
*
|
|
|
2005 |
* Note: This is not used by new logging system. Event with
|
|
|
2006 |
* crud = 'r' and edulevel = LEVEL_PARTICIPATING will
|
|
|
2007 |
* be considered as view action.
|
|
|
2008 |
*
|
|
|
2009 |
* @return array
|
|
|
2010 |
*/
|
|
|
2011 |
function data_get_view_actions() {
|
|
|
2012 |
return array('view');
|
|
|
2013 |
}
|
|
|
2014 |
|
|
|
2015 |
/**
|
|
|
2016 |
* List the actions that correspond to a post of this module.
|
|
|
2017 |
* This is used by the participation report.
|
|
|
2018 |
*
|
|
|
2019 |
* Note: This is not used by new logging system. Event with
|
|
|
2020 |
* crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
|
|
|
2021 |
* will be considered as post action.
|
|
|
2022 |
*
|
|
|
2023 |
* @return array
|
|
|
2024 |
*/
|
|
|
2025 |
function data_get_post_actions() {
|
|
|
2026 |
return array('add','update','record delete');
|
|
|
2027 |
}
|
|
|
2028 |
|
|
|
2029 |
/**
|
|
|
2030 |
* @param string $name
|
|
|
2031 |
* @param int $dataid
|
|
|
2032 |
* @param int $fieldid
|
|
|
2033 |
* @return bool
|
|
|
2034 |
*/
|
|
|
2035 |
function data_fieldname_exists($name, $dataid, $fieldid = 0) {
|
|
|
2036 |
global $DB;
|
|
|
2037 |
|
|
|
2038 |
if (!is_numeric($name)) {
|
|
|
2039 |
$like = $DB->sql_like('df.name', ':name', false);
|
|
|
2040 |
} else {
|
|
|
2041 |
$like = "df.name = :name";
|
|
|
2042 |
}
|
|
|
2043 |
$params = array('name'=>$name);
|
|
|
2044 |
if ($fieldid) {
|
|
|
2045 |
$params['dataid'] = $dataid;
|
|
|
2046 |
$params['fieldid1'] = $fieldid;
|
|
|
2047 |
$params['fieldid2'] = $fieldid;
|
|
|
2048 |
return $DB->record_exists_sql("SELECT * FROM {data_fields} df
|
|
|
2049 |
WHERE $like AND df.dataid = :dataid
|
|
|
2050 |
AND ((df.id < :fieldid1) OR (df.id > :fieldid2))", $params);
|
|
|
2051 |
} else {
|
|
|
2052 |
$params['dataid'] = $dataid;
|
|
|
2053 |
return $DB->record_exists_sql("SELECT * FROM {data_fields} df
|
|
|
2054 |
WHERE $like AND df.dataid = :dataid", $params);
|
|
|
2055 |
}
|
|
|
2056 |
}
|
|
|
2057 |
|
|
|
2058 |
/**
|
|
|
2059 |
* @param array $fieldinput
|
|
|
2060 |
*/
|
|
|
2061 |
function data_convert_arrays_to_strings(&$fieldinput) {
|
|
|
2062 |
foreach ($fieldinput as $key => $val) {
|
|
|
2063 |
if (is_array($val)) {
|
|
|
2064 |
$str = '';
|
|
|
2065 |
foreach ($val as $inner) {
|
|
|
2066 |
$str .= $inner . ',';
|
|
|
2067 |
}
|
|
|
2068 |
$str = substr($str, 0, -1);
|
|
|
2069 |
|
|
|
2070 |
$fieldinput->$key = $str;
|
|
|
2071 |
}
|
|
|
2072 |
}
|
|
|
2073 |
}
|
|
|
2074 |
|
|
|
2075 |
|
|
|
2076 |
/**
|
|
|
2077 |
* Converts a database (module instance) to use the Roles System
|
|
|
2078 |
*
|
|
|
2079 |
* @global object
|
|
|
2080 |
* @global object
|
|
|
2081 |
* @uses CONTEXT_MODULE
|
|
|
2082 |
* @uses CAP_PREVENT
|
|
|
2083 |
* @uses CAP_ALLOW
|
|
|
2084 |
* @param object $data a data object with the same attributes as a record
|
|
|
2085 |
* from the data database table
|
|
|
2086 |
* @param int $datamodid the id of the data module, from the modules table
|
|
|
2087 |
* @param array $teacherroles array of roles that have archetype teacher
|
|
|
2088 |
* @param array $studentroles array of roles that have archetype student
|
|
|
2089 |
* @param array $guestroles array of roles that have archetype guest
|
|
|
2090 |
* @param int $cmid the course_module id for this data instance
|
|
|
2091 |
* @return boolean data module was converted or not
|
|
|
2092 |
*/
|
|
|
2093 |
function data_convert_to_roles($data, $teacherroles=array(), $studentroles=array(), $cmid=NULL) {
|
|
|
2094 |
global $CFG, $DB, $OUTPUT;
|
|
|
2095 |
|
|
|
2096 |
if (!isset($data->participants) && !isset($data->assesspublic)
|
|
|
2097 |
&& !isset($data->groupmode)) {
|
|
|
2098 |
// We assume that this database has already been converted to use the
|
|
|
2099 |
// Roles System. above fields get dropped the data module has been
|
|
|
2100 |
// upgraded to use Roles.
|
|
|
2101 |
return false;
|
|
|
2102 |
}
|
|
|
2103 |
|
|
|
2104 |
if (empty($cmid)) {
|
|
|
2105 |
// We were not given the course_module id. Try to find it.
|
|
|
2106 |
if (!$cm = get_coursemodule_from_instance('data', $data->id)) {
|
|
|
2107 |
echo $OUTPUT->notification('Could not get the course module for the data');
|
|
|
2108 |
return false;
|
|
|
2109 |
} else {
|
|
|
2110 |
$cmid = $cm->id;
|
|
|
2111 |
}
|
|
|
2112 |
}
|
|
|
2113 |
$context = context_module::instance($cmid);
|
|
|
2114 |
|
|
|
2115 |
|
|
|
2116 |
// $data->participants:
|
|
|
2117 |
// 1 - Only teachers can add entries
|
|
|
2118 |
// 3 - Teachers and students can add entries
|
|
|
2119 |
switch ($data->participants) {
|
|
|
2120 |
case 1:
|
|
|
2121 |
foreach ($studentroles as $studentrole) {
|
|
|
2122 |
assign_capability('mod/data:writeentry', CAP_PREVENT, $studentrole->id, $context->id);
|
|
|
2123 |
}
|
|
|
2124 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2125 |
assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2126 |
}
|
|
|
2127 |
break;
|
|
|
2128 |
case 3:
|
|
|
2129 |
foreach ($studentroles as $studentrole) {
|
|
|
2130 |
assign_capability('mod/data:writeentry', CAP_ALLOW, $studentrole->id, $context->id);
|
|
|
2131 |
}
|
|
|
2132 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2133 |
assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2134 |
}
|
|
|
2135 |
break;
|
|
|
2136 |
}
|
|
|
2137 |
|
|
|
2138 |
// $data->assessed:
|
|
|
2139 |
// 2 - Only teachers can rate posts
|
|
|
2140 |
// 1 - Everyone can rate posts
|
|
|
2141 |
// 0 - No one can rate posts
|
|
|
2142 |
switch ($data->assessed) {
|
|
|
2143 |
case 0:
|
|
|
2144 |
foreach ($studentroles as $studentrole) {
|
|
|
2145 |
assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id);
|
|
|
2146 |
}
|
|
|
2147 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2148 |
assign_capability('mod/data:rate', CAP_PREVENT, $teacherrole->id, $context->id);
|
|
|
2149 |
}
|
|
|
2150 |
break;
|
|
|
2151 |
case 1:
|
|
|
2152 |
foreach ($studentroles as $studentrole) {
|
|
|
2153 |
assign_capability('mod/data:rate', CAP_ALLOW, $studentrole->id, $context->id);
|
|
|
2154 |
}
|
|
|
2155 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2156 |
assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2157 |
}
|
|
|
2158 |
break;
|
|
|
2159 |
case 2:
|
|
|
2160 |
foreach ($studentroles as $studentrole) {
|
|
|
2161 |
assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id);
|
|
|
2162 |
}
|
|
|
2163 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2164 |
assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2165 |
}
|
|
|
2166 |
break;
|
|
|
2167 |
}
|
|
|
2168 |
|
|
|
2169 |
// $data->assesspublic:
|
|
|
2170 |
// 0 - Students can only see their own ratings
|
|
|
2171 |
// 1 - Students can see everyone's ratings
|
|
|
2172 |
switch ($data->assesspublic) {
|
|
|
2173 |
case 0:
|
|
|
2174 |
foreach ($studentroles as $studentrole) {
|
|
|
2175 |
assign_capability('mod/data:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
|
|
|
2176 |
}
|
|
|
2177 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2178 |
assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2179 |
}
|
|
|
2180 |
break;
|
|
|
2181 |
case 1:
|
|
|
2182 |
foreach ($studentroles as $studentrole) {
|
|
|
2183 |
assign_capability('mod/data:viewrating', CAP_ALLOW, $studentrole->id, $context->id);
|
|
|
2184 |
}
|
|
|
2185 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2186 |
assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2187 |
}
|
|
|
2188 |
break;
|
|
|
2189 |
}
|
|
|
2190 |
|
|
|
2191 |
if (empty($cm)) {
|
|
|
2192 |
$cm = $DB->get_record('course_modules', array('id'=>$cmid));
|
|
|
2193 |
}
|
|
|
2194 |
|
|
|
2195 |
switch ($cm->groupmode) {
|
|
|
2196 |
case NOGROUPS:
|
|
|
2197 |
break;
|
|
|
2198 |
case SEPARATEGROUPS:
|
|
|
2199 |
foreach ($studentroles as $studentrole) {
|
|
|
2200 |
assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
|
|
|
2201 |
}
|
|
|
2202 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2203 |
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2204 |
}
|
|
|
2205 |
break;
|
|
|
2206 |
case VISIBLEGROUPS:
|
|
|
2207 |
foreach ($studentroles as $studentrole) {
|
|
|
2208 |
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
|
|
|
2209 |
}
|
|
|
2210 |
foreach ($teacherroles as $teacherrole) {
|
|
|
2211 |
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
|
|
|
2212 |
}
|
|
|
2213 |
break;
|
|
|
2214 |
}
|
|
|
2215 |
return true;
|
|
|
2216 |
}
|
|
|
2217 |
|
|
|
2218 |
/**
|
|
|
2219 |
* Returns the best name to show for a preset
|
|
|
2220 |
*
|
|
|
2221 |
* @param string $shortname
|
|
|
2222 |
* @param string $path
|
|
|
2223 |
* @return string
|
|
|
2224 |
* @deprecated since Moodle 4.1 MDL-75148 - please, use the preset::get_name_from_plugin() function instead.
|
|
|
2225 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
2226 |
* @see preset::get_name_from_plugin()
|
|
|
2227 |
*/
|
|
|
2228 |
function data_preset_name($shortname, $path) {
|
|
|
2229 |
debugging('data_preset_name() is deprecated. Please use preset::get_name_from_plugin() instead.', DEBUG_DEVELOPER);
|
|
|
2230 |
|
|
|
2231 |
return preset::get_name_from_plugin($shortname);
|
|
|
2232 |
}
|
|
|
2233 |
|
|
|
2234 |
/**
|
|
|
2235 |
* Returns an array of all the available presets.
|
|
|
2236 |
*
|
|
|
2237 |
* @return array
|
|
|
2238 |
* @deprecated since Moodle 4.1 MDL-75148 - please, use the manager::get_available_presets() function instead.
|
|
|
2239 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
2240 |
* @see manager::get_available_presets()
|
|
|
2241 |
*/
|
|
|
2242 |
function data_get_available_presets($context) {
|
|
|
2243 |
debugging('data_get_available_presets() is deprecated. Please use manager::get_available_presets() instead.', DEBUG_DEVELOPER);
|
|
|
2244 |
|
|
|
2245 |
$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
|
|
|
2246 |
$manager = manager::create_from_coursemodule($cm);
|
|
|
2247 |
return $manager->get_available_presets();
|
|
|
2248 |
}
|
|
|
2249 |
|
|
|
2250 |
/**
|
|
|
2251 |
* Gets an array of all of the presets that users have saved to the site.
|
|
|
2252 |
*
|
|
|
2253 |
* @param stdClass $context The context that we are looking from.
|
|
|
2254 |
* @param array $presets
|
|
|
2255 |
* @return array An array of presets
|
|
|
2256 |
* @deprecated since Moodle 4.1 MDL-75148 - please, use the manager::get_available_saved_presets() function instead.
|
|
|
2257 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
2258 |
* @see manager::get_available_saved_presets()
|
|
|
2259 |
*/
|
|
|
2260 |
function data_get_available_site_presets($context, array $presets=array()) {
|
|
|
2261 |
debugging(
|
|
|
2262 |
'data_get_available_site_presets() is deprecated. Please use manager::get_available_saved_presets() instead.',
|
|
|
2263 |
DEBUG_DEVELOPER
|
|
|
2264 |
);
|
|
|
2265 |
|
|
|
2266 |
$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
|
|
|
2267 |
$manager = manager::create_from_coursemodule($cm);
|
|
|
2268 |
$savedpresets = $manager->get_available_saved_presets();
|
|
|
2269 |
return array_merge($presets, $savedpresets);
|
|
|
2270 |
}
|
|
|
2271 |
|
|
|
2272 |
/**
|
|
|
2273 |
* Deletes a saved preset.
|
|
|
2274 |
*
|
|
|
2275 |
* @param string $name
|
|
|
2276 |
* @return bool
|
|
|
2277 |
* @deprecated since Moodle 4.1 MDL-75187 - please, use the preset::delete() function instead.
|
|
|
2278 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
2279 |
* @see preset::delete()
|
|
|
2280 |
*/
|
|
|
2281 |
function data_delete_site_preset($name) {
|
|
|
2282 |
debugging('data_delete_site_preset() is deprecated. Please use preset::delete() instead.', DEBUG_DEVELOPER);
|
|
|
2283 |
|
|
|
2284 |
$fs = get_file_storage();
|
|
|
2285 |
|
|
|
2286 |
$files = $fs->get_directory_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/');
|
|
|
2287 |
if (!empty($files)) {
|
|
|
2288 |
foreach ($files as $file) {
|
|
|
2289 |
$file->delete();
|
|
|
2290 |
}
|
|
|
2291 |
}
|
|
|
2292 |
|
|
|
2293 |
$dir = $fs->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/', '.');
|
|
|
2294 |
if (!empty($dir)) {
|
|
|
2295 |
$dir->delete();
|
|
|
2296 |
}
|
|
|
2297 |
return true;
|
|
|
2298 |
}
|
|
|
2299 |
|
|
|
2300 |
/**
|
|
|
2301 |
* Prints the heads for a page
|
|
|
2302 |
*
|
|
|
2303 |
* @param stdClass $course
|
|
|
2304 |
* @param stdClass $cm
|
|
|
2305 |
* @param stdClass $data
|
|
|
2306 |
* @param string $currenttab
|
|
|
2307 |
* @param string $actionbar
|
|
|
2308 |
*/
|
|
|
2309 |
function data_print_header($course, $cm, $data, $currenttab='', string $actionbar = '') {
|
|
|
2310 |
|
|
|
2311 |
global $CFG, $displaynoticegood, $displaynoticebad, $OUTPUT, $PAGE, $USER;
|
|
|
2312 |
|
|
|
2313 |
echo $OUTPUT->header();
|
|
|
2314 |
|
|
|
2315 |
echo $actionbar;
|
|
|
2316 |
|
|
|
2317 |
// Print any notices
|
|
|
2318 |
|
|
|
2319 |
if (!empty($displaynoticegood)) {
|
|
|
2320 |
echo $OUTPUT->notification($displaynoticegood, 'notifysuccess'); // good (usually green)
|
|
|
2321 |
} else if (!empty($displaynoticebad)) {
|
|
|
2322 |
echo $OUTPUT->notification($displaynoticebad); // bad (usuually red)
|
|
|
2323 |
}
|
|
|
2324 |
}
|
|
|
2325 |
|
|
|
2326 |
/**
|
|
|
2327 |
* Can user add more entries?
|
|
|
2328 |
*
|
|
|
2329 |
* @param object $data
|
|
|
2330 |
* @param mixed $currentgroup
|
|
|
2331 |
* @param int $groupmode
|
|
|
2332 |
* @param stdClass $context
|
|
|
2333 |
* @return bool
|
|
|
2334 |
*/
|
|
|
2335 |
function data_user_can_add_entry($data, $currentgroup, $groupmode, $context = null) {
|
|
|
2336 |
global $DB;
|
|
|
2337 |
|
|
|
2338 |
// Don't let add entry to a database that has no fields.
|
|
|
2339 |
if (!$DB->record_exists('data_fields', ['dataid' => $data->id])) {
|
|
|
2340 |
return false;
|
|
|
2341 |
}
|
|
|
2342 |
|
|
|
2343 |
if (empty($context)) {
|
|
|
2344 |
$cm = get_coursemodule_from_instance('data', $data->id, 0, false, MUST_EXIST);
|
|
|
2345 |
$context = context_module::instance($cm->id);
|
|
|
2346 |
}
|
|
|
2347 |
|
|
|
2348 |
if (has_capability('mod/data:manageentries', $context)) {
|
|
|
2349 |
// no entry limits apply if user can manage
|
|
|
2350 |
|
|
|
2351 |
} else if (!has_capability('mod/data:writeentry', $context)) {
|
|
|
2352 |
return false;
|
|
|
2353 |
|
|
|
2354 |
} else if (data_atmaxentries($data)) {
|
|
|
2355 |
return false;
|
|
|
2356 |
} else if (data_in_readonly_period($data)) {
|
|
|
2357 |
// Check whether we're in a read-only period
|
|
|
2358 |
return false;
|
|
|
2359 |
}
|
|
|
2360 |
|
|
|
2361 |
if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
|
|
|
2362 |
return true;
|
|
|
2363 |
}
|
|
|
2364 |
|
|
|
2365 |
if ($currentgroup) {
|
|
|
2366 |
return groups_is_member($currentgroup);
|
|
|
2367 |
} else {
|
|
|
2368 |
//else it might be group 0 in visible mode
|
|
|
2369 |
if ($groupmode == VISIBLEGROUPS){
|
|
|
2370 |
return true;
|
|
|
2371 |
} else {
|
|
|
2372 |
return false;
|
|
|
2373 |
}
|
|
|
2374 |
}
|
|
|
2375 |
}
|
|
|
2376 |
|
|
|
2377 |
/**
|
|
|
2378 |
* Check whether the current user is allowed to manage the given record considering manageentries capability,
|
|
|
2379 |
* data_in_readonly_period() result, ownership (determined by data_isowner()) and manageapproved setting.
|
|
|
2380 |
* @param mixed $record record object or id
|
|
|
2381 |
* @param object $data data object
|
|
|
2382 |
* @param object $context context object
|
|
|
2383 |
* @return bool returns true if the user is allowd to edit the entry, false otherwise
|
|
|
2384 |
*/
|
|
|
2385 |
function data_user_can_manage_entry($record, $data, $context) {
|
|
|
2386 |
global $DB;
|
|
|
2387 |
|
|
|
2388 |
if (has_capability('mod/data:manageentries', $context)) {
|
|
|
2389 |
return true;
|
|
|
2390 |
}
|
|
|
2391 |
|
|
|
2392 |
// Check whether this activity is read-only at present.
|
|
|
2393 |
$readonly = data_in_readonly_period($data);
|
|
|
2394 |
|
|
|
2395 |
if (!$readonly) {
|
|
|
2396 |
// Get record object from db if just id given like in data_isowner.
|
|
|
2397 |
// ...done before calling data_isowner() to avoid querying db twice.
|
|
|
2398 |
if (!is_object($record)) {
|
|
|
2399 |
if (!$record = $DB->get_record('data_records', array('id' => $record))) {
|
|
|
2400 |
return false;
|
|
|
2401 |
}
|
|
|
2402 |
}
|
|
|
2403 |
if (data_isowner($record)) {
|
|
|
2404 |
if ($data->approval && $record->approved) {
|
|
|
2405 |
return $data->manageapproved == 1;
|
|
|
2406 |
} else {
|
|
|
2407 |
return true;
|
|
|
2408 |
}
|
|
|
2409 |
}
|
|
|
2410 |
}
|
|
|
2411 |
|
|
|
2412 |
return false;
|
|
|
2413 |
}
|
|
|
2414 |
|
|
|
2415 |
/**
|
|
|
2416 |
* Check whether the specified database activity is currently in a read-only period
|
|
|
2417 |
*
|
|
|
2418 |
* @param object $data
|
|
|
2419 |
* @return bool returns true if the time fields in $data indicate a read-only period; false otherwise
|
|
|
2420 |
*/
|
|
|
2421 |
function data_in_readonly_period($data) {
|
|
|
2422 |
$now = time();
|
|
|
2423 |
if (!$data->timeviewfrom && !$data->timeviewto) {
|
|
|
2424 |
return false;
|
|
|
2425 |
} else if (($data->timeviewfrom && $now < $data->timeviewfrom) || ($data->timeviewto && $now > $data->timeviewto)) {
|
|
|
2426 |
return false;
|
|
|
2427 |
}
|
|
|
2428 |
return true;
|
|
|
2429 |
}
|
|
|
2430 |
|
|
|
2431 |
/**
|
|
|
2432 |
* Check if the files in a directory are the expected for a preset.
|
|
|
2433 |
*
|
|
|
2434 |
* @return bool Wheter the defined $directory has or not all the expected preset files.
|
|
|
2435 |
*
|
|
|
2436 |
* @deprecated since Moodle 4.1 MDL-75148 - please, use the preset::is_directory_a_preset() function instead.
|
|
|
2437 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
2438 |
* @see manager::is_directory_a_preset()
|
|
|
2439 |
*/
|
|
|
2440 |
function is_directory_a_preset($directory) {
|
|
|
2441 |
debugging('is_directory_a_preset() is deprecated. Please use preset::is_directory_a_preset() instead.', DEBUG_DEVELOPER);
|
|
|
2442 |
|
|
|
2443 |
return preset::is_directory_a_preset($directory);
|
|
|
2444 |
}
|
|
|
2445 |
|
|
|
2446 |
/**
|
|
|
2447 |
* Abstract class used for data preset importers
|
|
|
2448 |
*
|
|
|
2449 |
* @deprecated since Moodle 4.1 MDL-75140 - please do not use this class any more.
|
|
|
2450 |
* @todo MDL-75189 Final deprecation in Moodle 4.5.
|
|
|
2451 |
*/
|
|
|
2452 |
abstract class data_preset_importer {
|
|
|
2453 |
|
|
|
2454 |
protected $course;
|
|
|
2455 |
protected $cm;
|
|
|
2456 |
protected $module;
|
|
|
2457 |
protected $directory;
|
|
|
2458 |
|
|
|
2459 |
/**
|
|
|
2460 |
* Constructor
|
|
|
2461 |
*
|
|
|
2462 |
* @param stdClass $course
|
|
|
2463 |
* @param stdClass $cm
|
|
|
2464 |
* @param stdClass $module
|
|
|
2465 |
* @param string $directory
|
|
|
2466 |
*/
|
|
|
2467 |
public function __construct($course, $cm, $module, $directory) {
|
|
|
2468 |
debugging(
|
|
|
2469 |
'data_preset_importer is deprecated. Please use mod\\data\\local\\importer\\preset_importer instead',
|
|
|
2470 |
DEBUG_DEVELOPER
|
|
|
2471 |
);
|
|
|
2472 |
|
|
|
2473 |
$this->course = $course;
|
|
|
2474 |
$this->cm = $cm;
|
|
|
2475 |
$this->module = $module;
|
|
|
2476 |
$this->directory = $directory;
|
|
|
2477 |
}
|
|
|
2478 |
|
|
|
2479 |
/**
|
|
|
2480 |
* Returns the name of the directory the preset is located in
|
|
|
2481 |
* @return string
|
|
|
2482 |
*/
|
|
|
2483 |
public function get_directory() {
|
|
|
2484 |
return basename($this->directory);
|
|
|
2485 |
}
|
|
|
2486 |
|
|
|
2487 |
/**
|
|
|
2488 |
* Retreive the contents of a file. That file may either be in a conventional directory of the Moodle file storage
|
|
|
2489 |
* @param file_storage $filestorage. should be null if using a conventional directory
|
|
|
2490 |
* @param stored_file $fileobj the directory to look in. null if using a conventional directory
|
|
|
2491 |
* @param string $dir the directory to look in. null if using the Moodle file storage
|
|
|
2492 |
* @param string $filename the name of the file we want
|
|
|
2493 |
* @return string the contents of the file or null if the file doesn't exist.
|
|
|
2494 |
*/
|
|
|
2495 |
public function data_preset_get_file_contents(&$filestorage, &$fileobj, $dir, $filename) {
|
|
|
2496 |
if(empty($filestorage) || empty($fileobj)) {
|
|
|
2497 |
if (substr($dir, -1)!='/') {
|
|
|
2498 |
$dir .= '/';
|
|
|
2499 |
}
|
|
|
2500 |
if (file_exists($dir.$filename)) {
|
|
|
2501 |
return file_get_contents($dir.$filename);
|
|
|
2502 |
} else {
|
|
|
2503 |
return null;
|
|
|
2504 |
}
|
|
|
2505 |
} else {
|
|
|
2506 |
if ($filestorage->file_exists(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename)) {
|
|
|
2507 |
$file = $filestorage->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename);
|
|
|
2508 |
return $file->get_content();
|
|
|
2509 |
} else {
|
|
|
2510 |
return null;
|
|
|
2511 |
}
|
|
|
2512 |
}
|
|
|
2513 |
|
|
|
2514 |
}
|
|
|
2515 |
/**
|
|
|
2516 |
* Gets the preset settings
|
|
|
2517 |
* @global moodle_database $DB
|
|
|
2518 |
* @return stdClass
|
|
|
2519 |
*/
|
|
|
2520 |
public function get_preset_settings() {
|
|
|
2521 |
global $DB, $CFG;
|
|
|
2522 |
require_once($CFG->libdir.'/xmlize.php');
|
|
|
2523 |
|
|
|
2524 |
$fs = $fileobj = null;
|
|
|
2525 |
if (!preset::is_directory_a_preset($this->directory)) {
|
|
|
2526 |
//maybe the user requested a preset stored in the Moodle file storage
|
|
|
2527 |
|
|
|
2528 |
$fs = get_file_storage();
|
|
|
2529 |
$files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
|
|
|
2530 |
|
|
|
2531 |
//preset name to find will be the final element of the directory
|
|
|
2532 |
$explodeddirectory = explode('/', $this->directory);
|
|
|
2533 |
$presettofind = end($explodeddirectory);
|
|
|
2534 |
|
|
|
2535 |
//now go through the available files available and see if we can find it
|
|
|
2536 |
foreach ($files as $file) {
|
|
|
2537 |
if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory()) {
|
|
|
2538 |
continue;
|
|
|
2539 |
}
|
|
|
2540 |
$presetname = trim($file->get_filepath(), '/');
|
|
|
2541 |
if ($presetname==$presettofind) {
|
|
|
2542 |
$this->directory = $presetname;
|
|
|
2543 |
$fileobj = $file;
|
|
|
2544 |
}
|
|
|
2545 |
}
|
|
|
2546 |
|
|
|
2547 |
if (empty($fileobj)) {
|
|
|
2548 |
throw new \moodle_exception('invalidpreset', 'data', '', $this->directory);
|
|
|
2549 |
}
|
|
|
2550 |
}
|
|
|
2551 |
|
|
|
2552 |
$allowed_settings = array(
|
|
|
2553 |
'intro',
|
|
|
2554 |
'comments',
|
|
|
2555 |
'requiredentries',
|
|
|
2556 |
'requiredentriestoview',
|
|
|
2557 |
'maxentries',
|
|
|
2558 |
'rssarticles',
|
|
|
2559 |
'approval',
|
|
|
2560 |
'defaultsortdir',
|
|
|
2561 |
'defaultsort');
|
|
|
2562 |
|
|
|
2563 |
$result = new stdClass;
|
|
|
2564 |
$result->settings = new stdClass;
|
|
|
2565 |
$result->importfields = array();
|
|
|
2566 |
$result->currentfields = $DB->get_records('data_fields', array('dataid'=>$this->module->id));
|
|
|
2567 |
if (!$result->currentfields) {
|
|
|
2568 |
$result->currentfields = array();
|
|
|
2569 |
}
|
|
|
2570 |
|
|
|
2571 |
|
|
|
2572 |
/* Grab XML */
|
|
|
2573 |
$presetxml = $this->data_preset_get_file_contents($fs, $fileobj, $this->directory,'preset.xml');
|
|
|
2574 |
$parsedxml = xmlize($presetxml, 0);
|
|
|
2575 |
|
|
|
2576 |
/* First, do settings. Put in user friendly array. */
|
|
|
2577 |
$settingsarray = $parsedxml['preset']['#']['settings'][0]['#'];
|
|
|
2578 |
$result->settings = new StdClass();
|
|
|
2579 |
foreach ($settingsarray as $setting => $value) {
|
|
|
2580 |
if (!is_array($value) || !in_array($setting, $allowed_settings)) {
|
|
|
2581 |
// unsupported setting
|
|
|
2582 |
continue;
|
|
|
2583 |
}
|
|
|
2584 |
$result->settings->$setting = $value[0]['#'];
|
|
|
2585 |
}
|
|
|
2586 |
|
|
|
2587 |
/* Now work out fields to user friendly array */
|
|
|
2588 |
$fieldsarray = $parsedxml['preset']['#']['field'];
|
|
|
2589 |
foreach ($fieldsarray as $field) {
|
|
|
2590 |
if (!is_array($field)) {
|
|
|
2591 |
continue;
|
|
|
2592 |
}
|
|
|
2593 |
$f = new StdClass();
|
|
|
2594 |
foreach ($field['#'] as $param => $value) {
|
|
|
2595 |
if (!is_array($value)) {
|
|
|
2596 |
continue;
|
|
|
2597 |
}
|
|
|
2598 |
$f->$param = $value[0]['#'];
|
|
|
2599 |
}
|
|
|
2600 |
$f->dataid = $this->module->id;
|
|
|
2601 |
$f->type = clean_param($f->type, PARAM_ALPHA);
|
|
|
2602 |
$result->importfields[] = $f;
|
|
|
2603 |
}
|
|
|
2604 |
/* Now add the HTML templates to the settings array so we can update d */
|
|
|
2605 |
$result->settings->singletemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"singletemplate.html");
|
|
|
2606 |
$result->settings->listtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplate.html");
|
|
|
2607 |
$result->settings->listtemplateheader = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplateheader.html");
|
|
|
2608 |
$result->settings->listtemplatefooter = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplatefooter.html");
|
|
|
2609 |
$result->settings->addtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"addtemplate.html");
|
|
|
2610 |
$result->settings->rsstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstemplate.html");
|
|
|
2611 |
$result->settings->rsstitletemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstitletemplate.html");
|
|
|
2612 |
$result->settings->csstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"csstemplate.css");
|
|
|
2613 |
$result->settings->jstemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"jstemplate.js");
|
|
|
2614 |
$result->settings->asearchtemplate = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"asearchtemplate.html");
|
|
|
2615 |
|
|
|
2616 |
$result->settings->instance = $this->module->id;
|
|
|
2617 |
return $result;
|
|
|
2618 |
}
|
|
|
2619 |
|
|
|
2620 |
/**
|
|
|
2621 |
* Import the preset into the given database module
|
|
|
2622 |
* @return bool
|
|
|
2623 |
*/
|
|
|
2624 |
function import($overwritesettings) {
|
|
|
2625 |
global $DB, $CFG, $OUTPUT;
|
|
|
2626 |
|
|
|
2627 |
$params = $this->get_preset_settings();
|
|
|
2628 |
$settings = $params->settings;
|
|
|
2629 |
$newfields = $params->importfields;
|
|
|
2630 |
$currentfields = $params->currentfields;
|
|
|
2631 |
$preservedfields = array();
|
|
|
2632 |
|
|
|
2633 |
/* Maps fields and makes new ones */
|
|
|
2634 |
if (!empty($newfields)) {
|
|
|
2635 |
/* We require an injective mapping, and need to know what to protect */
|
|
|
2636 |
foreach ($newfields as $nid => $newfield) {
|
|
|
2637 |
$cid = optional_param("field_$nid", -1, PARAM_INT);
|
|
|
2638 |
if ($cid == -1) {
|
|
|
2639 |
continue;
|
|
|
2640 |
}
|
|
|
2641 |
if (array_key_exists($cid, $preservedfields)){
|
|
|
2642 |
throw new \moodle_exception('notinjectivemap', 'data');
|
|
|
2643 |
}
|
|
|
2644 |
else $preservedfields[$cid] = true;
|
|
|
2645 |
}
|
|
|
2646 |
$missingfieldtypes = [];
|
|
|
2647 |
foreach ($newfields as $nid => $newfield) {
|
|
|
2648 |
$cid = optional_param("field_$nid", -1, PARAM_INT);
|
|
|
2649 |
|
|
|
2650 |
/* A mapping. Just need to change field params. Data kept. */
|
|
|
2651 |
if ($cid != -1 and isset($currentfields[$cid])) {
|
|
|
2652 |
$fieldobject = data_get_field_from_id($currentfields[$cid]->id, $this->module);
|
|
|
2653 |
foreach ($newfield as $param => $value) {
|
|
|
2654 |
if ($param != "id") {
|
|
|
2655 |
$fieldobject->field->$param = $value;
|
|
|
2656 |
}
|
|
|
2657 |
}
|
|
|
2658 |
unset($fieldobject->field->similarfield);
|
|
|
2659 |
$fieldobject->update_field();
|
|
|
2660 |
unset($fieldobject);
|
|
|
2661 |
} else {
|
|
|
2662 |
/* Make a new field */
|
|
|
2663 |
$filepath = "field/$newfield->type/field.class.php";
|
|
|
2664 |
if (!file_exists($filepath)) {
|
|
|
2665 |
$missingfieldtypes[] = $newfield->name;
|
|
|
2666 |
continue;
|
|
|
2667 |
}
|
|
|
2668 |
include_once($filepath);
|
|
|
2669 |
|
|
|
2670 |
if (!isset($newfield->description)) {
|
|
|
2671 |
$newfield->description = '';
|
|
|
2672 |
}
|
|
|
2673 |
$classname = 'data_field_'.$newfield->type;
|
|
|
2674 |
$fieldclass = new $classname($newfield, $this->module);
|
|
|
2675 |
$fieldclass->insert_field();
|
|
|
2676 |
unset($fieldclass);
|
|
|
2677 |
}
|
|
|
2678 |
}
|
|
|
2679 |
if (!empty($missingfieldtypes)) {
|
|
|
2680 |
echo $OUTPUT->notification(get_string('missingfieldtypeimport', 'data') . html_writer::alist($missingfieldtypes));
|
|
|
2681 |
}
|
|
|
2682 |
}
|
|
|
2683 |
|
|
|
2684 |
/* Get rid of all old unused data */
|
|
|
2685 |
foreach ($currentfields as $cid => $currentfield) {
|
|
|
2686 |
if (!array_key_exists($cid, $preservedfields)) {
|
|
|
2687 |
/* Data not used anymore so wipe! */
|
|
|
2688 |
echo "Deleting field $currentfield->name<br />";
|
|
|
2689 |
|
|
|
2690 |
// Delete all information related to fields.
|
|
|
2691 |
$todelete = data_get_field_from_id($currentfield->id, $this->module);
|
|
|
2692 |
$todelete->delete_field();
|
|
|
2693 |
}
|
|
|
2694 |
}
|
|
|
2695 |
|
|
|
2696 |
// handle special settings here
|
|
|
2697 |
if (!empty($settings->defaultsort)) {
|
|
|
2698 |
if (is_numeric($settings->defaultsort)) {
|
|
|
2699 |
// old broken value
|
|
|
2700 |
$settings->defaultsort = 0;
|
|
|
2701 |
} else {
|
|
|
2702 |
$settings->defaultsort = (int)$DB->get_field('data_fields', 'id', array('dataid'=>$this->module->id, 'name'=>$settings->defaultsort));
|
|
|
2703 |
}
|
|
|
2704 |
} else {
|
|
|
2705 |
$settings->defaultsort = 0;
|
|
|
2706 |
}
|
|
|
2707 |
|
|
|
2708 |
// do we want to overwrite all current database settings?
|
|
|
2709 |
if ($overwritesettings) {
|
|
|
2710 |
// all supported settings
|
|
|
2711 |
$overwrite = array_keys((array)$settings);
|
|
|
2712 |
} else {
|
|
|
2713 |
// only templates and sorting
|
|
|
2714 |
$overwrite = array('singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter',
|
|
|
2715 |
'addtemplate', 'rsstemplate', 'rsstitletemplate', 'csstemplate', 'jstemplate',
|
|
|
2716 |
'asearchtemplate', 'defaultsortdir', 'defaultsort');
|
|
|
2717 |
}
|
|
|
2718 |
|
|
|
2719 |
// now overwrite current data settings
|
|
|
2720 |
foreach ($this->module as $prop=>$unused) {
|
|
|
2721 |
if (in_array($prop, $overwrite)) {
|
|
|
2722 |
$this->module->$prop = $settings->$prop;
|
|
|
2723 |
}
|
|
|
2724 |
}
|
|
|
2725 |
|
|
|
2726 |
data_update_instance($this->module);
|
|
|
2727 |
|
|
|
2728 |
return $this->cleanup();
|
|
|
2729 |
}
|
|
|
2730 |
|
|
|
2731 |
/**
|
|
|
2732 |
* Any clean up routines should go here
|
|
|
2733 |
* @return bool
|
|
|
2734 |
*/
|
|
|
2735 |
public function cleanup() {
|
|
|
2736 |
return true;
|
|
|
2737 |
}
|
|
|
2738 |
}
|
|
|
2739 |
|
|
|
2740 |
/**
|
|
|
2741 |
* Data preset importer for uploaded presets
|
|
|
2742 |
*
|
|
|
2743 |
* @deprecated since Moodle 4.1 MDL-75140 - please do not use this class any more.
|
|
|
2744 |
* @todo MDL-75189 Final deprecation in Moodle 4.5.
|
|
|
2745 |
*/
|
|
|
2746 |
class data_preset_upload_importer extends data_preset_importer {
|
|
|
2747 |
public function __construct($course, $cm, $module, $filepath) {
|
|
|
2748 |
global $USER;
|
|
|
2749 |
|
|
|
2750 |
debugging(
|
|
|
2751 |
'data_preset_upload_importer is deprecated. Please use mod\\data\\local\\importer\\preset_upload_importer instead',
|
|
|
2752 |
DEBUG_DEVELOPER
|
|
|
2753 |
);
|
|
|
2754 |
|
|
|
2755 |
if (is_file($filepath)) {
|
|
|
2756 |
$fp = get_file_packer();
|
|
|
2757 |
if ($fp->extract_to_pathname($filepath, $filepath.'_extracted')) {
|
|
|
2758 |
fulldelete($filepath);
|
|
|
2759 |
}
|
|
|
2760 |
$filepath .= '_extracted';
|
|
|
2761 |
}
|
|
|
2762 |
parent::__construct($course, $cm, $module, $filepath);
|
|
|
2763 |
}
|
|
|
2764 |
|
|
|
2765 |
public function cleanup() {
|
|
|
2766 |
return fulldelete($this->directory);
|
|
|
2767 |
}
|
|
|
2768 |
}
|
|
|
2769 |
|
|
|
2770 |
/**
|
|
|
2771 |
* Data preset importer for existing presets
|
|
|
2772 |
*
|
|
|
2773 |
* @deprecated since Moodle 4.1 MDL-75140 - please do not use this class any more.
|
|
|
2774 |
* @todo MDL-75189 Final deprecation in Moodle 4.5.
|
|
|
2775 |
*/
|
|
|
2776 |
class data_preset_existing_importer extends data_preset_importer {
|
|
|
2777 |
protected $userid;
|
|
|
2778 |
public function __construct($course, $cm, $module, $fullname) {
|
|
|
2779 |
global $USER;
|
|
|
2780 |
|
|
|
2781 |
debugging(
|
|
|
2782 |
'data_preset_existing_importer is deprecated. Please use mod\\data\\local\\importer\\preset_existing_importer instead',
|
|
|
2783 |
DEBUG_DEVELOPER
|
|
|
2784 |
);
|
|
|
2785 |
|
|
|
2786 |
list($userid, $shortname) = explode('/', $fullname, 2);
|
|
|
2787 |
$context = context_module::instance($cm->id);
|
|
|
2788 |
if ($userid && ($userid != $USER->id) && !has_capability('mod/data:manageuserpresets', $context) && !has_capability('mod/data:viewalluserpresets', $context)) {
|
|
|
2789 |
throw new coding_exception('Invalid preset provided');
|
|
|
2790 |
}
|
|
|
2791 |
|
|
|
2792 |
$this->userid = $userid;
|
|
|
2793 |
$filepath = data_preset_path($course, $userid, $shortname);
|
|
|
2794 |
parent::__construct($course, $cm, $module, $filepath);
|
|
|
2795 |
}
|
|
|
2796 |
public function get_userid() {
|
|
|
2797 |
return $this->userid;
|
|
|
2798 |
}
|
|
|
2799 |
}
|
|
|
2800 |
|
|
|
2801 |
/**
|
|
|
2802 |
* @global object
|
|
|
2803 |
* @global object
|
|
|
2804 |
* @param object $course
|
|
|
2805 |
* @param int $userid
|
|
|
2806 |
* @param string $shortname
|
|
|
2807 |
* @return string
|
|
|
2808 |
*/
|
|
|
2809 |
function data_preset_path($course, $userid, $shortname) {
|
|
|
2810 |
global $USER, $CFG;
|
|
|
2811 |
|
|
|
2812 |
$context = context_course::instance($course->id);
|
|
|
2813 |
|
|
|
2814 |
$userid = (int)$userid;
|
|
|
2815 |
|
|
|
2816 |
$path = null;
|
|
|
2817 |
if ($userid > 0 && ($userid == $USER->id || has_capability('mod/data:viewalluserpresets', $context))) {
|
|
|
2818 |
$path = $CFG->dataroot.'/data/preset/'.$userid.'/'.$shortname;
|
|
|
2819 |
} else if ($userid == 0) {
|
|
|
2820 |
$path = $CFG->dirroot.'/mod/data/preset/'.$shortname;
|
|
|
2821 |
} else if ($userid < 0) {
|
|
|
2822 |
$path = $CFG->tempdir.'/data/'.-$userid.'/'.$shortname;
|
|
|
2823 |
}
|
|
|
2824 |
|
|
|
2825 |
return $path;
|
|
|
2826 |
}
|
|
|
2827 |
|
|
|
2828 |
/**
|
|
|
2829 |
* Implementation of the function for printing the form elements that control
|
|
|
2830 |
* whether the course reset functionality affects the data.
|
|
|
2831 |
*
|
|
|
2832 |
* @param MoodleQuickForm $mform form passed by reference
|
|
|
2833 |
*/
|
|
|
2834 |
function data_reset_course_form_definition(&$mform) {
|
|
|
2835 |
$mform->addElement('header', 'dataheader', get_string('modulenameplural', 'data'));
|
|
|
2836 |
$mform->addElement('checkbox', 'reset_data', get_string('deleteallentries','data'));
|
|
|
2837 |
|
|
|
2838 |
$mform->addElement('checkbox', 'reset_data_notenrolled', get_string('deletenotenrolled', 'data'));
|
|
|
2839 |
$mform->disabledIf('reset_data_notenrolled', 'reset_data', 'checked');
|
|
|
2840 |
|
|
|
2841 |
$mform->addElement('checkbox', 'reset_data_ratings', get_string('deleteallratings'));
|
|
|
2842 |
$mform->disabledIf('reset_data_ratings', 'reset_data', 'checked');
|
|
|
2843 |
|
|
|
2844 |
$mform->addElement('checkbox', 'reset_data_comments', get_string('deleteallcomments'));
|
|
|
2845 |
$mform->disabledIf('reset_data_comments', 'reset_data', 'checked');
|
|
|
2846 |
|
|
|
2847 |
$mform->addElement('checkbox', 'reset_data_tags', get_string('removealldatatags', 'data'));
|
|
|
2848 |
$mform->disabledIf('reset_data_tags', 'reset_data', 'checked');
|
|
|
2849 |
}
|
|
|
2850 |
|
|
|
2851 |
/**
|
|
|
2852 |
* Course reset form defaults.
|
|
|
2853 |
* @return array
|
|
|
2854 |
*/
|
|
|
2855 |
function data_reset_course_form_defaults($course) {
|
|
|
2856 |
return array('reset_data'=>0, 'reset_data_ratings'=>1, 'reset_data_comments'=>1, 'reset_data_notenrolled'=>0);
|
|
|
2857 |
}
|
|
|
2858 |
|
|
|
2859 |
/**
|
|
|
2860 |
* Removes all grades from gradebook
|
|
|
2861 |
*
|
|
|
2862 |
* @global object
|
|
|
2863 |
* @global object
|
|
|
2864 |
* @param int $courseid
|
|
|
2865 |
* @param string $type optional type
|
|
|
2866 |
*/
|
|
|
2867 |
function data_reset_gradebook($courseid, $type='') {
|
|
|
2868 |
global $CFG, $DB;
|
|
|
2869 |
|
|
|
2870 |
$sql = "SELECT d.*, cm.idnumber as cmidnumber, d.course as courseid
|
|
|
2871 |
FROM {data} d, {course_modules} cm, {modules} m
|
|
|
2872 |
WHERE m.name='data' AND m.id=cm.module AND cm.instance=d.id AND d.course=?";
|
|
|
2873 |
|
|
|
2874 |
if ($datas = $DB->get_records_sql($sql, array($courseid))) {
|
|
|
2875 |
foreach ($datas as $data) {
|
|
|
2876 |
data_grade_item_update($data, 'reset');
|
|
|
2877 |
}
|
|
|
2878 |
}
|
|
|
2879 |
}
|
|
|
2880 |
|
|
|
2881 |
/**
|
|
|
2882 |
* Actual implementation of the reset course functionality, delete all the
|
|
|
2883 |
* data responses for course $data->courseid.
|
|
|
2884 |
*
|
|
|
2885 |
* @global object
|
|
|
2886 |
* @global object
|
|
|
2887 |
* @param object $data the data submitted from the reset course.
|
|
|
2888 |
* @return array status array
|
|
|
2889 |
*/
|
|
|
2890 |
function data_reset_userdata($data) {
|
|
|
2891 |
global $CFG, $DB;
|
|
|
2892 |
require_once($CFG->libdir.'/filelib.php');
|
|
|
2893 |
require_once($CFG->dirroot.'/rating/lib.php');
|
|
|
2894 |
|
|
|
2895 |
$componentstr = get_string('modulenameplural', 'data');
|
|
|
2896 |
$status = array();
|
|
|
2897 |
|
|
|
2898 |
$allrecordssql = "SELECT r.id
|
|
|
2899 |
FROM {data_records} r
|
|
|
2900 |
INNER JOIN {data} d ON r.dataid = d.id
|
|
|
2901 |
WHERE d.course = ?";
|
|
|
2902 |
|
|
|
2903 |
$alldatassql = "SELECT d.id
|
|
|
2904 |
FROM {data} d
|
|
|
2905 |
WHERE d.course=?";
|
|
|
2906 |
|
|
|
2907 |
$rm = new rating_manager();
|
|
|
2908 |
$ratingdeloptions = new stdClass;
|
|
|
2909 |
$ratingdeloptions->component = 'mod_data';
|
|
|
2910 |
$ratingdeloptions->ratingarea = 'entry';
|
|
|
2911 |
|
|
|
2912 |
// Set the file storage - may need it to remove files later.
|
|
|
2913 |
$fs = get_file_storage();
|
|
|
2914 |
|
|
|
2915 |
// delete entries if requested
|
|
|
2916 |
if (!empty($data->reset_data)) {
|
|
|
2917 |
$DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid));
|
|
|
2918 |
$DB->delete_records_select('data_content', "recordid IN ($allrecordssql)", array($data->courseid));
|
|
|
2919 |
$DB->delete_records_select('data_records', "dataid IN ($alldatassql)", array($data->courseid));
|
|
|
2920 |
|
|
|
2921 |
if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
|
|
|
2922 |
foreach ($datas as $dataid=>$unused) {
|
|
|
2923 |
if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
|
|
|
2924 |
continue;
|
|
|
2925 |
}
|
|
|
2926 |
$datacontext = context_module::instance($cm->id);
|
|
|
2927 |
|
|
|
2928 |
// Delete any files that may exist.
|
|
|
2929 |
$fs->delete_area_files($datacontext->id, 'mod_data', 'content');
|
|
|
2930 |
|
|
|
2931 |
$ratingdeloptions->contextid = $datacontext->id;
|
|
|
2932 |
$rm->delete_ratings($ratingdeloptions);
|
|
|
2933 |
|
|
|
2934 |
core_tag_tag::delete_instances('mod_data', null, $datacontext->id);
|
|
|
2935 |
}
|
|
|
2936 |
}
|
|
|
2937 |
|
|
|
2938 |
if (empty($data->reset_gradebook_grades)) {
|
|
|
2939 |
// remove all grades from gradebook
|
|
|
2940 |
data_reset_gradebook($data->courseid);
|
|
|
2941 |
}
|
|
|
2942 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallentries', 'data'), 'error'=>false);
|
|
|
2943 |
}
|
|
|
2944 |
|
|
|
2945 |
// remove entries by users not enrolled into course
|
|
|
2946 |
if (!empty($data->reset_data_notenrolled)) {
|
|
|
2947 |
$recordssql = "SELECT r.id, r.userid, r.dataid, u.id AS userexists, u.deleted AS userdeleted
|
|
|
2948 |
FROM {data_records} r
|
|
|
2949 |
JOIN {data} d ON r.dataid = d.id
|
|
|
2950 |
LEFT JOIN {user} u ON r.userid = u.id
|
|
|
2951 |
WHERE d.course = ? AND r.userid > 0";
|
|
|
2952 |
|
|
|
2953 |
$course_context = context_course::instance($data->courseid);
|
|
|
2954 |
$notenrolled = array();
|
|
|
2955 |
$fields = array();
|
|
|
2956 |
$rs = $DB->get_recordset_sql($recordssql, array($data->courseid));
|
|
|
2957 |
foreach ($rs as $record) {
|
|
|
2958 |
if (array_key_exists($record->userid, $notenrolled) or !$record->userexists or $record->userdeleted
|
|
|
2959 |
or !is_enrolled($course_context, $record->userid)) {
|
|
|
2960 |
//delete ratings
|
|
|
2961 |
if (!$cm = get_coursemodule_from_instance('data', $record->dataid)) {
|
|
|
2962 |
continue;
|
|
|
2963 |
}
|
|
|
2964 |
$datacontext = context_module::instance($cm->id);
|
|
|
2965 |
$ratingdeloptions->contextid = $datacontext->id;
|
|
|
2966 |
$ratingdeloptions->itemid = $record->id;
|
|
|
2967 |
$rm->delete_ratings($ratingdeloptions);
|
|
|
2968 |
|
|
|
2969 |
// Delete any files that may exist.
|
|
|
2970 |
if ($contents = $DB->get_records('data_content', array('recordid' => $record->id), '', 'id')) {
|
|
|
2971 |
foreach ($contents as $content) {
|
|
|
2972 |
$fs->delete_area_files($datacontext->id, 'mod_data', 'content', $content->id);
|
|
|
2973 |
}
|
|
|
2974 |
}
|
|
|
2975 |
$notenrolled[$record->userid] = true;
|
|
|
2976 |
|
|
|
2977 |
core_tag_tag::remove_all_item_tags('mod_data', 'data_records', $record->id);
|
|
|
2978 |
|
|
|
2979 |
$DB->delete_records('comments', array('itemid' => $record->id, 'commentarea' => 'database_entry'));
|
|
|
2980 |
$DB->delete_records('data_content', array('recordid' => $record->id));
|
|
|
2981 |
$DB->delete_records('data_records', array('id' => $record->id));
|
|
|
2982 |
}
|
|
|
2983 |
}
|
|
|
2984 |
$rs->close();
|
|
|
2985 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotenrolled', 'data'), 'error'=>false);
|
|
|
2986 |
}
|
|
|
2987 |
|
|
|
2988 |
// remove all ratings
|
|
|
2989 |
if (!empty($data->reset_data_ratings)) {
|
|
|
2990 |
if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
|
|
|
2991 |
foreach ($datas as $dataid=>$unused) {
|
|
|
2992 |
if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
|
|
|
2993 |
continue;
|
|
|
2994 |
}
|
|
|
2995 |
$datacontext = context_module::instance($cm->id);
|
|
|
2996 |
|
|
|
2997 |
$ratingdeloptions->contextid = $datacontext->id;
|
|
|
2998 |
$rm->delete_ratings($ratingdeloptions);
|
|
|
2999 |
}
|
|
|
3000 |
}
|
|
|
3001 |
|
|
|
3002 |
if (empty($data->reset_gradebook_grades)) {
|
|
|
3003 |
// remove all grades from gradebook
|
|
|
3004 |
data_reset_gradebook($data->courseid);
|
|
|
3005 |
}
|
|
|
3006 |
|
|
|
3007 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallratings'), 'error'=>false);
|
|
|
3008 |
}
|
|
|
3009 |
|
|
|
3010 |
// remove all comments
|
|
|
3011 |
if (!empty($data->reset_data_comments)) {
|
|
|
3012 |
$DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid));
|
|
|
3013 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallcomments'), 'error'=>false);
|
|
|
3014 |
}
|
|
|
3015 |
|
|
|
3016 |
// Remove all the tags.
|
|
|
3017 |
if (!empty($data->reset_data_tags)) {
|
|
|
3018 |
if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
|
|
|
3019 |
foreach ($datas as $dataid => $unused) {
|
|
|
3020 |
if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
|
|
|
3021 |
continue;
|
|
|
3022 |
}
|
|
|
3023 |
|
|
|
3024 |
$context = context_module::instance($cm->id);
|
|
|
3025 |
core_tag_tag::delete_instances('mod_data', null, $context->id);
|
|
|
3026 |
|
|
|
3027 |
}
|
|
|
3028 |
}
|
|
|
3029 |
$status[] = array('component' => $componentstr, 'item' => get_string('tagsdeleted', 'data'), 'error' => false);
|
|
|
3030 |
}
|
|
|
3031 |
|
|
|
3032 |
// updating dates - shift may be negative too
|
|
|
3033 |
if ($data->timeshift) {
|
|
|
3034 |
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
|
|
|
3035 |
// See MDL-9367.
|
|
|
3036 |
shift_course_mod_dates('data', array('timeavailablefrom', 'timeavailableto',
|
|
|
3037 |
'timeviewfrom', 'timeviewto', 'assesstimestart', 'assesstimefinish'), $data->timeshift, $data->courseid);
|
|
|
3038 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
|
|
|
3039 |
}
|
|
|
3040 |
|
|
|
3041 |
return $status;
|
|
|
3042 |
}
|
|
|
3043 |
|
|
|
3044 |
/**
|
|
|
3045 |
* Returns all other caps used in module
|
|
|
3046 |
*
|
|
|
3047 |
* @return array
|
|
|
3048 |
*/
|
|
|
3049 |
function data_get_extra_capabilities() {
|
|
|
3050 |
return ['moodle/rating:view', 'moodle/rating:viewany', 'moodle/rating:viewall', 'moodle/rating:rate',
|
|
|
3051 |
'moodle/comment:view', 'moodle/comment:post', 'moodle/comment:delete'];
|
|
|
3052 |
}
|
|
|
3053 |
|
|
|
3054 |
/**
|
|
|
3055 |
* @param string $feature FEATURE_xx constant for requested feature
|
|
|
3056 |
* @return mixed True if module supports feature, false if not, null if doesn't know or string for the module purpose.
|
|
|
3057 |
*/
|
|
|
3058 |
function data_supports($feature) {
|
|
|
3059 |
switch($feature) {
|
|
|
3060 |
case FEATURE_GROUPS: return true;
|
|
|
3061 |
case FEATURE_GROUPINGS: return true;
|
|
|
3062 |
case FEATURE_MOD_INTRO: return true;
|
|
|
3063 |
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
|
|
|
3064 |
case FEATURE_COMPLETION_HAS_RULES: return true;
|
|
|
3065 |
case FEATURE_GRADE_HAS_GRADE: return true;
|
|
|
3066 |
case FEATURE_GRADE_OUTCOMES: return true;
|
|
|
3067 |
case FEATURE_RATE: return true;
|
|
|
3068 |
case FEATURE_BACKUP_MOODLE2: return true;
|
|
|
3069 |
case FEATURE_SHOW_DESCRIPTION: return true;
|
|
|
3070 |
case FEATURE_COMMENT: return true;
|
|
|
3071 |
case FEATURE_MOD_PURPOSE: return MOD_PURPOSE_COLLABORATION;
|
|
|
3072 |
|
|
|
3073 |
default: return null;
|
|
|
3074 |
}
|
|
|
3075 |
}
|
|
|
3076 |
|
|
|
3077 |
////////////////////////////////////////////////////////////////////////////////
|
|
|
3078 |
// File API //
|
|
|
3079 |
////////////////////////////////////////////////////////////////////////////////
|
|
|
3080 |
|
|
|
3081 |
/**
|
|
|
3082 |
* Lists all browsable file areas
|
|
|
3083 |
*
|
|
|
3084 |
* @package mod_data
|
|
|
3085 |
* @category files
|
|
|
3086 |
* @param stdClass $course course object
|
|
|
3087 |
* @param stdClass $cm course module object
|
|
|
3088 |
* @param stdClass $context context object
|
|
|
3089 |
* @return array
|
|
|
3090 |
*/
|
|
|
3091 |
function data_get_file_areas($course, $cm, $context) {
|
|
|
3092 |
return array('content' => get_string('areacontent', 'mod_data'));
|
|
|
3093 |
}
|
|
|
3094 |
|
|
|
3095 |
/**
|
|
|
3096 |
* File browsing support for data module.
|
|
|
3097 |
*
|
|
|
3098 |
* @param file_browser $browser
|
|
|
3099 |
* @param array $areas
|
|
|
3100 |
* @param stdClass $course
|
|
|
3101 |
* @param cm_info $cm
|
|
|
3102 |
* @param context $context
|
|
|
3103 |
* @param string $filearea
|
|
|
3104 |
* @param int $itemid
|
|
|
3105 |
* @param string $filepath
|
|
|
3106 |
* @param string $filename
|
|
|
3107 |
* @return file_info_stored file_info_stored instance or null if not found
|
|
|
3108 |
*/
|
|
|
3109 |
function data_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
|
|
|
3110 |
global $CFG, $DB, $USER;
|
|
|
3111 |
|
|
|
3112 |
if ($context->contextlevel != CONTEXT_MODULE) {
|
|
|
3113 |
return null;
|
|
|
3114 |
}
|
|
|
3115 |
|
|
|
3116 |
if (!isset($areas[$filearea])) {
|
|
|
3117 |
return null;
|
|
|
3118 |
}
|
|
|
3119 |
|
|
|
3120 |
if (is_null($itemid)) {
|
|
|
3121 |
require_once($CFG->dirroot.'/mod/data/locallib.php');
|
|
|
3122 |
return new data_file_info_container($browser, $course, $cm, $context, $areas, $filearea);
|
|
|
3123 |
}
|
|
|
3124 |
|
|
|
3125 |
if (!$content = $DB->get_record('data_content', array('id'=>$itemid))) {
|
|
|
3126 |
return null;
|
|
|
3127 |
}
|
|
|
3128 |
|
|
|
3129 |
if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) {
|
|
|
3130 |
return null;
|
|
|
3131 |
}
|
|
|
3132 |
|
|
|
3133 |
if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) {
|
|
|
3134 |
return null;
|
|
|
3135 |
}
|
|
|
3136 |
|
|
|
3137 |
if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) {
|
|
|
3138 |
return null;
|
|
|
3139 |
}
|
|
|
3140 |
|
|
|
3141 |
//check if approved
|
|
|
3142 |
if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) {
|
|
|
3143 |
return null;
|
|
|
3144 |
}
|
|
|
3145 |
|
|
|
3146 |
// group access
|
|
|
3147 |
if ($record->groupid) {
|
|
|
3148 |
$groupmode = groups_get_activity_groupmode($cm, $course);
|
|
|
3149 |
if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
|
|
|
3150 |
if (!groups_is_member($record->groupid)) {
|
|
|
3151 |
return null;
|
|
|
3152 |
}
|
|
|
3153 |
}
|
|
|
3154 |
}
|
|
|
3155 |
|
|
|
3156 |
$fieldobj = data_get_field($field, $data, $cm);
|
|
|
3157 |
|
|
|
3158 |
$filepath = is_null($filepath) ? '/' : $filepath;
|
|
|
3159 |
$filename = is_null($filename) ? '.' : $filename;
|
|
|
3160 |
if (!$fieldobj->file_ok($filepath.$filename)) {
|
|
|
3161 |
return null;
|
|
|
3162 |
}
|
|
|
3163 |
|
|
|
3164 |
$fs = get_file_storage();
|
|
|
3165 |
if (!($storedfile = $fs->get_file($context->id, 'mod_data', $filearea, $itemid, $filepath, $filename))) {
|
|
|
3166 |
return null;
|
|
|
3167 |
}
|
|
|
3168 |
|
|
|
3169 |
// Checks to see if the user can manage files or is the owner.
|
|
|
3170 |
// TODO MDL-33805 - Do not use userid here and move the capability check above.
|
|
|
3171 |
if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) {
|
|
|
3172 |
return null;
|
|
|
3173 |
}
|
|
|
3174 |
|
|
|
3175 |
$urlbase = $CFG->wwwroot.'/pluginfile.php';
|
|
|
3176 |
|
|
|
3177 |
return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false);
|
|
|
3178 |
}
|
|
|
3179 |
|
|
|
3180 |
/**
|
|
|
3181 |
* Serves the data attachments. Implements needed access control ;-)
|
|
|
3182 |
*
|
|
|
3183 |
* @package mod_data
|
|
|
3184 |
* @category files
|
|
|
3185 |
* @param stdClass $course course object
|
|
|
3186 |
* @param stdClass $cm course module object
|
|
|
3187 |
* @param stdClass $context context object
|
|
|
3188 |
* @param string $filearea file area
|
|
|
3189 |
* @param array $args extra arguments
|
|
|
3190 |
* @param bool $forcedownload whether or not force download
|
|
|
3191 |
* @param array $options additional options affecting the file serving
|
|
|
3192 |
* @return bool false if file not found, does not return if found - justsend the file
|
|
|
3193 |
*/
|
|
|
3194 |
function data_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
|
|
|
3195 |
global $CFG, $DB;
|
|
|
3196 |
|
|
|
3197 |
if ($context->contextlevel != CONTEXT_MODULE) {
|
|
|
3198 |
return false;
|
|
|
3199 |
}
|
|
|
3200 |
|
|
|
3201 |
require_course_login($course, true, $cm);
|
|
|
3202 |
|
|
|
3203 |
if ($filearea === 'content') {
|
|
|
3204 |
$contentid = (int)array_shift($args);
|
|
|
3205 |
|
|
|
3206 |
if (!$content = $DB->get_record('data_content', array('id'=>$contentid))) {
|
|
|
3207 |
return false;
|
|
|
3208 |
}
|
|
|
3209 |
|
|
|
3210 |
if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) {
|
|
|
3211 |
return false;
|
|
|
3212 |
}
|
|
|
3213 |
|
|
|
3214 |
if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) {
|
|
|
3215 |
return false;
|
|
|
3216 |
}
|
|
|
3217 |
|
|
|
3218 |
if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) {
|
|
|
3219 |
return false;
|
|
|
3220 |
}
|
|
|
3221 |
|
|
|
3222 |
if ($data->id != $cm->instance) {
|
|
|
3223 |
// hacker attempt - context does not match the contentid
|
|
|
3224 |
return false;
|
|
|
3225 |
}
|
|
|
3226 |
|
|
|
3227 |
//check if approved
|
|
|
3228 |
if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) {
|
|
|
3229 |
return false;
|
|
|
3230 |
}
|
|
|
3231 |
|
|
|
3232 |
// group access
|
|
|
3233 |
if ($record->groupid) {
|
|
|
3234 |
$groupmode = groups_get_activity_groupmode($cm, $course);
|
|
|
3235 |
if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
|
|
|
3236 |
if (!groups_is_member($record->groupid)) {
|
|
|
3237 |
return false;
|
|
|
3238 |
}
|
|
|
3239 |
}
|
|
|
3240 |
}
|
|
|
3241 |
|
|
|
3242 |
$fieldobj = data_get_field($field, $data, $cm);
|
|
|
3243 |
|
|
|
3244 |
$relativepath = implode('/', $args);
|
|
|
3245 |
$fullpath = "/$context->id/mod_data/content/$content->id/$relativepath";
|
|
|
3246 |
|
|
|
3247 |
if (!$fieldobj->file_ok($relativepath)) {
|
|
|
3248 |
return false;
|
|
|
3249 |
}
|
|
|
3250 |
|
|
|
3251 |
$fs = get_file_storage();
|
|
|
3252 |
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
|
|
|
3253 |
return false;
|
|
|
3254 |
}
|
|
|
3255 |
|
|
|
3256 |
// finally send the file
|
|
|
3257 |
send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security!
|
|
|
3258 |
}
|
|
|
3259 |
|
|
|
3260 |
return false;
|
|
|
3261 |
}
|
|
|
3262 |
|
|
|
3263 |
|
|
|
3264 |
function data_extend_navigation($navigation, $course, $module, $cm) {
|
|
|
3265 |
global $CFG, $OUTPUT, $USER, $DB;
|
|
|
3266 |
require_once($CFG->dirroot . '/mod/data/locallib.php');
|
|
|
3267 |
|
|
|
3268 |
$rid = optional_param('rid', 0, PARAM_INT);
|
|
|
3269 |
|
|
|
3270 |
$data = $DB->get_record('data', array('id'=>$cm->instance));
|
|
|
3271 |
$currentgroup = groups_get_activity_group($cm);
|
|
|
3272 |
$groupmode = groups_get_activity_groupmode($cm);
|
|
|
3273 |
|
|
|
3274 |
$numentries = data_numentries($data);
|
|
|
3275 |
$canmanageentries = has_capability('mod/data:manageentries', context_module::instance($cm->id));
|
|
|
3276 |
|
|
|
3277 |
if ($data->entriesleft = data_get_entries_left_to_add($data, $numentries, $canmanageentries)) {
|
|
|
3278 |
$entriesnode = $navigation->add(get_string('entrieslefttoadd', 'data', $data));
|
|
|
3279 |
$entriesnode->add_class('note');
|
|
|
3280 |
}
|
|
|
3281 |
|
|
|
3282 |
$navigation->add(get_string('list', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance)));
|
|
|
3283 |
if (!empty($rid)) {
|
|
|
3284 |
$navigation->add(get_string('single', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'rid'=>$rid)));
|
|
|
3285 |
} else {
|
|
|
3286 |
$navigation->add(get_string('single', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'mode'=>'single')));
|
|
|
3287 |
}
|
|
|
3288 |
$navigation->add(get_string('search', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'mode'=>'asearch')));
|
|
|
3289 |
}
|
|
|
3290 |
|
|
|
3291 |
/**
|
|
|
3292 |
* Adds module specific settings to the settings block
|
|
|
3293 |
*
|
|
|
3294 |
* @param settings_navigation $settings The settings navigation object
|
|
|
3295 |
* @param navigation_node $datanode The node to add module settings to
|
|
|
3296 |
*/
|
|
|
3297 |
function data_extend_settings_navigation(settings_navigation $settings, navigation_node $datanode) {
|
|
|
3298 |
global $DB, $CFG, $USER;
|
|
|
3299 |
|
|
|
3300 |
$data = $DB->get_record('data', array("id" => $settings->get_page()->cm->instance));
|
|
|
3301 |
|
|
|
3302 |
$currentgroup = groups_get_activity_group($settings->get_page()->cm);
|
|
|
3303 |
$groupmode = groups_get_activity_groupmode($settings->get_page()->cm);
|
|
|
3304 |
|
|
|
3305 |
// Took out participation list here!
|
|
|
3306 |
if (data_user_can_add_entry($data, $currentgroup, $groupmode, $settings->get_page()->cm->context)) {
|
|
|
3307 |
if (empty($editentry)) { //TODO: undefined
|
|
|
3308 |
$addstring = get_string('add', 'data');
|
|
|
3309 |
} else {
|
|
|
3310 |
$addstring = get_string('editentry', 'data');
|
|
|
3311 |
}
|
|
|
3312 |
$addentrynode = $datanode->add($addstring,
|
|
|
3313 |
new moodle_url('/mod/data/edit.php', array('d' => $settings->get_page()->cm->instance)));
|
|
|
3314 |
$addentrynode->set_show_in_secondary_navigation(false);
|
|
|
3315 |
}
|
|
|
3316 |
|
|
|
3317 |
if (has_capability(DATA_CAP_EXPORT, $settings->get_page()->cm->context)) {
|
|
|
3318 |
// The capability required to Export database records is centrally defined in 'lib.php'
|
|
|
3319 |
// and should be weaker than those required to edit Templates, Fields and Presets.
|
|
|
3320 |
$exportentriesnode = $datanode->add(get_string('exportentries', 'data'),
|
|
|
3321 |
new moodle_url('/mod/data/export.php', array('d' => $data->id)));
|
|
|
3322 |
$exportentriesnode->set_show_in_secondary_navigation(false);
|
|
|
3323 |
}
|
|
|
3324 |
if (has_capability('mod/data:manageentries', $settings->get_page()->cm->context)) {
|
|
|
3325 |
$importentriesnode = $datanode->add(get_string('importentries', 'data'),
|
|
|
3326 |
new moodle_url('/mod/data/import.php', array('d' => $data->id)));
|
|
|
3327 |
$importentriesnode->set_show_in_secondary_navigation(false);
|
|
|
3328 |
}
|
|
|
3329 |
|
|
|
3330 |
if (has_capability('mod/data:managetemplates', $settings->get_page()->cm->context)) {
|
|
|
3331 |
$currenttab = '';
|
|
|
3332 |
if ($currenttab == 'list') {
|
|
|
3333 |
$defaultemplate = 'listtemplate';
|
|
|
3334 |
} else if ($currenttab == 'add') {
|
|
|
3335 |
$defaultemplate = 'addtemplate';
|
|
|
3336 |
} else if ($currenttab == 'asearch') {
|
|
|
3337 |
$defaultemplate = 'asearchtemplate';
|
|
|
3338 |
} else {
|
|
|
3339 |
$defaultemplate = 'singletemplate';
|
|
|
3340 |
}
|
|
|
3341 |
|
|
|
3342 |
$datanode->add(get_string('presets', 'data'), new moodle_url('/mod/data/preset.php', array('d' => $data->id)));
|
|
|
3343 |
$datanode->add(get_string('fields', 'data'),
|
|
|
3344 |
new moodle_url('/mod/data/field.php', array('d' => $data->id)));
|
|
|
3345 |
$datanode->add(get_string('templates', 'data'),
|
|
|
3346 |
new moodle_url('/mod/data/templates.php', array('d' => $data->id)));
|
|
|
3347 |
}
|
|
|
3348 |
|
|
|
3349 |
if (!empty($CFG->enablerssfeeds) && !empty($CFG->data_enablerssfeeds) && $data->rssarticles > 0) {
|
|
|
3350 |
require_once("$CFG->libdir/rsslib.php");
|
|
|
3351 |
|
|
|
3352 |
$string = get_string('rsstype', 'data');
|
|
|
3353 |
|
|
|
3354 |
$url = new moodle_url(rss_get_url($settings->get_page()->cm->context->id, $USER->id, 'mod_data', $data->id));
|
|
|
3355 |
$datanode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
|
|
|
3356 |
}
|
|
|
3357 |
}
|
|
|
3358 |
|
|
|
3359 |
/**
|
|
|
3360 |
* Save the database configuration as a preset.
|
|
|
3361 |
*
|
|
|
3362 |
* @param stdClass $course The course the database module belongs to.
|
|
|
3363 |
* @param stdClass $cm The course module record
|
|
|
3364 |
* @param stdClass $data The database record
|
|
|
3365 |
* @param string $path
|
|
|
3366 |
* @return bool
|
|
|
3367 |
* @deprecated since Moodle 4.1 MDL-75142 - please, use the preset::save() function instead.
|
|
|
3368 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
3369 |
* @see preset::save()
|
|
|
3370 |
*/
|
|
|
3371 |
function data_presets_save($course, $cm, $data, $path) {
|
|
|
3372 |
debugging('data_presets_save() is deprecated. Please use preset::save() instead.', DEBUG_DEVELOPER);
|
|
|
3373 |
|
|
|
3374 |
$manager = manager::create_from_instance($data);
|
|
|
3375 |
$preset = preset::create_from_instance($manager, $path);
|
|
|
3376 |
return $preset->save();
|
|
|
3377 |
}
|
|
|
3378 |
|
|
|
3379 |
/**
|
|
|
3380 |
* Generates the XML for the database module provided
|
|
|
3381 |
*
|
|
|
3382 |
* @global moodle_database $DB
|
|
|
3383 |
* @param stdClass $course The course the database module belongs to.
|
|
|
3384 |
* @param stdClass $cm The course module record
|
|
|
3385 |
* @param stdClass $data The database record
|
|
|
3386 |
* @return string The XML for the preset
|
|
|
3387 |
* @deprecated since Moodle 4.1 MDL-75142 - please, use the protected preset::generate_preset_xml() function instead.
|
|
|
3388 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
3389 |
* @see preset::generate_preset_xml()
|
|
|
3390 |
*/
|
|
|
3391 |
function data_presets_generate_xml($course, $cm, $data) {
|
|
|
3392 |
debugging(
|
|
|
3393 |
'data_presets_generate_xml() is deprecated. Please use the protected preset::generate_preset_xml() instead.',
|
|
|
3394 |
DEBUG_DEVELOPER
|
|
|
3395 |
);
|
|
|
3396 |
|
|
|
3397 |
$manager = manager::create_from_instance($data);
|
|
|
3398 |
$preset = preset::create_from_instance($manager, $data->name);
|
|
|
3399 |
$reflection = new \ReflectionClass(preset::class);
|
|
|
3400 |
$method = $reflection->getMethod('generate_preset_xml');
|
|
|
3401 |
return $method->invokeArgs($preset, []);
|
|
|
3402 |
}
|
|
|
3403 |
|
|
|
3404 |
/**
|
|
|
3405 |
* Export current fields and presets.
|
|
|
3406 |
*
|
|
|
3407 |
* @param stdClass $course The course the database module belongs to.
|
|
|
3408 |
* @param stdClass $cm The course module record
|
|
|
3409 |
* @param stdClass $data The database record
|
|
|
3410 |
* @param bool $tostorage
|
|
|
3411 |
* @return string the full path to the exported preset file.
|
|
|
3412 |
* @deprecated since Moodle 4.1 MDL-75142 - please, use the preset::export() function instead.
|
|
|
3413 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
3414 |
* @see preset::export()
|
|
|
3415 |
*/
|
|
|
3416 |
function data_presets_export($course, $cm, $data, $tostorage=false) {
|
|
|
3417 |
debugging('data_presets_export() is deprecated. Please use preset::export() instead.', DEBUG_DEVELOPER);
|
|
|
3418 |
|
|
|
3419 |
$manager = manager::create_from_instance($data);
|
|
|
3420 |
$preset = preset::create_from_instance($manager, $data->name);
|
|
|
3421 |
return $preset->export();
|
|
|
3422 |
}
|
|
|
3423 |
|
|
|
3424 |
/**
|
|
|
3425 |
* Running addtional permission check on plugin, for example, plugins
|
|
|
3426 |
* may have switch to turn on/off comments option, this callback will
|
|
|
3427 |
* affect UI display, not like pluginname_comment_validate only throw
|
|
|
3428 |
* exceptions.
|
|
|
3429 |
* Capability check has been done in comment->check_permissions(), we
|
|
|
3430 |
* don't need to do it again here.
|
|
|
3431 |
*
|
|
|
3432 |
* @package mod_data
|
|
|
3433 |
* @category comment
|
|
|
3434 |
*
|
|
|
3435 |
* @param stdClass $comment_param {
|
|
|
3436 |
* context => context the context object
|
|
|
3437 |
* courseid => int course id
|
|
|
3438 |
* cm => stdClass course module object
|
|
|
3439 |
* commentarea => string comment area
|
|
|
3440 |
* itemid => int itemid
|
|
|
3441 |
* }
|
|
|
3442 |
* @return array
|
|
|
3443 |
*/
|
|
|
3444 |
function data_comment_permissions($comment_param) {
|
|
|
3445 |
global $CFG, $DB;
|
|
|
3446 |
if (!$record = $DB->get_record('data_records', array('id'=>$comment_param->itemid))) {
|
|
|
3447 |
throw new comment_exception('invalidcommentitemid');
|
|
|
3448 |
}
|
|
|
3449 |
if (!$data = $DB->get_record('data', array('id'=>$record->dataid))) {
|
|
|
3450 |
throw new comment_exception('invalidid', 'data');
|
|
|
3451 |
}
|
|
|
3452 |
if ($data->comments) {
|
|
|
3453 |
return array('post'=>true, 'view'=>true);
|
|
|
3454 |
} else {
|
|
|
3455 |
return array('post'=>false, 'view'=>false);
|
|
|
3456 |
}
|
|
|
3457 |
}
|
|
|
3458 |
|
|
|
3459 |
/**
|
|
|
3460 |
* Validate comment parameter before perform other comments actions
|
|
|
3461 |
*
|
|
|
3462 |
* @package mod_data
|
|
|
3463 |
* @category comment
|
|
|
3464 |
*
|
|
|
3465 |
* @param stdClass $comment_param {
|
|
|
3466 |
* context => context the context object
|
|
|
3467 |
* courseid => int course id
|
|
|
3468 |
* cm => stdClass course module object
|
|
|
3469 |
* commentarea => string comment area
|
|
|
3470 |
* itemid => int itemid
|
|
|
3471 |
* }
|
|
|
3472 |
* @return boolean
|
|
|
3473 |
*/
|
|
|
3474 |
function data_comment_validate($comment_param) {
|
|
|
3475 |
global $DB;
|
|
|
3476 |
// validate comment area
|
|
|
3477 |
if ($comment_param->commentarea != 'database_entry') {
|
|
|
3478 |
throw new comment_exception('invalidcommentarea');
|
|
|
3479 |
}
|
|
|
3480 |
// validate itemid
|
|
|
3481 |
if (!$record = $DB->get_record('data_records', array('id'=>$comment_param->itemid))) {
|
|
|
3482 |
throw new comment_exception('invalidcommentitemid');
|
|
|
3483 |
}
|
|
|
3484 |
if (!$data = $DB->get_record('data', array('id'=>$record->dataid))) {
|
|
|
3485 |
throw new comment_exception('invalidid', 'data');
|
|
|
3486 |
}
|
|
|
3487 |
if (!$course = $DB->get_record('course', array('id'=>$data->course))) {
|
|
|
3488 |
throw new comment_exception('coursemisconf');
|
|
|
3489 |
}
|
|
|
3490 |
if (!$cm = get_coursemodule_from_instance('data', $data->id, $course->id)) {
|
|
|
3491 |
throw new comment_exception('invalidcoursemodule');
|
|
|
3492 |
}
|
|
|
3493 |
if (!$data->comments) {
|
|
|
3494 |
throw new comment_exception('commentsoff', 'data');
|
|
|
3495 |
}
|
|
|
3496 |
$context = context_module::instance($cm->id);
|
|
|
3497 |
|
|
|
3498 |
//check if approved
|
|
|
3499 |
if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) {
|
|
|
3500 |
throw new comment_exception('notapprovederror', 'data');
|
|
|
3501 |
}
|
|
|
3502 |
|
|
|
3503 |
// group access
|
|
|
3504 |
if ($record->groupid) {
|
|
|
3505 |
$groupmode = groups_get_activity_groupmode($cm, $course);
|
|
|
3506 |
if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
|
|
|
3507 |
if (!groups_is_member($record->groupid)) {
|
|
|
3508 |
throw new comment_exception('notmemberofgroup');
|
|
|
3509 |
}
|
|
|
3510 |
}
|
|
|
3511 |
}
|
|
|
3512 |
// validate context id
|
|
|
3513 |
if ($context->id != $comment_param->context->id) {
|
|
|
3514 |
throw new comment_exception('invalidcontext');
|
|
|
3515 |
}
|
|
|
3516 |
// validation for comment deletion
|
|
|
3517 |
if (!empty($comment_param->commentid)) {
|
|
|
3518 |
if ($comment = $DB->get_record('comments', array('id'=>$comment_param->commentid))) {
|
|
|
3519 |
if ($comment->commentarea != 'database_entry') {
|
|
|
3520 |
throw new comment_exception('invalidcommentarea');
|
|
|
3521 |
}
|
|
|
3522 |
if ($comment->contextid != $comment_param->context->id) {
|
|
|
3523 |
throw new comment_exception('invalidcontext');
|
|
|
3524 |
}
|
|
|
3525 |
if ($comment->itemid != $comment_param->itemid) {
|
|
|
3526 |
throw new comment_exception('invalidcommentitemid');
|
|
|
3527 |
}
|
|
|
3528 |
} else {
|
|
|
3529 |
throw new comment_exception('invalidcommentid');
|
|
|
3530 |
}
|
|
|
3531 |
}
|
|
|
3532 |
return true;
|
|
|
3533 |
}
|
|
|
3534 |
|
|
|
3535 |
/**
|
|
|
3536 |
* Return a list of page types
|
|
|
3537 |
* @param string $pagetype current page type
|
|
|
3538 |
* @param stdClass $parentcontext Block's parent context
|
|
|
3539 |
* @param stdClass $currentcontext Current context of block
|
|
|
3540 |
*/
|
|
|
3541 |
function data_page_type_list($pagetype, $parentcontext, $currentcontext) {
|
|
|
3542 |
$module_pagetype = array('mod-data-*'=>get_string('page-mod-data-x', 'data'));
|
|
|
3543 |
return $module_pagetype;
|
|
|
3544 |
}
|
|
|
3545 |
|
|
|
3546 |
/**
|
|
|
3547 |
* Get all of the record ids from a database activity.
|
|
|
3548 |
*
|
|
|
3549 |
* @param int $dataid The dataid of the database module.
|
|
|
3550 |
* @param object $selectdata Contains an additional sql statement for the
|
|
|
3551 |
* where clause for group and approval fields.
|
|
|
3552 |
* @param array $params Parameters that coincide with the sql statement.
|
|
|
3553 |
* @return array $idarray An array of record ids
|
|
|
3554 |
*/
|
|
|
3555 |
function data_get_all_recordids($dataid, $selectdata = '', $params = null) {
|
|
|
3556 |
global $DB;
|
|
|
3557 |
$initsql = 'SELECT r.id
|
|
|
3558 |
FROM {data_records} r
|
|
|
3559 |
WHERE r.dataid = :dataid';
|
|
|
3560 |
if ($selectdata != '') {
|
|
|
3561 |
$initsql .= $selectdata;
|
|
|
3562 |
$params = array_merge(array('dataid' => $dataid), $params);
|
|
|
3563 |
} else {
|
|
|
3564 |
$params = array('dataid' => $dataid);
|
|
|
3565 |
}
|
|
|
3566 |
$initsql .= ' GROUP BY r.id';
|
|
|
3567 |
$initrecord = $DB->get_recordset_sql($initsql, $params);
|
|
|
3568 |
$idarray = array();
|
|
|
3569 |
foreach ($initrecord as $data) {
|
|
|
3570 |
$idarray[] = $data->id;
|
|
|
3571 |
}
|
|
|
3572 |
// Close the record set and free up resources.
|
|
|
3573 |
$initrecord->close();
|
|
|
3574 |
return $idarray;
|
|
|
3575 |
}
|
|
|
3576 |
|
|
|
3577 |
/**
|
|
|
3578 |
* Get the ids of all the records that match that advanced search criteria
|
|
|
3579 |
* This goes and loops through each criterion one at a time until it either
|
|
|
3580 |
* runs out of records or returns a subset of records.
|
|
|
3581 |
*
|
|
|
3582 |
* @param array $recordids An array of record ids.
|
|
|
3583 |
* @param array $searcharray Contains information for the advanced search criteria
|
|
|
3584 |
* @param int $dataid The data id of the database.
|
|
|
3585 |
* @return array $recordids An array of record ids.
|
|
|
3586 |
*/
|
|
|
3587 |
function data_get_advance_search_ids($recordids, $searcharray, $dataid) {
|
|
|
3588 |
// Check to see if we have any record IDs.
|
|
|
3589 |
if (empty($recordids)) {
|
|
|
3590 |
// Send back an empty search.
|
|
|
3591 |
return array();
|
|
|
3592 |
}
|
|
|
3593 |
$searchcriteria = array_keys($searcharray);
|
|
|
3594 |
// Loop through and reduce the IDs one search criteria at a time.
|
|
|
3595 |
foreach ($searchcriteria as $key) {
|
|
|
3596 |
$recordids = data_get_recordids($key, $searcharray, $dataid, $recordids);
|
|
|
3597 |
// If we don't have anymore IDs then stop.
|
|
|
3598 |
if (!$recordids) {
|
|
|
3599 |
break;
|
|
|
3600 |
}
|
|
|
3601 |
}
|
|
|
3602 |
return $recordids;
|
|
|
3603 |
}
|
|
|
3604 |
|
|
|
3605 |
/**
|
|
|
3606 |
* Gets the record IDs given the search criteria
|
|
|
3607 |
*
|
|
|
3608 |
* @param string $alias Record alias.
|
|
|
3609 |
* @param array $searcharray Criteria for the search.
|
|
|
3610 |
* @param int $dataid Data ID for the database
|
|
|
3611 |
* @param array $recordids An array of record IDs.
|
|
|
3612 |
* @return array $nestarray An arry of record IDs
|
|
|
3613 |
*/
|
|
|
3614 |
function data_get_recordids($alias, $searcharray, $dataid, $recordids) {
|
|
|
3615 |
global $DB;
|
|
|
3616 |
$searchcriteria = $alias; // Keep the criteria.
|
|
|
3617 |
$nestsearch = $searcharray[$alias];
|
|
|
3618 |
// searching for content outside of mdl_data_content
|
|
|
3619 |
if ($alias < 0) {
|
|
|
3620 |
$alias = '';
|
|
|
3621 |
}
|
|
|
3622 |
list($insql, $params) = $DB->get_in_or_equal($recordids, SQL_PARAMS_NAMED);
|
|
|
3623 |
$nestselect = 'SELECT c' . $alias . '.recordid
|
|
|
3624 |
FROM {data_content} c' . $alias . '
|
|
|
3625 |
INNER JOIN {data_fields} f
|
|
|
3626 |
ON f.id = c' . $alias . '.fieldid
|
|
|
3627 |
INNER JOIN {data_records} r
|
|
|
3628 |
ON r.id = c' . $alias . '.recordid
|
|
|
3629 |
INNER JOIN {user} u
|
|
|
3630 |
ON u.id = r.userid ';
|
|
|
3631 |
$nestwhere = 'WHERE r.dataid = :dataid
|
|
|
3632 |
AND c' . $alias .'.recordid ' . $insql . '
|
|
|
3633 |
AND ';
|
|
|
3634 |
|
|
|
3635 |
$params['dataid'] = $dataid;
|
|
|
3636 |
if (count($nestsearch->params) != 0) {
|
|
|
3637 |
$params = array_merge($params, $nestsearch->params);
|
|
|
3638 |
$nestsql = $nestselect . $nestwhere . $nestsearch->sql;
|
|
|
3639 |
} else if ($searchcriteria == DATA_TIMEMODIFIED) {
|
|
|
3640 |
$nestsql = $nestselect . $nestwhere . $nestsearch->field . ' >= :timemodified GROUP BY c' . $alias . '.recordid';
|
|
|
3641 |
$params['timemodified'] = $nestsearch->data;
|
|
|
3642 |
} else if ($searchcriteria == DATA_TAGS) {
|
|
|
3643 |
if (empty($nestsearch->rawtagnames)) {
|
|
|
3644 |
return [];
|
|
|
3645 |
}
|
|
|
3646 |
$i = 0;
|
|
|
3647 |
$tagwhere = [];
|
|
|
3648 |
$tagselect = '';
|
|
|
3649 |
foreach ($nestsearch->rawtagnames as $tagrawname) {
|
|
|
3650 |
$tagselect .= " INNER JOIN {tag_instance} ti_$i
|
|
|
3651 |
ON ti_$i.component = 'mod_data'
|
|
|
3652 |
AND ti_$i.itemtype = 'data_records'
|
|
|
3653 |
AND ti_$i.itemid = r.id
|
|
|
3654 |
INNER JOIN {tag} t_$i
|
|
|
3655 |
ON ti_$i.tagid = t_$i.id ";
|
|
|
3656 |
$tagwhere[] = " t_$i.rawname = :trawname_$i ";
|
|
|
3657 |
$params["trawname_$i"] = $tagrawname;
|
|
|
3658 |
$i++;
|
|
|
3659 |
}
|
|
|
3660 |
$nestsql = $nestselect . $tagselect . $nestwhere . implode(' AND ', $tagwhere);
|
|
|
3661 |
} else { // First name or last name.
|
|
|
3662 |
$thing = $DB->sql_like($nestsearch->field, ':search1', false);
|
|
|
3663 |
$nestsql = $nestselect . $nestwhere . $thing . ' GROUP BY c' . $alias . '.recordid';
|
|
|
3664 |
$params['search1'] = "%$nestsearch->data%";
|
|
|
3665 |
}
|
|
|
3666 |
$nestrecords = $DB->get_recordset_sql($nestsql, $params);
|
|
|
3667 |
$nestarray = array();
|
|
|
3668 |
foreach ($nestrecords as $data) {
|
|
|
3669 |
$nestarray[] = $data->recordid;
|
|
|
3670 |
}
|
|
|
3671 |
// Close the record set and free up resources.
|
|
|
3672 |
$nestrecords->close();
|
|
|
3673 |
return $nestarray;
|
|
|
3674 |
}
|
|
|
3675 |
|
|
|
3676 |
/**
|
|
|
3677 |
* Returns an array with an sql string for advanced searches and the parameters that go with them.
|
|
|
3678 |
*
|
|
|
3679 |
* @param int $sort DATA_*
|
|
|
3680 |
* @param stdClass $data Data module object
|
|
|
3681 |
* @param array $recordids An array of record IDs.
|
|
|
3682 |
* @param string $selectdata Information for the where and select part of the sql statement.
|
|
|
3683 |
* @param string $sortorder Additional sort parameters
|
|
|
3684 |
* @return array sqlselect sqlselect['sql'] has the sql string, sqlselect['params'] contains an array of parameters.
|
|
|
3685 |
*/
|
|
|
3686 |
function data_get_advanced_search_sql($sort, $data, $recordids, $selectdata, $sortorder) {
|
|
|
3687 |
global $DB;
|
|
|
3688 |
|
|
|
3689 |
$userfieldsapi = \core_user\fields::for_userpic()->excluding('id');
|
|
|
3690 |
$namefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
|
|
3691 |
|
|
|
3692 |
if ($sort == 0) {
|
|
|
3693 |
$nestselectsql = 'SELECT r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . '
|
|
|
3694 |
FROM {data_content} c,
|
|
|
3695 |
{data_records} r,
|
|
|
3696 |
{user} u ';
|
|
|
3697 |
$groupsql = ' GROUP BY r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname, ' . $namefields;
|
|
|
3698 |
} else {
|
|
|
3699 |
// Sorting through 'Other' criteria
|
|
|
3700 |
if ($sort <= 0) {
|
|
|
3701 |
switch ($sort) {
|
|
|
3702 |
case DATA_LASTNAME:
|
|
|
3703 |
$sortcontentfull = "u.lastname";
|
|
|
3704 |
break;
|
|
|
3705 |
case DATA_FIRSTNAME:
|
|
|
3706 |
$sortcontentfull = "u.firstname";
|
|
|
3707 |
break;
|
|
|
3708 |
case DATA_APPROVED:
|
|
|
3709 |
$sortcontentfull = "r.approved";
|
|
|
3710 |
break;
|
|
|
3711 |
case DATA_TIMEMODIFIED:
|
|
|
3712 |
$sortcontentfull = "r.timemodified";
|
|
|
3713 |
break;
|
|
|
3714 |
case DATA_TIMEADDED:
|
|
|
3715 |
default:
|
|
|
3716 |
$sortcontentfull = "r.timecreated";
|
|
|
3717 |
}
|
|
|
3718 |
} else {
|
|
|
3719 |
$sortfield = data_get_field_from_id($sort, $data);
|
|
|
3720 |
$sortcontent = $DB->sql_compare_text('c.' . $sortfield->get_sort_field());
|
|
|
3721 |
$sortcontentfull = $sortfield->get_sort_sql($sortcontent);
|
|
|
3722 |
}
|
|
|
3723 |
|
|
|
3724 |
$nestselectsql = 'SELECT r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . ',
|
|
|
3725 |
' . $sortcontentfull . '
|
|
|
3726 |
AS sortorder
|
|
|
3727 |
FROM {data_content} c,
|
|
|
3728 |
{data_records} r,
|
|
|
3729 |
{user} u ';
|
|
|
3730 |
$groupsql = ' GROUP BY r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . ', ' .$sortcontentfull;
|
|
|
3731 |
}
|
|
|
3732 |
|
|
|
3733 |
// Default to a standard Where statement if $selectdata is empty.
|
|
|
3734 |
if ($selectdata == '') {
|
|
|
3735 |
$selectdata = 'WHERE c.recordid = r.id
|
|
|
3736 |
AND r.dataid = :dataid
|
|
|
3737 |
AND r.userid = u.id ';
|
|
|
3738 |
}
|
|
|
3739 |
|
|
|
3740 |
// Find the field we are sorting on
|
|
|
3741 |
if ($sort > 0 or data_get_field_from_id($sort, $data)) {
|
|
|
3742 |
$selectdata .= ' AND c.fieldid = :sort AND s.recordid = r.id';
|
|
|
3743 |
$nestselectsql .= ',{data_content} s ';
|
|
|
3744 |
}
|
|
|
3745 |
|
|
|
3746 |
// If there are no record IDs then return an sql statment that will return no rows.
|
|
|
3747 |
if (count($recordids) != 0) {
|
|
|
3748 |
list($insql, $inparam) = $DB->get_in_or_equal($recordids, SQL_PARAMS_NAMED);
|
|
|
3749 |
} else {
|
|
|
3750 |
list($insql, $inparam) = $DB->get_in_or_equal(array('-1'), SQL_PARAMS_NAMED);
|
|
|
3751 |
}
|
|
|
3752 |
$nestfromsql = $selectdata . ' AND c.recordid ' . $insql . $groupsql;
|
|
|
3753 |
$sqlselect['sql'] = "$nestselectsql $nestfromsql $sortorder";
|
|
|
3754 |
$sqlselect['params'] = $inparam;
|
|
|
3755 |
return $sqlselect;
|
|
|
3756 |
}
|
|
|
3757 |
|
|
|
3758 |
/**
|
|
|
3759 |
* Checks to see if the user has permission to delete the preset.
|
|
|
3760 |
* @param stdClass $context Context object.
|
|
|
3761 |
* @param stdClass $preset The preset object that we are checking for deletion.
|
|
|
3762 |
* @return bool Returns true if the user can delete, otherwise false.
|
|
|
3763 |
* @deprecated since Moodle 4.1 MDL-75187 - please, use the preset::can_manage() function instead.
|
|
|
3764 |
* @todo MDL-75189 This will be deleted in Moodle 4.5.
|
|
|
3765 |
* @see preset::can_manage()
|
|
|
3766 |
*/
|
|
|
3767 |
function data_user_can_delete_preset($context, $preset) {
|
|
|
3768 |
global $USER;
|
|
|
3769 |
|
|
|
3770 |
debugging('data_user_can_delete_preset() is deprecated. Please use manager::can_manage() instead.', DEBUG_DEVELOPER);
|
|
|
3771 |
|
|
|
3772 |
if ($context->contextlevel == CONTEXT_MODULE && isset($preset->name)) {
|
|
|
3773 |
$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
|
|
|
3774 |
$manager = manager::create_from_coursemodule($cm);
|
|
|
3775 |
$todelete = preset::create_from_instance($manager, $preset->name);
|
|
|
3776 |
return $todelete->can_manage();
|
|
|
3777 |
}
|
|
|
3778 |
|
|
|
3779 |
if (has_capability('mod/data:manageuserpresets', $context)) {
|
|
|
3780 |
return true;
|
|
|
3781 |
} else {
|
|
|
3782 |
$candelete = false;
|
|
|
3783 |
$userid = $preset instanceof preset ? $preset->get_userid() : $preset->userid;
|
|
|
3784 |
if ($userid == $USER->id) {
|
|
|
3785 |
$candelete = true;
|
|
|
3786 |
}
|
|
|
3787 |
return $candelete;
|
|
|
3788 |
}
|
|
|
3789 |
}
|
|
|
3790 |
|
|
|
3791 |
/**
|
|
|
3792 |
* Delete a record entry.
|
|
|
3793 |
*
|
|
|
3794 |
* @param int $recordid The ID for the record to be deleted.
|
|
|
3795 |
* @param object $data The data object for this activity.
|
|
|
3796 |
* @param int $courseid ID for the current course (for logging).
|
|
|
3797 |
* @param int $cmid The course module ID.
|
|
|
3798 |
* @return bool True if the record deleted, false if not.
|
|
|
3799 |
*/
|
|
|
3800 |
function data_delete_record($recordid, $data, $courseid, $cmid) {
|
|
|
3801 |
global $DB, $CFG;
|
|
|
3802 |
|
|
|
3803 |
if ($deleterecord = $DB->get_record('data_records', array('id' => $recordid))) {
|
|
|
3804 |
if ($deleterecord->dataid == $data->id) {
|
|
|
3805 |
if ($contents = $DB->get_records('data_content', array('recordid' => $deleterecord->id))) {
|
|
|
3806 |
foreach ($contents as $content) {
|
|
|
3807 |
if ($field = data_get_field_from_id($content->fieldid, $data)) {
|
|
|
3808 |
$field->delete_content($content->recordid);
|
|
|
3809 |
}
|
|
|
3810 |
}
|
|
|
3811 |
$DB->delete_records('data_content', array('recordid'=>$deleterecord->id));
|
|
|
3812 |
$DB->delete_records('data_records', array('id'=>$deleterecord->id));
|
|
|
3813 |
|
|
|
3814 |
// Delete cached RSS feeds.
|
|
|
3815 |
if (!empty($CFG->enablerssfeeds)) {
|
|
|
3816 |
require_once($CFG->dirroot.'/mod/data/rsslib.php');
|
|
|
3817 |
data_rss_delete_file($data);
|
|
|
3818 |
}
|
|
|
3819 |
|
|
|
3820 |
core_tag_tag::remove_all_item_tags('mod_data', 'data_records', $recordid);
|
|
|
3821 |
|
|
|
3822 |
// Trigger an event for deleting this record.
|
|
|
3823 |
$event = \mod_data\event\record_deleted::create(array(
|
|
|
3824 |
'objectid' => $deleterecord->id,
|
|
|
3825 |
'context' => context_module::instance($cmid),
|
|
|
3826 |
'courseid' => $courseid,
|
|
|
3827 |
'other' => array(
|
|
|
3828 |
'dataid' => $deleterecord->dataid
|
|
|
3829 |
)
|
|
|
3830 |
));
|
|
|
3831 |
$event->add_record_snapshot('data_records', $deleterecord);
|
|
|
3832 |
$event->trigger();
|
|
|
3833 |
$course = get_course($courseid);
|
|
|
3834 |
$cm = get_coursemodule_from_instance('data', $data->id, 0, false, MUST_EXIST);
|
|
|
3835 |
data_update_completion_state($data, $course, $cm);
|
|
|
3836 |
|
|
|
3837 |
return true;
|
|
|
3838 |
}
|
|
|
3839 |
}
|
|
|
3840 |
}
|
|
|
3841 |
|
|
|
3842 |
return false;
|
|
|
3843 |
}
|
|
|
3844 |
|
|
|
3845 |
/**
|
|
|
3846 |
* Check for required fields, and build a list of fields to be updated in a
|
|
|
3847 |
* submission.
|
|
|
3848 |
*
|
|
|
3849 |
* @param $mod stdClass The current recordid - provided as an optimisation.
|
|
|
3850 |
* @param $fields array The field data
|
|
|
3851 |
* @param $datarecord stdClass The submitted data.
|
|
|
3852 |
* @return stdClass containing:
|
|
|
3853 |
* * string[] generalnotifications Notifications for the form as a whole.
|
|
|
3854 |
* * string[] fieldnotifications Notifications for a specific field.
|
|
|
3855 |
* * bool validated Whether the field was validated successfully.
|
|
|
3856 |
* * data_field_base[] fields The field objects to be update.
|
|
|
3857 |
*/
|
|
|
3858 |
function data_process_submission(stdClass $mod, $fields, stdClass $datarecord) {
|
|
|
3859 |
$result = new stdClass();
|
|
|
3860 |
|
|
|
3861 |
// Empty form checking - you can't submit an empty form.
|
|
|
3862 |
$emptyform = true;
|
|
|
3863 |
$requiredfieldsfilled = true;
|
|
|
3864 |
$fieldsvalidated = true;
|
|
|
3865 |
|
|
|
3866 |
// Store the notifications.
|
|
|
3867 |
$result->generalnotifications = array();
|
|
|
3868 |
$result->fieldnotifications = array();
|
|
|
3869 |
|
|
|
3870 |
// Store the instantiated classes as an optimisation when processing the result.
|
|
|
3871 |
// This prevents the fields being re-initialised when updating.
|
|
|
3872 |
$result->fields = array();
|
|
|
3873 |
|
|
|
3874 |
$submitteddata = array();
|
|
|
3875 |
foreach ($datarecord as $fieldname => $fieldvalue) {
|
|
|
3876 |
if (strpos($fieldname, '_')) {
|
|
|
3877 |
$namearray = explode('_', $fieldname, 3);
|
|
|
3878 |
$fieldid = $namearray[1];
|
|
|
3879 |
if (!isset($submitteddata[$fieldid])) {
|
|
|
3880 |
$submitteddata[$fieldid] = array();
|
|
|
3881 |
}
|
|
|
3882 |
if (count($namearray) === 2) {
|
|
|
3883 |
$subfieldid = 0;
|
|
|
3884 |
} else {
|
|
|
3885 |
$subfieldid = $namearray[2];
|
|
|
3886 |
}
|
|
|
3887 |
|
|
|
3888 |
$fielddata = new stdClass();
|
|
|
3889 |
$fielddata->fieldname = $fieldname;
|
|
|
3890 |
$fielddata->value = $fieldvalue;
|
|
|
3891 |
$submitteddata[$fieldid][$subfieldid] = $fielddata;
|
|
|
3892 |
}
|
|
|
3893 |
}
|
|
|
3894 |
|
|
|
3895 |
// Check all form fields which have the required are filled.
|
|
|
3896 |
foreach ($fields as $fieldrecord) {
|
|
|
3897 |
// Check whether the field has any data.
|
|
|
3898 |
$fieldhascontent = false;
|
|
|
3899 |
|
|
|
3900 |
$field = data_get_field($fieldrecord, $mod);
|
|
|
3901 |
if (isset($submitteddata[$fieldrecord->id])) {
|
|
|
3902 |
// Field validation check.
|
|
|
3903 |
if (method_exists($field, 'field_validation')) {
|
|
|
3904 |
$errormessage = $field->field_validation($submitteddata[$fieldrecord->id]);
|
|
|
3905 |
if ($errormessage) {
|
|
|
3906 |
$result->fieldnotifications[$field->field->name][] = $errormessage;
|
|
|
3907 |
$fieldsvalidated = false;
|
|
|
3908 |
}
|
|
|
3909 |
}
|
|
|
3910 |
foreach ($submitteddata[$fieldrecord->id] as $fieldname => $value) {
|
|
|
3911 |
if ($field->notemptyfield($value->value, $value->fieldname)) {
|
|
|
3912 |
// The field has content and the form is not empty.
|
|
|
3913 |
$fieldhascontent = true;
|
|
|
3914 |
$emptyform = false;
|
|
|
3915 |
}
|
|
|
3916 |
}
|
|
|
3917 |
}
|
|
|
3918 |
|
|
|
3919 |
// If the field is required, add a notification to that effect.
|
|
|
3920 |
if ($field->field->required && !$fieldhascontent) {
|
|
|
3921 |
if (!isset($result->fieldnotifications[$field->field->name])) {
|
|
|
3922 |
$result->fieldnotifications[$field->field->name] = array();
|
|
|
3923 |
}
|
|
|
3924 |
$result->fieldnotifications[$field->field->name][] = get_string('errormustsupplyvalue', 'data');
|
|
|
3925 |
$requiredfieldsfilled = false;
|
|
|
3926 |
}
|
|
|
3927 |
|
|
|
3928 |
// Update the field.
|
|
|
3929 |
if (isset($submitteddata[$fieldrecord->id])) {
|
|
|
3930 |
foreach ($submitteddata[$fieldrecord->id] as $value) {
|
|
|
3931 |
$result->fields[$value->fieldname] = $field;
|
|
|
3932 |
}
|
|
|
3933 |
}
|
|
|
3934 |
}
|
|
|
3935 |
|
|
|
3936 |
if ($emptyform) {
|
|
|
3937 |
// The form is empty.
|
|
|
3938 |
$result->generalnotifications[] = get_string('emptyaddform', 'data');
|
|
|
3939 |
}
|
|
|
3940 |
|
|
|
3941 |
$result->validated = $requiredfieldsfilled && !$emptyform && $fieldsvalidated;
|
|
|
3942 |
|
|
|
3943 |
return $result;
|
|
|
3944 |
}
|
|
|
3945 |
|
|
|
3946 |
/**
|
|
|
3947 |
* This standard function will check all instances of this module
|
|
|
3948 |
* and make sure there are up-to-date events created for each of them.
|
|
|
3949 |
* If courseid = 0, then every data event in the site is checked, else
|
|
|
3950 |
* only data events belonging to the course specified are checked.
|
|
|
3951 |
* This function is used, in its new format, by restore_refresh_events()
|
|
|
3952 |
*
|
|
|
3953 |
* @param int $courseid
|
|
|
3954 |
* @param int|stdClass $instance Data module instance or ID.
|
|
|
3955 |
* @param int|stdClass $cm Course module object or ID (not used in this module).
|
|
|
3956 |
* @return bool
|
|
|
3957 |
*/
|
|
|
3958 |
function data_refresh_events($courseid = 0, $instance = null, $cm = null) {
|
|
|
3959 |
global $DB, $CFG;
|
|
|
3960 |
require_once($CFG->dirroot.'/mod/data/locallib.php');
|
|
|
3961 |
|
|
|
3962 |
// If we have instance information then we can just update the one event instead of updating all events.
|
|
|
3963 |
if (isset($instance)) {
|
|
|
3964 |
if (!is_object($instance)) {
|
|
|
3965 |
$instance = $DB->get_record('data', array('id' => $instance), '*', MUST_EXIST);
|
|
|
3966 |
}
|
|
|
3967 |
data_set_events($instance);
|
|
|
3968 |
return true;
|
|
|
3969 |
}
|
|
|
3970 |
|
|
|
3971 |
if ($courseid) {
|
|
|
3972 |
if (! $data = $DB->get_records("data", array("course" => $courseid))) {
|
|
|
3973 |
return true;
|
|
|
3974 |
}
|
|
|
3975 |
} else {
|
|
|
3976 |
if (! $data = $DB->get_records("data")) {
|
|
|
3977 |
return true;
|
|
|
3978 |
}
|
|
|
3979 |
}
|
|
|
3980 |
|
|
|
3981 |
foreach ($data as $datum) {
|
|
|
3982 |
data_set_events($datum);
|
|
|
3983 |
}
|
|
|
3984 |
return true;
|
|
|
3985 |
}
|
|
|
3986 |
|
|
|
3987 |
/**
|
|
|
3988 |
* Fetch the configuration for this database activity.
|
|
|
3989 |
*
|
|
|
3990 |
* @param stdClass $database The object returned from the database for this instance
|
|
|
3991 |
* @param string $key The name of the key to retrieve. If none is supplied, then all configuration is returned
|
|
|
3992 |
* @param mixed $default The default value to use if no value was found for the specified key
|
|
|
3993 |
* @return mixed The returned value
|
|
|
3994 |
*/
|
|
|
3995 |
function data_get_config($database, $key = null, $default = null) {
|
|
|
3996 |
if (!empty($database->config)) {
|
|
|
3997 |
$config = json_decode($database->config);
|
|
|
3998 |
} else {
|
|
|
3999 |
$config = new stdClass();
|
|
|
4000 |
}
|
|
|
4001 |
|
|
|
4002 |
if ($key === null) {
|
|
|
4003 |
return $config;
|
|
|
4004 |
}
|
|
|
4005 |
|
|
|
4006 |
if (property_exists($config, $key)) {
|
|
|
4007 |
return $config->$key;
|
|
|
4008 |
}
|
|
|
4009 |
return $default;
|
|
|
4010 |
}
|
|
|
4011 |
|
|
|
4012 |
/**
|
|
|
4013 |
* Update the configuration for this database activity.
|
|
|
4014 |
*
|
|
|
4015 |
* @param stdClass $database The object returned from the database for this instance
|
|
|
4016 |
* @param string $key The name of the key to set
|
|
|
4017 |
* @param mixed $value The value to set for the key
|
|
|
4018 |
*/
|
|
|
4019 |
function data_set_config(&$database, $key, $value) {
|
|
|
4020 |
// Note: We must pass $database by reference because there may be subsequent calls to update_record and these should
|
|
|
4021 |
// not overwrite the configuration just set.
|
|
|
4022 |
global $DB;
|
|
|
4023 |
|
|
|
4024 |
$config = data_get_config($database);
|
|
|
4025 |
|
|
|
4026 |
if (!isset($config->$key) || $config->$key !== $value) {
|
|
|
4027 |
$config->$key = $value;
|
|
|
4028 |
$database->config = json_encode($config);
|
|
|
4029 |
$DB->set_field('data', 'config', $database->config, ['id' => $database->id]);
|
|
|
4030 |
}
|
|
|
4031 |
}
|
|
|
4032 |
/**
|
|
|
4033 |
* Sets the automatic completion state for this database item based on the
|
|
|
4034 |
* count of on its entries.
|
|
|
4035 |
* @since Moodle 3.3
|
|
|
4036 |
* @param object $data The data object for this activity
|
|
|
4037 |
* @param object $course Course
|
|
|
4038 |
* @param object $cm course-module
|
|
|
4039 |
*/
|
|
|
4040 |
function data_update_completion_state($data, $course, $cm) {
|
|
|
4041 |
// If completion option is enabled, evaluate it and return true/false.
|
|
|
4042 |
$completion = new completion_info($course);
|
|
|
4043 |
if ($data->completionentries && $completion->is_enabled($cm)) {
|
|
|
4044 |
$numentries = data_numentries($data);
|
|
|
4045 |
// Check the number of entries required against the number of entries already made.
|
|
|
4046 |
if ($numentries >= $data->completionentries) {
|
|
|
4047 |
$completion->update_state($cm, COMPLETION_COMPLETE);
|
|
|
4048 |
} else {
|
|
|
4049 |
$completion->update_state($cm, COMPLETION_INCOMPLETE);
|
|
|
4050 |
}
|
|
|
4051 |
}
|
|
|
4052 |
}
|
|
|
4053 |
|
|
|
4054 |
/**
|
|
|
4055 |
* Mark the activity completed (if required) and trigger the course_module_viewed event.
|
|
|
4056 |
*
|
|
|
4057 |
* @deprecated since Moodle 4.1 MDL-75146 - please do not use this function any more.
|
|
|
4058 |
* @todo MDL-75189 Final deprecation in Moodle 4.5.
|
|
|
4059 |
* @param stdClass $data data object
|
|
|
4060 |
* @param stdClass $course course object
|
|
|
4061 |
* @param stdClass $cm course module object
|
|
|
4062 |
* @param stdClass $context context object
|
|
|
4063 |
* @since Moodle 3.3
|
|
|
4064 |
*/
|
|
|
4065 |
function data_view($data, $course, $cm, $context) {
|
|
|
4066 |
global $CFG;
|
|
|
4067 |
debugging('data_view is deprecated. Use mod_data\\manager::set_module_viewed instead', DEBUG_DEVELOPER);
|
|
|
4068 |
require_once($CFG->libdir . '/completionlib.php');
|
|
|
4069 |
|
|
|
4070 |
// Trigger course_module_viewed event.
|
|
|
4071 |
$params = array(
|
|
|
4072 |
'context' => $context,
|
|
|
4073 |
'objectid' => $data->id
|
|
|
4074 |
);
|
|
|
4075 |
|
|
|
4076 |
$event = \mod_data\event\course_module_viewed::create($params);
|
|
|
4077 |
$event->add_record_snapshot('course_modules', $cm);
|
|
|
4078 |
$event->add_record_snapshot('course', $course);
|
|
|
4079 |
$event->add_record_snapshot('data', $data);
|
|
|
4080 |
$event->trigger();
|
|
|
4081 |
|
|
|
4082 |
// Completion.
|
|
|
4083 |
$completion = new completion_info($course);
|
|
|
4084 |
$completion->set_module_viewed($cm);
|
|
|
4085 |
}
|
|
|
4086 |
|
|
|
4087 |
/**
|
|
|
4088 |
* Get icon mapping for font-awesome.
|
|
|
4089 |
*/
|
|
|
4090 |
function mod_data_get_fontawesome_icon_map() {
|
|
|
4091 |
return [
|
|
|
4092 |
'mod_data:field/checkbox' => 'fa-check-square-o',
|
|
|
4093 |
'mod_data:field/date' => 'fa-calendar-o',
|
|
|
4094 |
'mod_data:field/file' => 'fa-file',
|
|
|
4095 |
'mod_data:field/latlong' => 'fa-globe',
|
|
|
4096 |
'mod_data:field/menu' => 'fa-bars',
|
|
|
4097 |
'mod_data:field/multimenu' => 'fa-bars',
|
|
|
4098 |
'mod_data:field/number' => 'fa-hashtag',
|
|
|
4099 |
'mod_data:field/picture' => 'fa-picture-o',
|
|
|
4100 |
'mod_data:field/radiobutton' => 'fa-circle-o',
|
|
|
4101 |
'mod_data:field/textarea' => 'fa-font',
|
|
|
4102 |
'mod_data:field/text' => 'fa-i-cursor',
|
|
|
4103 |
'mod_data:field/url' => 'fa-link',
|
|
|
4104 |
];
|
|
|
4105 |
}
|
|
|
4106 |
|
|
|
4107 |
/*
|
|
|
4108 |
* Check if the module has any update that affects the current user since a given time.
|
|
|
4109 |
*
|
|
|
4110 |
* @param cm_info $cm course module data
|
|
|
4111 |
* @param int $from the time to check updates from
|
|
|
4112 |
* @param array $filter if we need to check only specific updates
|
|
|
4113 |
* @return stdClass an object with the different type of areas indicating if they were updated or not
|
|
|
4114 |
* @since Moodle 3.2
|
|
|
4115 |
*/
|
|
|
4116 |
function data_check_updates_since(cm_info $cm, $from, $filter = array()) {
|
|
|
4117 |
global $DB, $CFG;
|
|
|
4118 |
require_once($CFG->dirroot . '/mod/data/locallib.php');
|
|
|
4119 |
|
|
|
4120 |
$updates = course_check_module_updates_since($cm, $from, array(), $filter);
|
|
|
4121 |
|
|
|
4122 |
// Check for new entries.
|
|
|
4123 |
$updates->entries = (object) array('updated' => false);
|
|
|
4124 |
|
|
|
4125 |
$data = $DB->get_record('data', array('id' => $cm->instance), '*', MUST_EXIST);
|
|
|
4126 |
$searcharray = [];
|
|
|
4127 |
$searcharray[DATA_TIMEMODIFIED] = new stdClass();
|
|
|
4128 |
$searcharray[DATA_TIMEMODIFIED]->sql = '';
|
|
|
4129 |
$searcharray[DATA_TIMEMODIFIED]->params = array();
|
|
|
4130 |
$searcharray[DATA_TIMEMODIFIED]->field = 'r.timemodified';
|
|
|
4131 |
$searcharray[DATA_TIMEMODIFIED]->data = $from;
|
|
|
4132 |
|
|
|
4133 |
$currentgroup = groups_get_activity_group($cm);
|
|
|
4134 |
// Teachers should retrieve all entries when not in separate groups.
|
|
|
4135 |
if (has_capability('mod/data:manageentries', $cm->context) && groups_get_activity_groupmode($cm) != SEPARATEGROUPS) {
|
|
|
4136 |
$currentgroup = 0;
|
|
|
4137 |
}
|
|
|
4138 |
list($entries, $maxcount, $totalcount, $page, $nowperpage, $sort, $mode) =
|
|
|
4139 |
data_search_entries($data, $cm, $cm->context, 'list', $currentgroup, '', null, null, 0, 0, true, $searcharray);
|
|
|
4140 |
|
|
|
4141 |
if (!empty($entries)) {
|
|
|
4142 |
$updates->entries->updated = true;
|
|
|
4143 |
$updates->entries->itemids = array_keys($entries);
|
|
|
4144 |
}
|
|
|
4145 |
|
|
|
4146 |
return $updates;
|
|
|
4147 |
}
|
|
|
4148 |
|
|
|
4149 |
/**
|
|
|
4150 |
* This function receives a calendar event and returns the action associated with it, or null if there is none.
|
|
|
4151 |
*
|
|
|
4152 |
* This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
|
|
|
4153 |
* is not displayed on the block.
|
|
|
4154 |
*
|
|
|
4155 |
* @param calendar_event $event
|
|
|
4156 |
* @param \core_calendar\action_factory $factory
|
|
|
4157 |
* @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
|
|
|
4158 |
* @return \core_calendar\local\event\entities\action_interface|null
|
|
|
4159 |
*/
|
|
|
4160 |
function mod_data_core_calendar_provide_event_action(calendar_event $event,
|
|
|
4161 |
\core_calendar\action_factory $factory,
|
|
|
4162 |
int $userid = 0) {
|
|
|
4163 |
global $USER;
|
|
|
4164 |
|
|
|
4165 |
if (!$userid) {
|
|
|
4166 |
$userid = $USER->id;
|
|
|
4167 |
}
|
|
|
4168 |
|
|
|
4169 |
$cm = get_fast_modinfo($event->courseid, $userid)->instances['data'][$event->instance];
|
|
|
4170 |
|
|
|
4171 |
if (!$cm->uservisible) {
|
|
|
4172 |
// The module is not visible to the user for any reason.
|
|
|
4173 |
return null;
|
|
|
4174 |
}
|
|
|
4175 |
|
|
|
4176 |
$now = time();
|
|
|
4177 |
|
|
|
4178 |
if (!empty($cm->customdata['timeavailableto']) && $cm->customdata['timeavailableto'] < $now) {
|
|
|
4179 |
// The module has closed so the user can no longer submit anything.
|
|
|
4180 |
return null;
|
|
|
4181 |
}
|
|
|
4182 |
|
|
|
4183 |
// The module is actionable if we don't have a start time or the start time is
|
|
|
4184 |
// in the past.
|
|
|
4185 |
$actionable = (empty($cm->customdata['timeavailablefrom']) || $cm->customdata['timeavailablefrom'] <= $now);
|
|
|
4186 |
|
|
|
4187 |
return $factory->create_instance(
|
|
|
4188 |
get_string('add', 'data'),
|
|
|
4189 |
new \moodle_url('/mod/data/view.php', array('id' => $cm->id)),
|
|
|
4190 |
1,
|
|
|
4191 |
$actionable
|
|
|
4192 |
);
|
|
|
4193 |
}
|
|
|
4194 |
|
|
|
4195 |
/**
|
|
|
4196 |
* Add a get_coursemodule_info function in case any database type wants to add 'extra' information
|
|
|
4197 |
* for the course (see resource).
|
|
|
4198 |
*
|
|
|
4199 |
* Given a course_module object, this function returns any "extra" information that may be needed
|
|
|
4200 |
* when printing this activity in a course listing. See get_array_of_activities() in course/lib.php.
|
|
|
4201 |
*
|
|
|
4202 |
* @param stdClass $coursemodule The coursemodule object (record).
|
|
|
4203 |
* @return cached_cm_info An object on information that the courses
|
|
|
4204 |
* will know about (most noticeably, an icon).
|
|
|
4205 |
*/
|
|
|
4206 |
function data_get_coursemodule_info($coursemodule) {
|
|
|
4207 |
global $DB;
|
|
|
4208 |
|
|
|
4209 |
$dbparams = ['id' => $coursemodule->instance];
|
|
|
4210 |
$fields = 'id, name, intro, introformat, completionentries, timeavailablefrom, timeavailableto';
|
|
|
4211 |
if (!$data = $DB->get_record('data', $dbparams, $fields)) {
|
|
|
4212 |
return false;
|
|
|
4213 |
}
|
|
|
4214 |
|
|
|
4215 |
$result = new cached_cm_info();
|
|
|
4216 |
$result->name = $data->name;
|
|
|
4217 |
|
|
|
4218 |
if ($coursemodule->showdescription) {
|
|
|
4219 |
// Convert intro to html. Do not filter cached version, filters run at display time.
|
|
|
4220 |
$result->content = format_module_intro('data', $data, $coursemodule->id, false);
|
|
|
4221 |
}
|
|
|
4222 |
|
|
|
4223 |
// Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'.
|
|
|
4224 |
if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) {
|
|
|
4225 |
$result->customdata['customcompletionrules']['completionentries'] = $data->completionentries;
|
|
|
4226 |
}
|
|
|
4227 |
// Other properties that may be used in calendar or on dashboard.
|
|
|
4228 |
if ($data->timeavailablefrom) {
|
|
|
4229 |
$result->customdata['timeavailablefrom'] = $data->timeavailablefrom;
|
|
|
4230 |
}
|
|
|
4231 |
if ($data->timeavailableto) {
|
|
|
4232 |
$result->customdata['timeavailableto'] = $data->timeavailableto;
|
|
|
4233 |
}
|
|
|
4234 |
|
|
|
4235 |
return $result;
|
|
|
4236 |
}
|
|
|
4237 |
|
|
|
4238 |
/**
|
|
|
4239 |
* Callback which returns human-readable strings describing the active completion custom rules for the module instance.
|
|
|
4240 |
*
|
|
|
4241 |
* @param cm_info|stdClass $cm object with fields ->completion and ->customdata['customcompletionrules']
|
|
|
4242 |
* @return array $descriptions the array of descriptions for the custom rules.
|
|
|
4243 |
*/
|
|
|
4244 |
function mod_data_get_completion_active_rule_descriptions($cm) {
|
|
|
4245 |
// Values will be present in cm_info, and we assume these are up to date.
|
|
|
4246 |
if (empty($cm->customdata['customcompletionrules'])
|
|
|
4247 |
|| $cm->completion != COMPLETION_TRACKING_AUTOMATIC) {
|
|
|
4248 |
return [];
|
|
|
4249 |
}
|
|
|
4250 |
|
|
|
4251 |
$descriptions = [];
|
|
|
4252 |
foreach ($cm->customdata['customcompletionrules'] as $key => $val) {
|
|
|
4253 |
switch ($key) {
|
|
|
4254 |
case 'completionentries':
|
|
|
4255 |
if (!empty($val)) {
|
|
|
4256 |
$descriptions[] = get_string('completionentriesdesc', 'data', $val);
|
|
|
4257 |
}
|
|
|
4258 |
break;
|
|
|
4259 |
default:
|
|
|
4260 |
break;
|
|
|
4261 |
}
|
|
|
4262 |
}
|
|
|
4263 |
return $descriptions;
|
|
|
4264 |
}
|
|
|
4265 |
|
|
|
4266 |
/**
|
|
|
4267 |
* This function calculates the minimum and maximum cutoff values for the timestart of
|
|
|
4268 |
* the given event.
|
|
|
4269 |
*
|
|
|
4270 |
* It will return an array with two values, the first being the minimum cutoff value and
|
|
|
4271 |
* the second being the maximum cutoff value. Either or both values can be null, which
|
|
|
4272 |
* indicates there is no minimum or maximum, respectively.
|
|
|
4273 |
*
|
|
|
4274 |
* If a cutoff is required then the function must return an array containing the cutoff
|
|
|
4275 |
* timestamp and error string to display to the user if the cutoff value is violated.
|
|
|
4276 |
*
|
|
|
4277 |
* A minimum and maximum cutoff return value will look like:
|
|
|
4278 |
* [
|
|
|
4279 |
* [1505704373, 'The due date must be after the sbumission start date'],
|
|
|
4280 |
* [1506741172, 'The due date must be before the cutoff date']
|
|
|
4281 |
* ]
|
|
|
4282 |
*
|
|
|
4283 |
* @param calendar_event $event The calendar event to get the time range for
|
|
|
4284 |
* @param stdClass $instance The module instance to get the range from
|
|
|
4285 |
* @return array
|
|
|
4286 |
*/
|
|
|
4287 |
function mod_data_core_calendar_get_valid_event_timestart_range(\calendar_event $event, \stdClass $instance) {
|
|
|
4288 |
$mindate = null;
|
|
|
4289 |
$maxdate = null;
|
|
|
4290 |
|
|
|
4291 |
if ($event->eventtype == DATA_EVENT_TYPE_OPEN) {
|
|
|
4292 |
// The start time of the open event can't be equal to or after the
|
|
|
4293 |
// close time of the database activity.
|
|
|
4294 |
if (!empty($instance->timeavailableto)) {
|
|
|
4295 |
$maxdate = [
|
|
|
4296 |
$instance->timeavailableto,
|
|
|
4297 |
get_string('openafterclose', 'data')
|
|
|
4298 |
];
|
|
|
4299 |
}
|
|
|
4300 |
} else if ($event->eventtype == DATA_EVENT_TYPE_CLOSE) {
|
|
|
4301 |
// The start time of the close event can't be equal to or earlier than the
|
|
|
4302 |
// open time of the database activity.
|
|
|
4303 |
if (!empty($instance->timeavailablefrom)) {
|
|
|
4304 |
$mindate = [
|
|
|
4305 |
$instance->timeavailablefrom,
|
|
|
4306 |
get_string('closebeforeopen', 'data')
|
|
|
4307 |
];
|
|
|
4308 |
}
|
|
|
4309 |
}
|
|
|
4310 |
|
|
|
4311 |
return [$mindate, $maxdate];
|
|
|
4312 |
}
|
|
|
4313 |
|
|
|
4314 |
/**
|
|
|
4315 |
* This function will update the data module according to the
|
|
|
4316 |
* event that has been modified.
|
|
|
4317 |
*
|
|
|
4318 |
* It will set the timeopen or timeclose value of the data instance
|
|
|
4319 |
* according to the type of event provided.
|
|
|
4320 |
*
|
|
|
4321 |
* @throws \moodle_exception
|
|
|
4322 |
* @param \calendar_event $event
|
|
|
4323 |
* @param stdClass $data The module instance to get the range from
|
|
|
4324 |
*/
|
|
|
4325 |
function mod_data_core_calendar_event_timestart_updated(\calendar_event $event, \stdClass $data) {
|
|
|
4326 |
global $DB;
|
|
|
4327 |
|
|
|
4328 |
if (empty($event->instance) || $event->modulename != 'data') {
|
|
|
4329 |
return;
|
|
|
4330 |
}
|
|
|
4331 |
|
|
|
4332 |
if ($event->instance != $data->id) {
|
|
|
4333 |
return;
|
|
|
4334 |
}
|
|
|
4335 |
|
|
|
4336 |
if (!in_array($event->eventtype, [DATA_EVENT_TYPE_OPEN, DATA_EVENT_TYPE_CLOSE])) {
|
|
|
4337 |
return;
|
|
|
4338 |
}
|
|
|
4339 |
|
|
|
4340 |
$courseid = $event->courseid;
|
|
|
4341 |
$modulename = $event->modulename;
|
|
|
4342 |
$instanceid = $event->instance;
|
|
|
4343 |
$modified = false;
|
|
|
4344 |
|
|
|
4345 |
$coursemodule = get_fast_modinfo($courseid)->instances[$modulename][$instanceid];
|
|
|
4346 |
$context = context_module::instance($coursemodule->id);
|
|
|
4347 |
|
|
|
4348 |
// The user does not have the capability to modify this activity.
|
|
|
4349 |
if (!has_capability('moodle/course:manageactivities', $context)) {
|
|
|
4350 |
return;
|
|
|
4351 |
}
|
|
|
4352 |
|
|
|
4353 |
if ($event->eventtype == DATA_EVENT_TYPE_OPEN) {
|
|
|
4354 |
// If the event is for the data activity opening then we should
|
|
|
4355 |
// set the start time of the data activity to be the new start
|
|
|
4356 |
// time of the event.
|
|
|
4357 |
if ($data->timeavailablefrom != $event->timestart) {
|
|
|
4358 |
$data->timeavailablefrom = $event->timestart;
|
|
|
4359 |
$data->timemodified = time();
|
|
|
4360 |
$modified = true;
|
|
|
4361 |
}
|
|
|
4362 |
} else if ($event->eventtype == DATA_EVENT_TYPE_CLOSE) {
|
|
|
4363 |
// If the event is for the data activity closing then we should
|
|
|
4364 |
// set the end time of the data activity to be the new start
|
|
|
4365 |
// time of the event.
|
|
|
4366 |
if ($data->timeavailableto != $event->timestart) {
|
|
|
4367 |
$data->timeavailableto = $event->timestart;
|
|
|
4368 |
$modified = true;
|
|
|
4369 |
}
|
|
|
4370 |
}
|
|
|
4371 |
|
|
|
4372 |
if ($modified) {
|
|
|
4373 |
$data->timemodified = time();
|
|
|
4374 |
$DB->update_record('data', $data);
|
|
|
4375 |
$event = \core\event\course_module_updated::create_from_cm($coursemodule, $context);
|
|
|
4376 |
$event->trigger();
|
|
|
4377 |
}
|
|
|
4378 |
}
|
|
|
4379 |
|
|
|
4380 |
/**
|
|
|
4381 |
* Callback to fetch the activity event type lang string.
|
|
|
4382 |
*
|
|
|
4383 |
* @param string $eventtype The event type.
|
|
|
4384 |
* @return lang_string The event type lang string.
|
|
|
4385 |
*/
|
|
|
4386 |
function mod_data_core_calendar_get_event_action_string(string $eventtype): string {
|
|
|
4387 |
$modulename = get_string('modulename', 'data');
|
|
|
4388 |
|
|
|
4389 |
switch ($eventtype) {
|
|
|
4390 |
case DATA_EVENT_TYPE_OPEN:
|
|
|
4391 |
$identifier = 'calendarstart';
|
|
|
4392 |
break;
|
|
|
4393 |
case DATA_EVENT_TYPE_CLOSE:
|
|
|
4394 |
$identifier = 'calendarend';
|
|
|
4395 |
break;
|
|
|
4396 |
default:
|
|
|
4397 |
return get_string('requiresaction', 'calendar', $modulename);
|
|
|
4398 |
}
|
|
|
4399 |
|
|
|
4400 |
return get_string($identifier, 'data', $modulename);
|
|
|
4401 |
}
|