Proyectos de Subversion Moodle

Rev

Rev 11 | | 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
namespace core_calendar;
18
 
19
use core_calendar\local\event\entities\action_event;
20
use core_calendar\local\event\entities\event;
21
use core_calendar\local\event\entities\event_interface;
22
use core_calendar\local\event\factories\event_factory;
23
use core_calendar\local\event\factories\event_factory_interface;
24
use core_calendar\local\event\mappers\event_mapper;
25
use core_calendar\local\event\mappers\event_mapper_interface;
26
use core_completion\api;
27
 
28
defined('MOODLE_INTERNAL') || die();
29
 
30
global $CFG;
31
 
32
require_once($CFG->dirroot . '/calendar/lib.php');
33
 
34
/**
35
 * Event container test..
36
 *
37
 * @package core_calendar
38
 * @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
39
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1441 ariadna 40
 * @covers \core_calendar\local\event\container
1 efrain 41
 */
1441 ariadna 42
final class container_test extends \advanced_testcase {
1 efrain 43
 
44
    /**
45
     * Test setup.
46
     */
47
    public function setUp(): void {
1441 ariadna 48
        parent::setUp();
1 efrain 49
        $this->resetAfterTest();
50
        $this->setAdminUser();
51
    }
52
 
53
    /**
54
     * Test getting the event factory.
55
     */
11 efrain 56
    public function test_get_event_factory(): void {
1 efrain 57
        $factory = \core_calendar\local\event\container::get_event_factory();
58
 
59
        // Test that the container is returning the right type.
60
        $this->assertInstanceOf(event_factory_interface::class, $factory);
61
        // Test that the container is returning the right implementation.
62
        $this->assertInstanceOf(event_factory::class, $factory);
63
 
64
        // Test that getting the factory a second time returns the same instance.
65
        $factory2 = \core_calendar\local\event\container::get_event_factory();
66
        $this->assertTrue($factory === $factory2);
67
    }
68
 
69
    /**
70
     * Test that the event factory correctly creates instances of events.
71
     *
1441 ariadna 72
     * @dataProvider get_event_factory_testcases
1 efrain 73
     * @param \stdClass $dbrow Row from the "database".
74
     */
11 efrain 75
    public function test_event_factory_create_instance($dbrow): void {
1 efrain 76
        $legacyevent = $this->create_event($dbrow);
77
        $factory = \core_calendar\local\event\container::get_event_factory();
78
        $course = $this->getDataGenerator()->create_course();
79
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
80
        $moduleinstance = $generator->create_instance(['course' => $course->id]);
81
 
82
        // Set some of the fake dbrow properties to match real data in the DB
83
        // this is necessary as the factory hides things that modinfo doesn't
84
        // know about.
85
        $dbrow->id = $legacyevent->id;
86
        $dbrow->courseid = $course->id;
87
        $dbrow->instance = $moduleinstance->id;
88
        $dbrow->modulename = 'assign';
89
        $event = $factory->create_instance($dbrow);
90
 
91
        // Test that the factory is returning the right type.
92
        $this->assertInstanceOf(event_interface::class, $event);
93
        // Test that the factory is returning the right implementation.
94
        $this->assertTrue($event instanceof event || $event instanceof action_event);
95
 
96
        // Test that the event created has the correct properties.
97
        $this->assertEquals($legacyevent->id, $event->get_id());
98
        $this->assertEquals($dbrow->description, $event->get_description()->get_value());
99
        $this->assertEquals($dbrow->format, $event->get_description()->get_format());
100
        $this->assertEquals($dbrow->courseid, $event->get_course()->get('id'));
101
        $this->assertEquals($dbrow->location, $event->get_location());
102
 
103
        if ($dbrow->groupid == 0) {
104
            $this->assertNull($event->get_group());
105
        } else {
106
            $this->assertEquals($dbrow->groupid, $event->get_group()->get('id'));
107
        }
108
 
109
        $this->assertEquals($dbrow->userid, $event->get_user()->get('id'));
110
        $this->assertEquals(null, $event->get_repeats());
111
        $this->assertEquals($dbrow->modulename, $event->get_course_module()->get('modname'));
112
        $this->assertEquals($dbrow->instance, $event->get_course_module()->get('instance'));
113
        $this->assertEquals($dbrow->timestart, $event->get_times()->get_start_time()->getTimestamp());
114
        $this->assertEquals($dbrow->timemodified, $event->get_times()->get_modified_time()->getTimestamp());
115
        $this->assertEquals($dbrow->timesort, $event->get_times()->get_sort_time()->getTimestamp());
116
 
117
        if ($dbrow->visible == 1) {
118
            $this->assertTrue($event->is_visible());
119
        } else {
120
            $this->assertFalse($event->is_visible());
121
        }
122
 
123
        if (!$dbrow->subscriptionid) {
124
            $this->assertNull($event->get_subscription());
125
        } else {
126
            $this->assertEquals($event->get_subscription()->get('id'));
127
        }
128
    }
129
 
130
    /**
131
     * Test that the event factory deals with invisible modules properly as admin.
132
     *
1441 ariadna 133
     * @dataProvider get_event_factory_testcases
1 efrain 134
     * @param \stdClass $dbrow Row from the "database".
135
     */
11 efrain 136
    public function test_event_factory_when_module_visibility_is_toggled_as_admin($dbrow): void {
1 efrain 137
        $legacyevent = $this->create_event($dbrow);
138
        $factory = \core_calendar\local\event\container::get_event_factory();
139
        $course = $this->getDataGenerator()->create_course();
140
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
141
        $moduleinstance = $generator->create_instance(['course' => $course->id]);
142
 
143
        $dbrow->id = $legacyevent->id;
144
        $dbrow->courseid = $course->id;
145
        $dbrow->instance = $moduleinstance->id;
146
        $dbrow->modulename = 'assign';
147
 
148
        set_coursemodule_visible($moduleinstance->cmid, 0);
149
 
150
        $event = $factory->create_instance($dbrow);
151
 
152
        // Test that the factory is returning an event as the admin can see hidden course modules.
153
        $this->assertInstanceOf(event_interface::class, $event);
154
    }
155
 
156
    /**
157
     * Test that the event factory deals with invisible modules properly as a guest.
158
     *
1441 ariadna 159
     * @dataProvider get_event_factory_testcases
1 efrain 160
     * @param \stdClass $dbrow Row from the "database".
161
     */
11 efrain 162
    public function test_event_factory_when_module_visibility_is_toggled_as_guest($dbrow): void {
1 efrain 163
        $legacyevent = $this->create_event($dbrow);
164
        $factory = \core_calendar\local\event\container::get_event_factory();
165
        $course = $this->getDataGenerator()->create_course();
166
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
167
        $moduleinstance = $generator->create_instance(['course' => $course->id]);
168
 
169
        $dbrow->id = $legacyevent->id;
170
        $dbrow->courseid = $course->id;
171
        $dbrow->instance = $moduleinstance->id;
172
        $dbrow->modulename = 'assign';
173
 
174
        set_coursemodule_visible($moduleinstance->cmid, 0);
175
 
176
        // Set to a user who can not view hidden course modules.
177
        $this->setGuestUser();
178
 
179
        $event = $factory->create_instance($dbrow);
180
 
181
        // Module is invisible to guest users so this should return null.
182
        $this->assertNull($event);
183
    }
184
 
185
    /**
186
     * Test that the event factory deals with invisible courses as an admin.
187
     *
1441 ariadna 188
     * @dataProvider get_event_factory_testcases
1 efrain 189
     * @param \stdClass $dbrow Row from the "database".
190
     */
11 efrain 191
    public function test_event_factory_when_course_visibility_is_toggled_as_admin($dbrow): void {
1 efrain 192
        $legacyevent = $this->create_event($dbrow);
193
        $factory = \core_calendar\local\event\container::get_event_factory();
194
 
195
        // Create a hidden course with an assignment.
196
        $course = $this->getDataGenerator()->create_course(['visible' => 0]);
197
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
198
        $moduleinstance = $generator->create_instance(['course' => $course->id]);
199
 
200
        $dbrow->id = $legacyevent->id;
201
        $dbrow->courseid = $course->id;
202
        $dbrow->instance = $moduleinstance->id;
203
        $dbrow->modulename = 'assign';
204
        $event = $factory->create_instance($dbrow);
205
 
206
        // Module is still visible to admins even if the course is invisible.
207
        $this->assertInstanceOf(event_interface::class, $event);
208
    }
209
 
210
    /**
211
     * Test that the event factory deals with invisible courses as a student.
212
     *
1441 ariadna 213
     * @dataProvider get_event_factory_testcases
1 efrain 214
     * @param \stdClass $dbrow Row from the "database".
215
     */
11 efrain 216
    public function test_event_factory_when_course_visibility_is_toggled_as_student($dbrow): void {
1 efrain 217
        $legacyevent = $this->create_event($dbrow);
218
        $factory = \core_calendar\local\event\container::get_event_factory();
219
 
220
        // Create a hidden course with an assignment.
221
        $course = $this->getDataGenerator()->create_course(['visible' => 0]);
222
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
223
        $moduleinstance = $generator->create_instance(['course' => $course->id]);
224
 
225
        // Enrol a student into this course.
226
        $student = $this->getDataGenerator()->create_user();
227
        $this->getDataGenerator()->enrol_user($student->id, $course->id);
228
 
229
        // Set the user to the student.
230
        $this->setUser($student);
231
 
232
        $dbrow->id = $legacyevent->id;
233
        $dbrow->courseid = $course->id;
234
        $dbrow->instance = $moduleinstance->id;
235
        $dbrow->modulename = 'assign';
236
        $event = $factory->create_instance($dbrow);
237
 
238
        // Module is invisible to students if the course is invisible.
239
        $this->assertNull($event);
240
    }
241
 
242
    /**
243
     * Test that the event factory deals with invisible categorys as an admin.
244
     */
11 efrain 245
    public function test_event_factory_when_category_visibility_is_toggled_as_admin(): void {
1 efrain 246
        // Create a hidden category.
247
        $category = $this->getDataGenerator()->create_category(['visible' => 0]);
248
 
249
        $eventdata = [
250
                'categoryid' => $category->id,
251
                'eventtype' => 'category',
252
            ];
253
        $legacyevent = $this->create_event($eventdata);
254
 
255
        $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
256
        $dbrow->id = $legacyevent->id;
257
 
258
        $factory = \core_calendar\local\event\container::get_event_factory();
259
        $event = $factory->create_instance($dbrow);
260
 
261
        // Module is still visible to admins even if the category is invisible.
262
        $this->assertInstanceOf(event_interface::class, $event);
263
    }
264
 
265
    /**
266
     * Test that the event factory deals with invisible categorys as an user.
267
     */
11 efrain 268
    public function test_event_factory_when_category_visibility_is_toggled_as_user(): void {
1 efrain 269
        // Create a hidden category.
270
        $category = $this->getDataGenerator()->create_category(['visible' => 0]);
271
 
272
        $eventdata = [
273
                'categoryid' => $category->id,
274
                'eventtype' => 'category',
275
            ];
276
        $legacyevent = $this->create_event($eventdata);
277
 
278
        $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
279
        $dbrow->id = $legacyevent->id;
280
 
281
        // Use a standard user.
282
        $user = $this->getDataGenerator()->create_user();
283
 
284
        // Set the user to the student.
285
        $this->setUser($user);
286
 
287
        $factory = \core_calendar\local\event\container::get_event_factory();
288
        $event = $factory->create_instance($dbrow);
289
 
290
        // Module is invisible to non-privileged users.
291
        $this->assertNull($event);
292
    }
293
 
294
    /**
295
     * Test that the event factory deals with invisible categorys as an guest.
296
     */
11 efrain 297
    public function test_event_factory_when_category_visibility_is_toggled_as_guest(): void {
1 efrain 298
        // Create a hidden category.
299
        $category = $this->getDataGenerator()->create_category(['visible' => 0]);
300
 
301
        $eventdata = [
302
                'categoryid' => $category->id,
303
                'eventtype' => 'category',
304
            ];
305
        $legacyevent = $this->create_event($eventdata);
306
 
307
        $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
308
        $dbrow->id = $legacyevent->id;
309
 
310
        // Set the user to the student.
311
        $this->setGuestUser();
312
 
313
        $factory = \core_calendar\local\event\container::get_event_factory();
314
        $event = $factory->create_instance($dbrow);
315
 
316
        // Module is invisible to guests.
317
        $this->assertNull($event);
318
    }
319
 
320
    /**
321
     * Test that the event factory deals with completion related events properly.
322
     */
11 efrain 323
    public function test_event_factory_with_completion_related_event(): void {
1 efrain 324
        global $CFG;
325
 
326
        $CFG->enablecompletion = true;
327
 
328
        // Create the course we will be using.
329
        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
330
 
331
        // Add the assignment.
332
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
333
        $assign = $generator->create_instance(array('course' => $course->id), array('completion' => 1));
334
 
335
        // Create a completion event.
336
        $event = new \stdClass();
337
        $event->name = 'An event';
338
        $event->description = 'Event description';
339
        $event->location = 'Event location';
340
        $event->format = FORMAT_HTML;
341
        $event->eventtype = \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED;
342
        $event->userid = 1;
343
        $event->modulename = 'assign';
344
        $event->instance = $assign->id;
345
        $event->categoryid = 0;
346
        $event->courseid = $course->id;
347
        $event->groupid = 0;
348
        $event->timestart = time();
349
        $event->timesort = time();
350
        $event->timemodified = time();
351
        $event->timeduration = 0;
352
        $event->subscriptionid = null;
353
        $event->repeatid = 0;
354
        $legacyevent = $this->create_event($event);
355
 
356
        // Update the id of the event that was created.
357
        $event->id = $legacyevent->id;
358
 
359
        // Create the factory we are going to be testing the behaviour of.
360
        $factory = \core_calendar\local\event\container::get_event_factory();
361
 
362
        // Check that we get the correct instance.
363
        $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
364
 
365
        // Now, disable completion.
366
        $CFG->enablecompletion = false;
367
 
368
        // The result should now be null since we have disabled completion.
369
        $this->assertNull($factory->create_instance($event));
370
    }
371
 
372
    /**
373
     * Checks that completed activities events do not show.
374
     */
11 efrain 375
    public function test_event_factory_with_completed_module_related_event(): void {
1 efrain 376
        global $CFG, $DB;
377
 
378
        $this->setAdminUser();
379
 
380
        // Create a course.
381
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
382
        $user = $this->getDataGenerator()->create_and_enrol($course);
383
        // Create an assign activity with a time set.
384
        $time = time();
385
        $assign = $this->getDataGenerator()->create_module(
386
            'assign', ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
387
 
388
        // Create the event but set it to tomorrow.
389
        $CFG->enablecompletion = true;
390
        api::update_completion_date_event($assign->cmid, 'assign', $assign,
391
            $time + DAYSECS);
392
 
393
        $this->setUser($user);
394
        // Check that we get should be completed event.
395
        $this->assertCount(1, \core_calendar\local\event\container::get_event_vault()->get_events());
396
        // Then Complete the activity.
397
        $completion = new \completion_info($course);
398
        $cmassign = get_coursemodule_from_id('assign', $assign->cmid);
399
        // This should trigger another call to the update_completion_date_event.
400
        $completion->update_state($cmassign, COMPLETION_COMPLETE, $user->id);
401
        // Check that we do not see the event anymore.
402
        $this->assertCount(0, \core_calendar\local\event\container::get_event_vault()->get_events());
403
    }
404
 
405
 
406
    /**
407
     * Test that the event factory only returns an event if the logged in user
408
     * is enrolled in the course.
409
     */
11 efrain 410
    public function test_event_factory_unenrolled_user(): void {
1 efrain 411
        $user = $this->getDataGenerator()->create_user();
412
        // Create the course we will be using.
413
        $course = $this->getDataGenerator()->create_course();
414
 
415
        // Add the assignment.
416
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
417
        $lesson = $generator->create_instance(array('course' => $course->id));
418
 
419
        // Create a user override event for the lesson.
420
        $event = new \stdClass();
421
        $event->name = 'An event';
422
        $event->description = 'Event description';
423
        $event->location = 'Event location';
424
        $event->format = FORMAT_HTML;
425
        $event->eventtype = 'close';
426
        $event->userid = $user->id;
427
        $event->modulename = 'lesson';
428
        $event->instance = $lesson->id;
429
        $event->categoryid = 0;
430
        $event->courseid = $course->id;
431
        $event->groupid = 0;
432
        $event->timestart = time();
433
        $event->timesort = time();
434
        $event->timemodified = time();
435
        $event->timeduration = 0;
436
        $event->subscriptionid = null;
437
        $event->repeatid = 0;
438
        $legacyevent = $this->create_event($event);
439
 
440
        // Update the id of the event that was created.
441
        $event->id = $legacyevent->id;
442
 
443
        // Set the logged in user to the one we created.
444
        $this->setUser($user);
445
 
446
        // Create the factory we are going to be testing the behaviour of.
447
        $factory = \core_calendar\local\event\container::get_event_factory();
448
 
449
        // The result should be null since the user is not enrolled in the
450
        // course the event is for.
451
        $this->assertNull($factory->create_instance($event));
452
 
453
        // Now enrol the user in the course.
454
        $this->getDataGenerator()->enrol_user($user->id, $course->id);
455
 
456
        // Check that we get the correct instance.
457
        $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
458
    }
459
 
460
    /**
461
     * Test that when course module is deleted all events are also deleted.
462
     */
11 efrain 463
    public function test_delete_module_delete_events(): void {
1 efrain 464
        global $DB;
465
        $user = $this->getDataGenerator()->create_user();
466
        // Create the course we will be using.
467
        $course = $this->getDataGenerator()->create_course();
468
        $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
469
 
470
        foreach (\core_component::get_plugin_list('mod') as $modname => $unused) {
471
            try {
472
                $generator = $this->getDataGenerator()->get_plugin_generator('mod_'.$modname);
473
            } catch (\coding_exception $e) {
474
                // Module generator is not implemented.
475
                continue;
476
            }
477
            $module = $generator->create_instance(['course' => $course->id]);
478
 
479
            // Create bunch of events of different type (user override, group override, module event).
480
            $this->create_event(['userid' => $user->id, 'modulename' => $modname, 'instance' => $module->id]);
481
            $this->create_event(['groupid' => $group->id, 'modulename' => $modname, 'instance' => $module->id]);
482
            $this->create_event(['modulename' => $modname, 'instance' => $module->id]);
483
            $this->create_event(['modulename' => $modname, 'instance' => $module->id, 'courseid' => $course->id]);
484
 
485
            // Delete module and make sure all events are deleted.
486
            course_delete_module($module->cmid);
487
            $this->assertEmpty($DB->get_record('event', ['modulename' => $modname, 'instance' => $module->id]));
488
        }
489
    }
490
 
491
    /**
492
     * Test getting the event mapper.
493
     */
11 efrain 494
    public function test_get_event_mapper(): void {
1 efrain 495
        $mapper = \core_calendar\local\event\container::get_event_mapper();
496
 
497
        $this->assertInstanceOf(event_mapper_interface::class, $mapper);
498
        $this->assertInstanceOf(event_mapper::class, $mapper);
499
 
500
        $mapper2 = \core_calendar\local\event\container::get_event_mapper();
501
 
502
        $this->assertTrue($mapper === $mapper2);
503
    }
504
 
505
    /**
506
     * Test cases for the get event factory test.
507
     */
1441 ariadna 508
    public static function get_event_factory_testcases(): array {
1 efrain 509
        return [
510
            'Data set 1' => [
511
                'dbrow' => (object)[
512
                    'name' => 'Test event',
513
                    'description' => 'Hello',
514
                    'format' => 1,
515
                    'categoryid' => 0,
516
                    'courseid' => 1,
517
                    'groupid' => 0,
518
                    'userid' => 1,
519
                    'repeatid' => 0,
520
                    'modulename' => 'assign',
521
                    'instance' => 2,
522
                    'eventtype' => 'due',
523
                    'timestart' => 1486396800,
524
                    'timeduration' => 0,
525
                    'timesort' => 1486396800,
526
                    'visible' => 1,
527
                    'timemodified' => 1485793098,
528
                    'subscriptionid' => null,
529
                    'location' => 'Test location',
530
                ]
531
            ],
532
 
533
            'Data set 2' => [
534
                'dbrow' => (object)[
535
                    'name' => 'Test event',
536
                    'description' => 'Hello',
537
                    'format' => 1,
538
                    'categoryid' => 0,
539
                    'courseid' => 1,
540
                    'groupid' => 1,
541
                    'userid' => 1,
542
                    'repeatid' => 0,
543
                    'modulename' => 'assign',
544
                    'instance' => 2,
545
                    'eventtype' => 'due',
546
                    'timestart' => 1486396800,
547
                    'timeduration' => 0,
548
                    'timesort' => 1486396800,
549
                    'visible' => 1,
550
                    'timemodified' => 1485793098,
551
                    'subscriptionid' => null,
552
                    'location' => 'Test location',
553
                ]
554
            ]
555
        ];
556
    }
557
 
558
    /**
559
     * Helper function to create calendar events using the old code.
560
     *
561
     * @param array $properties A list of calendar event properties to set
562
     * @return calendar_event|bool
563
     */
564
    protected function create_event($properties = []) {
565
        $record = new \stdClass();
566
        $record->name = 'event name';
567
        $record->eventtype = 'site';
568
        $record->timestart = time();
569
        $record->timeduration = 0;
570
        $record->timesort = 0;
571
        $record->type = 1;
572
        $record->courseid = 0;
573
        $record->categoryid = 0;
574
 
575
        foreach ($properties as $name => $value) {
576
            $record->$name = $value;
577
        }
578
 
579
        $event = new \calendar_event($record);
580
        return $event->create($record, false);
581
    }
582
 
583
    /**
584
     * Pad out a basic DB row with basic information.
585
     *
586
     * @param   \stdClass   $skeleton the current skeleton
587
     * @return  \stdClass
588
     */
589
    protected function get_dbrow_from_skeleton($skeleton) {
590
        $dbrow = (object) [
591
            'name' => 'Name',
592
            'description' => 'Description',
593
            'format' => 1,
594
            'categoryid' => 0,
595
            'courseid' => 0,
596
            'groupid' => 0,
597
            'userid' => 0,
598
            'repeatid' => 0,
599
            'modulename' => '',
600
            'instance' => 0,
601
            'eventtype' => 'user',
602
            'timestart' => 1486396800,
603
            'timeduration' => 0,
604
            'timesort' => 1486396800,
605
            'visible' => 1,
606
            'timemodified' => 1485793098,
607
            'subscriptionid' => null,
608
            'location' => 'Test location',
609
        ];
610
 
611
        foreach ((array) $skeleton as $key => $value) {
612
            $dbrow->$key = $value;
613
        }
614
 
615
        return $dbrow;
616
    }
617
}