Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
 * Definition of a grade object class for grade item, grade category etc to inherit from
19
 *
20
 * @package   core_grades
21
 * @category  grade
22
 * @copyright 2006 Nicolas Connault
23
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
/**
29
 * An abstract object that holds methods and attributes common to all grade_* objects defined here.
30
 *
31
 * @package   core_grades
32
 * @category  grade
33
 * @copyright 2006 Nicolas Connault
34
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 */
36
abstract class grade_object {
37
    /**
38
     * The database table this grade object is stored in
39
     * @var string $table
40
     */
41
    public $table;
42
 
43
    /**
44
     * Array of required table fields, must start with 'id'.
45
     * @var array $required_fields
46
     */
47
    public $required_fields = array('id', 'timecreated', 'timemodified', 'hidden');
48
 
49
    /**
50
     * Array of optional fields with default values - usually long text information that is not always needed.
51
     * If you want to create an instance without optional fields use: new grade_object($only_required_fields, false);
52
     * @var array $optional_fields
53
     */
54
    public $optional_fields = array();
55
 
56
    /**
57
     * The PK.
58
     * @var int $id
59
     */
60
    public $id;
61
 
62
    /**
63
     * The first time this grade_object was created.
64
     * @var int $timecreated
65
     */
66
    public $timecreated;
67
 
68
    /**
69
     * The last time this grade_object was modified.
70
     * @var int $timemodified
71
     */
72
    public $timemodified;
73
 
74
    /**
75
     * 0 if visible, 1 always hidden or date not visible until
76
     * @var int $hidden
77
     */
78
    var $hidden = 0;
79
 
80
    /**
81
     * Constructor. Optionally (and by default) attempts to fetch corresponding row from the database
82
     *
83
     * @param array $params An array with required parameters for this grade object.
84
     * @param bool $fetch Whether to fetch corresponding row from the database or not,
85
     *        optional fields might not be defined if false used
86
     */
87
    public function __construct($params=NULL, $fetch=true) {
88
        if (!empty($params) and (is_array($params) or is_object($params))) {
89
            if ($fetch) {
90
                if ($data = $this->fetch($params)) {
91
                    grade_object::set_properties($this, $data);
92
                } else {
93
                    grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
94
                    grade_object::set_properties($this, $params);
95
                }
96
 
97
            } else {
98
                grade_object::set_properties($this, $params);
99
            }
100
 
101
        } else {
102
            grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
103
        }
104
    }
105
 
106
    /**
107
     * Makes sure all the optional fields are loaded.
108
     *
109
     * If id present, meaning the instance exists in the database, then data will be fetched from the database.
110
     * Defaults are used for new instances.
111
     */
112
    public function load_optional_fields() {
113
        global $DB;
114
        foreach ($this->optional_fields as $field=>$default) {
115
            if (property_exists($this, $field)) {
116
                continue;
117
            }
118
            if (empty($this->id)) {
119
                $this->$field = $default;
120
            } else {
121
                $this->$field = $DB->get_field($this->table, $field, array('id', $this->id));
122
            }
123
        }
124
    }
125
 
126
    /**
127
     * Finds and returns a grade_object instance based on params.
128
     *
129
     * @static
130
     * @abstract
131
     * @param array $params associative arrays varname=>value
132
     * @return object grade_object instance or false if none found.
133
     */
134
    public static function fetch($params) {
135
        throw new coding_exception('fetch() method needs to be overridden in each subclass of grade_object');
136
    }
137
 
138
    /**
139
     * Finds and returns all grade_object instances based on $params.
140
     *
141
     * @static
142
     * @abstract
143
     * @throws coding_exception Throws a coding exception if fetch_all() has not been overriden by the grade object subclass
144
     * @param array $params Associative arrays varname=>value
145
     * @return array|bool Array of grade_object instances or false if none found.
146
     */
147
    public static function fetch_all($params) {
148
        throw new coding_exception('fetch_all() method needs to be overridden in each subclass of grade_object');
149
    }
150
 
151
    /**
152
     * Factory method which uses the parameters to retrieve matching instances from the database
153
     *
154
     * @param string $table The table to retrieve from
155
     * @param string $classname The name of the class to instantiate
156
     * @param array $params An array of conditions like $fieldname => $fieldvalue
157
     * @return mixed An object instance or false if not found
158
     */
159
    protected static function fetch_helper($table, $classname, $params) {
160
        if ($instances = grade_object::fetch_all_helper($table, $classname, $params)) {
161
            if (count($instances) > 1) {
162
                // we should not tolerate any errors here - problems might appear later
163
                throw new \moodle_exception('morethanonerecordinfetch', 'debug');
164
            }
165
            return reset($instances);
166
        } else {
167
            return false;
168
        }
169
    }
170
 
171
    /**
172
     * Factory method which uses the parameters to retrieve all matching instances from the database
173
     *
174
     * @param string $table The table to retrieve from
175
     * @param string $classname The name of the class to instantiate
176
     * @param array $params An array of conditions like $fieldname => $fieldvalue
1441 ariadna 177
     * @param string $sortby The SQL sort order. E.g. 'id ASC'
178
     * @return array|bool Array of object instances (sorted by $sortby) or false if not found
1 efrain 179
     */
1441 ariadna 180
    public static function fetch_all_helper($table, $classname, $params, string $sortby = 'id ASC') {
1 efrain 181
        global $DB; // Need to introspect DB here.
182
 
183
        $instance = new $classname();
184
 
185
        $classvars = (array)$instance;
186
        $params    = (array)$params;
187
 
188
        $wheresql = array();
189
        $newparams = array();
190
 
191
        $columns = $DB->get_columns($table); // Cached, no worries.
192
 
193
        foreach ($params as $var=>$value) {
194
            if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) {
195
                continue;
196
            }
197
            if (!array_key_exists($var, $columns)) {
198
                continue;
199
            }
200
            if (is_null($value)) {
201
                $wheresql[] = " $var IS NULL ";
202
            } else {
203
                if ($columns[$var]->meta_type === 'X') {
204
                    // We have a text/clob column, use the cross-db method for its comparison.
205
                    $wheresql[] = ' ' . $DB->sql_compare_text($var) . ' = ' . $DB->sql_compare_text('?') . ' ';
206
                } else {
207
                    // Other columns (varchar, integers...).
208
                    $wheresql[] = " $var = ? ";
209
                }
210
                $newparams[] = $value;
211
            }
212
        }
213
 
214
        if (empty($wheresql)) {
215
            $wheresql = '';
216
        } else {
217
            $wheresql = implode("AND", $wheresql);
218
        }
219
 
220
        global $DB;
1441 ariadna 221
        $rs = $DB->get_recordset_select($table, $wheresql, $newparams, $sortby);
1 efrain 222
        //returning false rather than empty array if nothing found
223
        if (!$rs->valid()) {
224
            $rs->close();
225
            return false;
226
        }
227
 
228
        $result = array();
229
        foreach($rs as $data) {
230
            $instance = new $classname();
231
            grade_object::set_properties($instance, $data);
232
            $result[$instance->id] = $instance;
233
        }
234
        $rs->close();
235
        return $result;
236
    }
237
 
238
    /**
239
     * Updates this object in the Database, based on its object variables. ID must be set.
240
     *
241
     * @param string $source from where was the object updated (mod/forum, manual, etc.)
242
     * @param bool $isbulkupdate If bulk grade update is happening.
243
     * @return bool success
244
     */
245
    public function update($source = null, $isbulkupdate = false) {
246
        global $USER, $CFG, $DB;
247
 
248
        if (empty($this->id)) {
249
            debugging('Can not update grade object, no id!');
250
            return false;
251
        }
252
 
253
        $data = $this->get_record_data();
254
 
255
        $DB->update_record($this->table, $data);
256
 
257
        $historyid = null;
258
        if (empty($CFG->disablegradehistory)) {
259
            unset($data->timecreated);
260
            $data->action       = GRADE_HISTORY_UPDATE;
261
            $data->oldid        = $this->id;
262
            $data->source       = $source;
263
            $data->timemodified = time();
264
            $data->loggeduser   = $USER->id;
265
            $historyid = $DB->insert_record($this->table.'_history', $data);
266
        }
267
 
268
        $this->notify_changed(false, $isbulkupdate);
269
 
270
        $this->update_feedback_files($historyid);
271
 
272
        return true;
273
    }
274
 
275
    /**
276
     * Deletes this object from the database.
277
     *
278
     * @param string $source From where was the object deleted (mod/forum, manual, etc.)
279
     * @return bool success
280
     */
281
    public function delete($source=null) {
282
        global $USER, $CFG, $DB;
283
 
284
        if (empty($this->id)) {
285
            debugging('Can not delete grade object, no id!');
286
            return false;
287
        }
288
 
289
        $data = $this->get_record_data();
290
 
291
        if ($DB->delete_records($this->table, array('id'=>$this->id))) {
292
            if (empty($CFG->disablegradehistory)) {
293
                unset($data->id);
294
                unset($data->timecreated);
295
                $data->action       = GRADE_HISTORY_DELETE;
296
                $data->oldid        = $this->id;
297
                $data->source       = $source;
298
                $data->timemodified = time();
299
                $data->loggeduser   = $USER->id;
300
                $DB->insert_record($this->table.'_history', $data);
301
            }
302
 
303
            $this->notify_changed(true);
304
 
305
            $this->delete_feedback_files();
306
 
307
            return true;
308
        } else {
309
            return false;
310
        }
311
    }
312
 
313
    /**
314
     * Returns object with fields and values that are defined in database
315
     *
316
     * @return stdClass
317
     */
318
    public function get_record_data() {
319
        $data = new stdClass();
320
 
321
        foreach ($this as $var=>$value) {
322
            if (in_array($var, $this->required_fields) or array_key_exists($var, $this->optional_fields)) {
323
                if (is_object($value) or is_array($value)) {
324
                    debugging("Incorrect property '$var' found when inserting grade object");
325
                } else {
326
                    $data->$var = $value;
327
                }
328
            }
329
        }
330
        return $data;
331
    }
332
 
333
    /**
334
     * Records this object in the Database, sets its id to the returned value, and returns that value.
335
     * If successful this function also fetches the new object data from database and stores it
336
     * in object properties.
337
     *
338
     * @param string $source From where was the object inserted (mod/forum, manual, etc.)
339
     * @param string $isbulkupdate If bulk grade update is happening.
340
     * @return int The new grade object ID if successful, false otherwise
341
     */
342
    public function insert($source = null, $isbulkupdate = false) {
343
        global $USER, $CFG, $DB;
344
 
345
        if (!empty($this->id)) {
346
            debugging("Grade object already exists!");
347
            return false;
348
        }
349
 
350
        $data = $this->get_record_data();
351
 
352
        $this->id = $DB->insert_record($this->table, $data);
353
 
354
        // set all object properties from real db data
355
        $this->update_from_db();
356
 
357
        $data = $this->get_record_data();
358
 
359
        $historyid = null;
360
        if (empty($CFG->disablegradehistory)) {
361
            unset($data->timecreated);
362
            $data->action       = GRADE_HISTORY_INSERT;
363
            $data->oldid        = $this->id;
364
            $data->source       = $source;
365
            $data->timemodified = time();
366
            $data->loggeduser   = $USER->id;
367
            $historyid = $DB->insert_record($this->table.'_history', $data);
368
        }
369
 
370
        $this->notify_changed(false, $isbulkupdate);
371
 
372
        $this->add_feedback_files($historyid);
373
 
374
        return $this->id;
375
    }
376
 
377
    /**
378
     * Using this object's id field, fetches the matching record in the DB, and looks at
379
     * each variable in turn. If the DB has different data, the db's data is used to update
380
     * the object. This is different from the update() function, which acts on the DB record
381
     * based on the object.
382
     *
383
     * @return bool True if successful
384
     */
385
    public function update_from_db() {
386
        if (empty($this->id)) {
387
            debugging("The object could not be used in its state to retrieve a matching record from the DB, because its id field is not set.");
388
            return false;
389
        }
390
        global $DB;
391
        if (!$params = $DB->get_record($this->table, array('id' => $this->id))) {
392
            debugging("Object with this id:{$this->id} does not exist in table:{$this->table}, can not update from db!");
393
            return false;
394
        }
395
 
396
        grade_object::set_properties($this, $params);
397
 
398
        return true;
399
    }
400
 
401
    /**
402
     * Given an associated array or object, cycles through each key/variable
403
     * and assigns the value to the corresponding variable in this object.
404
     *
405
     * @param grade_object $instance The object to set the properties on
406
     * @param array $params An array of properties to set like $propertyname => $propertyvalue
407
     * @return array|stdClass Either an associative array or an object containing property name, property value pairs
408
     */
409
    public static function set_properties(&$instance, $params) {
410
        $params = (array) $params;
411
        foreach ($params as $var => $value) {
412
            if (in_array($var, $instance->required_fields) or array_key_exists($var, $instance->optional_fields)) {
413
                $instance->$var = $value;
414
            }
415
        }
416
    }
417
 
418
    /**
419
     * Called immediately after the object data has been inserted, updated, or
420
     * deleted in the database. Default does nothing, can be overridden to
421
     * hook in special behaviour.
422
     *
423
     * @param bool $deleted
424
     */
425
    protected function notify_changed($deleted) {
426
    }
427
 
428
    /**
429
     * Handles adding feedback files in the gradebook.
430
     *
431
     * @param int|null $historyid
432
     */
1441 ariadna 433
    protected function add_feedback_files(?int $historyid = null) {
1 efrain 434
    }
435
 
436
    /**
437
     * Handles updating feedback files in the gradebook.
438
     *
439
     * @param int|null $historyid
440
     */
1441 ariadna 441
    protected function update_feedback_files(?int $historyid = null) {
1 efrain 442
    }
443
 
444
    /**
445
     * Handles deleting feedback files in the gradebook.
446
     */
447
    protected function delete_feedback_files() {
448
    }
449
 
450
    /**
451
     * Returns the current hidden state of this grade_item
452
     *
453
     * This depends on the grade object hidden setting and the current time if hidden is set to a "hidden until" timestamp
454
     *
455
     * @return bool Current hidden state
456
     */
457
    function is_hidden() {
458
        return ($this->hidden == 1 or ($this->hidden != 0 and $this->hidden > time()));
459
    }
460
 
461
    /**
462
     * Check grade object hidden status
463
     *
464
     * @return bool True if a "hidden until" timestamp is set, false if grade object is set to always visible or always hidden.
465
     */
466
    function is_hiddenuntil() {
467
        return $this->hidden > 1;
468
    }
469
 
470
    /**
471
     * Check a grade item hidden status.
472
     *
473
     * @return int 0 means visible, 1 hidden always, a timestamp means "hidden until"
474
     */
475
    function get_hidden() {
476
        return $this->hidden;
477
    }
478
 
479
    /**
480
     * Set a grade object hidden status
481
     *
482
     * @param int $hidden 0 means visiable, 1 means hidden always, a timestamp means "hidden until"
483
     * @param bool $cascade Ignored
484
     */
485
    function set_hidden($hidden, $cascade=false) {
486
        $this->hidden = $hidden;
487
        $this->update();
488
    }
489
 
490
    /**
491
     * Returns whether the grade object can control the visibility of the grades.
492
     *
493
     * @return bool
494
     */
495
    public function can_control_visibility() {
496
        return true;
497
    }
498
}