Skip to content

Commit 4cc8e25

Browse files
committed
Issue #93: Add support for customfield backup/restore
1 parent 9eb38ef commit 4cc8e25

11 files changed

+394
-20
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Using git submodule:
1616
git submodule add [email protected]:catalyst/moodle-mod_cms.git mod/cms
1717
```
1818

19-
OR you can download as a zip from github
19+
Or you can download as a zip from github
2020

2121
https://github.com/catalyst/moodle-mod_cms/archive/refs/heads/main.zip
2222

classes/customfield/cmsfield_handler.php

+12-7
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class cmsfield_handler extends handler {
3232
use cms_restore;
3333

3434
/**
35-
* Context that should be used for new categories created by this handler
35+
* Context that should be used for new categories created by this handler.
3636
*
3737
* @return \context
3838
*/
@@ -51,13 +51,18 @@ public function get_configuration_url(): \moodle_url {
5151
}
5252

5353
/**
54-
* Context that should be used for data stored for the given record
54+
* Context that should be used for data stored for the given record.
55+
* Uses the activity context.
5556
*
5657
* @param int $instanceid id of the instance or 0 if the instance is being created
5758
* @return \context
5859
*/
5960
public function get_instance_context(int $instanceid = 0): \context {
60-
return $this->get_configuration_context();
61+
$modinfo = get_coursemodule_from_instance('cms', $instanceid);
62+
if ($modinfo) {
63+
return \context_module::instance($modinfo->id);
64+
}
65+
return \context_system::instance();
6166
}
6267

6368
/**
@@ -70,11 +75,11 @@ public function can_configure(): bool {
7075
}
7176

7277
/**
73-
* The current user can edit given custom fields on the given instance
78+
* The current user can edit given custom fields on the given instance.
7479
*
75-
* Called to filter list of fields displayed on the instance edit form
80+
* Called to filter list of fields displayed on the instance edit form.
7681
*
77-
* Capability to edit/create instance is checked separately
82+
* Capability to edit/create instance is checked separately.
7883
*
7984
* @param field_controller $field
8085
* @param int $instanceid id of the instance or 0 if the instance is being created
@@ -85,7 +90,7 @@ public function can_edit(field_controller $field, int $instanceid = 0): bool {
8590
}
8691

8792
/**
88-
* The current user can view the value of the custom field for a given custom field and instance
93+
* The current user can view the value of the custom field for a given custom field and instance.
8994
*
9095
* Called to filter list of fields returned by methods get_instance_data(), get_instances_data(),
9196
* export_instance_data(), export_instance_data_object()

classes/customfield/cmsuserlist_handler.php

+18-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
namespace mod_cms\customfield;
1818

1919
use core_customfield\{field_controller, handler};
20+
use mod_cms\local\model\cms;
2021
use mod_cms\local\model\cms_types;
2122
use mod_cms\local\datasource\userlist;
2223

@@ -31,8 +32,11 @@
3132
class cmsuserlist_handler extends handler {
3233
use cms_restore;
3334

35+
/** @var cms The CMS for the fields. */
36+
public $cms = null;
37+
3438
/**
35-
* Context that should be used for new categories created by this handler
39+
* Context that should be used for new categories created by this handler.
3640
*
3741
* @return \context
3842
*/
@@ -41,13 +45,20 @@ public function get_configuration_context(): \context {
4145
}
4246

4347
/**
44-
* Context that should be used for data stored for the given record
48+
* Context that should be used for data stored for the given record.
49+
* Uses the activity context.
4550
*
4651
* @param int $instanceid id of the instance or 0 if the instance is being created
4752
* @return \context
4853
*/
4954
public function get_instance_context(int $instanceid = 0): \context {
50-
return $this->get_configuration_context();
55+
if (!is_null($this->cms)) {
56+
$modinfo = get_coursemodule_from_instance('cms', $this->cms->get('id'));
57+
if ($modinfo) {
58+
return \context_module::instance($modinfo->id);
59+
}
60+
}
61+
return \context_system::instance();
5162
}
5263

5364
/**
@@ -78,11 +89,11 @@ public function can_configure(): bool {
7889
}
7990

8091
/**
81-
* The current user can edit given custom fields on the given instance
92+
* The current user can edit given custom fields on the given instance.
8293
*
83-
* Called to filter list of fields displayed on the instance edit form
94+
* Called to filter list of fields displayed on the instance edit form.
8495
*
85-
* Capability to edit/create instance is checked separately
96+
* Capability to edit/create instance is checked separately.
8697
*
8798
* @param field_controller $field
8899
* @param int $instanceid id of the instance or 0 if the instance is being created
@@ -93,7 +104,7 @@ public function can_edit(field_controller $field, int $instanceid = 0): bool {
93104
}
94105

95106
/**
96-
* The current user can view the value of the custom field for a given custom field and instance
107+
* The current user can view the value of the custom field for a given custom field and instance.
97108
*
98109
* Called to filter list of fields returned by methods get_instance_data(), get_instances_data(),
99110
* export_instance_data(), export_instance_data_object()

classes/local/datasource/fields.php

+4
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ public function instance_backup_define_structure(\backup_nested_element $parent)
252252
$fields->add_child($field);
253253

254254
$fieldsforbackup = $this->cfhandler->get_instance_data_for_backup($this->cms->get('id'));
255+
// Backup annotations. Check for function existence for the sake of backward compatibility.
256+
if (method_exists($this->cfhandler, 'backup_define_structure')) {
257+
$this->cfhandler->backup_define_structure($this->cms->get('id'), $field);
258+
}
255259
$field->set_source_array($fieldsforbackup);
256260
}
257261

classes/local/datasource/restore/fields.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ public function process_restore_ds_fields(array $data) {
5555

5656
// Add data to instance.
5757
$handler = cmsfield_handler::create($cms->get('typeid'));
58-
$handler->cms_restore_instance_data_from_backup($this->stepslib->get_task(), $data, $cmsid);
58+
$newid = $handler->cms_restore_instance_data_from_backup($this->stepslib->get_task(), $data, $cmsid);
59+
60+
if ($newid && method_exists($handler, 'restore_define_structure')) {
61+
$handler->restore_define_structure($this->stepslib, $newid, $data['id']);
62+
}
5963
}
6064

6165
/**

classes/local/datasource/restore/userlist.php

+4
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ public function process_restore_ds_userlist_field(array $data) {
8080
$cmsid = $this->stepslib->get_new_parentid('cms');
8181
$cms = new cms($cmsid);
8282
$handler = cmsuserlist_handler::create($cms->get('typeid'));
83+
$handler->cms = $cms;
8384
$newid = $handler->cms_restore_instance_data_from_backup($this->stepslib->get_task(), $data, $rowid);
85+
if ($newid && method_exists($handler, 'restore_define_structure')) {
86+
$handler->restore_define_structure($this->stepslib, $newid, $data['id']);
87+
}
8488

8589
$this->stepslib->set_mapping('cms_userlist_field', $oldid, $newid, true, $handler->get_instance_context()->id);
8690
}

classes/local/datasource/userlist.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public static function get_displayname(): string {
6767
public function __construct(cms $cms) {
6868
parent::__construct($cms);
6969
$this->cfhandler = cmsuserlist_handler::create($this->cms->get('typeid'));
70+
$this->cfhandler->cms = $cms;
7071
}
7172

7273
/**
@@ -462,7 +463,7 @@ public function get_element_names(field_controller $columndef, int $rownum = 0):
462463
* @param int $rownum
463464
* @return int
464465
*/
465-
protected function get_instance_id(int $rownum): int {
466+
public function get_instance_id(int $rownum): int {
466467
$ids = $this->get_instance_ids();
467468
if (!isset($ids[$rownum])) {
468469
$ids[$rownum] = $this->get_new_row_id();
@@ -541,6 +542,10 @@ function ($arr) {
541542
)
542543
];
543544
$fieldsforbackup = array_merge($fieldsforbackup, $this->cfhandler->get_instance_data_for_backup($id));
545+
// Backup annotations. Check for function existence for the sake of backward compatibility.
546+
if (method_exists($this->cfhandler, 'backup_define_structure')) {
547+
$this->cfhandler->backup_define_structure($id, $field);
548+
}
544549
}
545550
$row->set_source_array($instanceids);
546551
$field->set_source_array($fieldsforbackup);

classes/local/lib.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,20 @@ public static function get_course_content_items(content_item $defaultmoduleconte
9696
* @return int The ID of the newly crated instance.
9797
*/
9898
public static function add_instance(\stdClass $instancedata, $mform = null): int {
99+
global $DB;
100+
99101
$cms = new cms();
100102
$cms->set('typeid', $instancedata->typeid);
101103
$cms->set('intro', '');
102104
$cms->set('course', $instancedata->course);
103105
$cms->set('name', '');
104106
$cms->save();
105-
106107
$instancedata->id = $cms->get('id');
108+
109+
// We must do this here before updating the datasources, otherwise customfield handler will not be able to find the right
110+
// context.
111+
$DB->set_field('course_modules', 'instance', $instancedata->id, ['id' => $instancedata->coursemodule]);
112+
107113
foreach (dsbase::get_datasources($cms) as $ds) {
108114
$ds->update_instance($instancedata, true);
109115
}

tests/datasource_fields_test.php

+85
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
namespace mod_cms;
1818

19+
use context_module;
1920
use core_customfield\{category_controller, field_controller};
2021
use mod_cms\customfield\cmsfield_handler;
2122
use mod_cms\local\datasource\fields as dsfields;
2223
use mod_cms\local\model\cms;
2324
use mod_cms\local\model\cms_types;
25+
use mod_cms_generator;
2426

2527
defined('MOODLE_INTERNAL') || die();
2628

@@ -51,6 +53,14 @@ protected function setUp(): void {
5153
$this->setAdminUser();
5254
}
5355

56+
/**
57+
* Get generator for mod_cms.
58+
* @return mod_cms_generator
59+
*/
60+
protected function get_generator(): mod_cms_generator {
61+
return $this->getDataGenerator()->get_plugin_generator('mod_cms');
62+
}
63+
5464
/**
5565
* Tests short name.
5666
*
@@ -335,4 +345,79 @@ public function test_duplicate() {
335345
// Assert that the CMS type is not duplicated.
336346
$this->assertEquals($cms->get('typeid'), $newcms->get('typeid'));
337347
}
348+
349+
/**
350+
* Tests backup and restore of embedded files in textarea fields.
351+
*
352+
* @covers \mod_cms\local\datasource\fields::instance_backup_define_structure
353+
* @covers \mod_cms\local\datasource\fields::restore_define_structure
354+
*/
355+
public function test_file_backup_and_restore() {
356+
if (!method_exists('\core_customfield\handler', 'backup_define_structure')) {
357+
$this->markTestSkipped('Only test if backup and restore is supported for embedded files.');
358+
}
359+
360+
$filename = 'somefilename.txt';
361+
362+
$course = $this->getDataGenerator()->create_course();
363+
$cmstype = $this->get_generator()->create_cms_type(['datasources' => 'fields']);
364+
$category = $this->get_generator()->create_datasource_fields_category($cmstype);
365+
$cffield = $this->get_generator()->create_datasource_fields_field([
366+
'categoryid' => $category->get('id'),
367+
'shortname' => 'field1',
368+
'type' => 'textarea'
369+
]);
370+
371+
$fs = get_file_storage();
372+
373+
$fileid = $this->get_generator()->make_file($filename, 'Some content');
374+
375+
// Create data for making a module. Add the file to the custom field.
376+
$instancedata = [
377+
'modulename' => 'cms',
378+
'course' => $course->id,
379+
'section' => 0,
380+
'visible' => true,
381+
'typeid' => $cmstype->get('id'),
382+
'name' => 'Some module',
383+
'customfield_field1_editor' => [
384+
'text' => 'Here is a file: @@PLUGINFILE@@/'.$filename,
385+
'format' => FORMAT_HTML,
386+
'itemid' => $fileid,
387+
]
388+
];
389+
390+
$module = create_module((object) $instancedata);
391+
$cm = get_coursemodule_from_id('', $module->coursemodule, 0, false, MUST_EXIST);
392+
$cms = new cms($cm->instance);
393+
$context = context_module::instance($cm->id);
394+
395+
// Get the data ID to find the file with.
396+
$cfhandler = cmsfield_handler::create($cmstype->get('id'));
397+
$d = $cfhandler->get_instance_data($cms->get('id'));
398+
$itemid = $d[$cffield->get('id')]->get('id');
399+
400+
// Check if the permanent file exists.
401+
$file = $fs->get_file($context->id, 'customfield_textarea', 'value', $itemid, '/', $filename);
402+
$this->assertNotEmpty($file);
403+
404+
// Duplicate the module (which is done via backup and restore).
405+
$newcm = duplicate_module($course, $cm);
406+
$newcms = new cms($newcm->instance);
407+
$newcontext = context_module::instance($newcm->id);
408+
409+
// Get the data ID to find the new file with.
410+
$d = $cfhandler->get_instance_data($newcms->get('id'));
411+
$itemid = $d[$cffield->get('id')]->get('id');
412+
413+
// Check if the permanent file exists.
414+
$newfile = $fs->get_file($newcontext->id, 'customfield_textarea', 'value', $itemid, '/', $filename);
415+
$this->assertNotEmpty($newfile);
416+
417+
// Check that the files are distinct.
418+
$this->assertNotEquals($file->get_id(), $newfile->get_id());
419+
420+
// Check the files have the same content.
421+
$this->assertEquals($file->get_content(), $newfile->get_content());
422+
}
338423
}

0 commit comments

Comments
 (0)