16
16
17
17
namespace mod_cms \local \datasource ;
18
18
19
- use core_customfield \field ;
19
+ use core_customfield \{ data_controller , field , field_controller } ;
20
20
use core_customfield \output \management ;
21
21
use mod_cms \customfield \cmsuserlist_handler ;
22
- use mod_cms \helper ;
23
22
use mod_cms \local \lib ;
24
- use mod_cms \local \model \{cms , cms_userlist , cms_userlist_columns };
23
+ use mod_cms \local \model \{cms , cms_userlist };
25
24
26
25
/**
27
26
* User designed lists
36
35
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
36
*/
38
37
class userlist extends base {
39
-
40
- /** URL for the config page. */
41
- const CONFIG_URL = '/mod/cms/userlist.php ' ;
42
- /** Icon to use for the link in the table list. */
43
- const ACTION_ICON = 't/grades ' ;
44
-
45
38
/** Default number of rows to include in the list edit form. */
46
39
const DEFAULT_NUM_ROWS = 2 ;
47
40
/** Prefix to use for list elements. */
@@ -101,10 +94,24 @@ public function get_data(): \stdClass {
101
94
if ($ this ->cms ->issample ) {
102
95
$ data ->data = $ this ->get_sample ($ columndefs );
103
96
} else {
104
- $ list = cms_userlist::get_from_cmsid ($ this ->cms ->get ('id ' ));
105
- if (isset ($ list )) {
106
- $ data ->data = $ list ->get ('data ' );
97
+ $ instanceids = $ this ->get_instance_ids ();
98
+ ksort ($ instanceids );
99
+ $ rows = [];
100
+ foreach ($ instanceids as $ id ) {
101
+ $ rowobj = new \stdClass ();
102
+ $ row = $ this ->cfhandler ->get_instance_data ($ id , true );
103
+ foreach ($ row as $ datacontroller ) {
104
+ $ name = $ datacontroller ->get_field ()->get ('shortname ' );
105
+ $ rowobj ->$ name = $ datacontroller ->export_value ();
106
+ }
107
+ $ rows [] = $ rowobj ;
107
108
}
109
+ $ data ->data = $ rows ;
110
+ }
111
+ if (isset ($ data ->data )) {
112
+ $ data ->numrows = count ($ data ->data );
113
+ } else {
114
+ $ data ->numrows = 0 ;
108
115
}
109
116
110
117
return $ data ;
@@ -158,34 +165,34 @@ public function config_form_definition(\MoodleQuickForm $mform) {
158
165
* @param \MoodleQuickForm $mform
159
166
*/
160
167
public function instance_form_definition (\moodleform_mod $ form , \MoodleQuickForm $ mform ) {
168
+ $ cmsid = $ this ->cms ->get ('id ' );
161
169
$ fakeform = new \MoodleQuickForm ('a ' , 'b ' , 'c ' );
162
- $ this ->cfhandler ->instance_form_definition ($ fakeform , $ this ->cms ->get ('id ' ));
163
- $ list = cms_userlist::get_from_cmsid ($ this ->cms ->get ('id ' ));
170
+ $ this ->cfhandler ->instance_form_definition ($ fakeform , $ cmsid );
164
171
172
+ $ instanceids = $ this ->get_instance_ids ();
165
173
$ repeatable = [];
166
174
$ repeatoptions = [];
167
- $ repeatno = $ list ? $ list -> get ( ' numrows ' ) : self ::DEFAULT_NUM_ROWS ;
175
+ $ repeatno = count ( $ instanceids ) ? : self ::DEFAULT_NUM_ROWS ;
168
176
169
177
$ fields = $ this ->cfhandler ->get_fields ();
170
178
foreach ($ fields as $ field ) {
171
- $ actualname = self ::FORM_PREFIX . $ field ->get ('shortname ' );
172
- $ fakename = self ::CUSTOMFIELD_PREFIX . $ field ->get ('shortname ' );
173
- $ element = $ fakeform ->getElement ($ fakename );
174
- $ element ->setName ($ actualname );
179
+ $ names = $ this ->get_element_names ($ field );
180
+ $ element = $ fakeform ->getElement ($ names ->cfelementname );
181
+ $ element ->setName ($ names ->ulelementname );
175
182
$ repeatable [] = $ element ;
176
183
177
184
// Sometimes, the default has to be an integer.
178
185
$ default = $ field ->get_configdata_property ('defaultvalue ' );
179
186
if ($ default == (int ) $ default ) {
180
187
$ default = (int ) $ default ;
181
188
}
182
- $ repeatoptions [$ actualname ] = [
189
+ $ repeatoptions [$ names -> ulelementname ] = [
183
190
'default ' => $ default ,
184
- 'type ' => $ fakeform ->getCleanType ($ fakename , 0 ),
191
+ 'type ' => $ fakeform ->getCleanType ($ names -> cfelementname , 0 ),
185
192
];
186
193
// Set the rule. Only one rule is allowed.
187
194
if ($ field ->get_configdata_property ('required ' )) {
188
- $ repeatoptions [$ actualname ]['rule ' ] = 'required ' ;
195
+ $ repeatoptions [$ names -> ulelementname ]['rule ' ] = 'required ' ;
189
196
}
190
197
}
191
198
$ repeatable [] = $ mform ->createElement ('submit ' , 'delete ' , 'Remove ' , [], false );
@@ -209,17 +216,20 @@ public function instance_form_definition(\moodleform_mod $form, \MoodleQuickForm
209
216
210
217
/**
211
218
* Get extra data needed to add to the form.
219
+ *
212
220
* @param mixed $data
213
221
*/
214
222
public function instance_form_default_data (&$ data ) {
215
- $ list = cms_userlist::get_from_cmsid ($ this ->cms ->get ('id ' ));
216
-
217
- // Extract the values of the column defs and convert into the format that the form requires.
218
- $ listdata = $ list ->get ('data ' );
219
- foreach ($ listdata as $ count => $ row ) {
220
- foreach ($ row as $ idx => $ val ) {
221
- $ key = self ::FORM_PREFIX . "{$ idx }[ {$ count }] " ;
222
- $ data ->$ key = $ val ;
223
+ $ instanceids = $ this ->get_instance_ids ();
224
+ foreach ($ instanceids as $ rownum => $ id ) {
225
+ $ cfdata = new \stdClass ();
226
+ $ cfdata ->id = $ id ;
227
+ $ this ->cfhandler ->instance_form_before_set_data ($ cfdata );
228
+ unset($ cfdata ->id );
229
+ $ cfdata = $ this ->swap_prefix ($ cfdata , self ::CUSTOMFIELD_PREFIX , self ::FORM_PREFIX );
230
+ foreach ($ cfdata as $ name => $ value ) {
231
+ $ key = "{$ name }[ {$ rownum }] " ;
232
+ $ data ->$ key = $ value ;
223
233
}
224
234
}
225
235
}
@@ -233,11 +243,12 @@ public function instance_form_default_data(&$data) {
233
243
*/
234
244
public function instance_form_validation (array $ data , array $ files ): array {
235
245
$ objdata = (object ) $ data ;
236
- $ this ->convert ($ objdata, self :: CUSTOMFIELD_PREFIX );
246
+ $ this ->from_repeatable ($ objdata );
237
247
238
248
$ errors = [];
239
249
foreach ($ objdata ->data as $ count => $ group ) {
240
- $ fielderrors = $ this ->cfhandler ->instance_form_validation ((array )$ group , $ files );
250
+ $ datatovalidate = $ this ->swap_prefix ($ group , self ::FORM_PREFIX , self ::CUSTOMFIELD_PREFIX );
251
+ $ fielderrors = $ this ->cfhandler ->instance_form_validation ((array )$ datatovalidate , $ files );
241
252
foreach ($ fielderrors as $ idx => $ error ) {
242
253
$ idx = str_replace (self ::CUSTOMFIELD_PREFIX , self ::FORM_PREFIX , $ idx );
243
254
$ errors ["{$ idx }[ {$ count }" ] = $ error ;
@@ -253,17 +264,27 @@ public function instance_form_validation(array $data, array $files): array {
253
264
* @param bool $isnewinstance
254
265
*/
255
266
public function update_instance (\stdClass $ instancedata , bool $ isnewinstance ) {
256
- $ list = cms_userlist::get_from_cmsid ($ instancedata ->id );
257
- if ($ list === null ) {
258
- $ list = new cms_userlist ();
259
- $ list ->set ('cmsid ' , $ instancedata ->id );
260
- $ list ->set ('typeid ' , $ this ->cms ->get ('typeid ' ));
267
+ $ this ->from_repeatable ($ instancedata );
268
+
269
+ $ instanceids = $ this ->get_instance_ids ();
270
+
271
+ $ numexisting = count ($ instanceids );
272
+
273
+ foreach ($ instancedata ->data as $ count => $ group ) {
274
+ $ datatosave = $ this ->swap_prefix ($ group , self ::FORM_PREFIX , self ::CUSTOMFIELD_PREFIX );
275
+ $ datatosave ->id = $ this ->get_instance_id ($ count );
276
+ $ this ->cfhandler ->instance_form_save ($ datatosave , $ count >= $ numexisting );
261
277
}
262
278
263
- $ this ->convert ($ instancedata );
264
- $ list ->set ('data ' , $ instancedata ->data );
265
- $ list ->set ('numrows ' , $ instancedata ->numrows );
266
- $ list ->save ();
279
+ // Remove excess.
280
+ $ count = count ($ instancedata ->data );
281
+ if ($ count < $ numexisting ) {
282
+ for ($ i = $ count ; $ i < $ numexisting ; ++$ i ) {
283
+ $ this ->cfhandler ->delete_instance ($ instanceids [$ i ]);
284
+ }
285
+ $ instanceids = array_slice ($ instanceids , 0 , $ count , true );
286
+ $ this ->cms ->set_custom_data ('userlistinstanceids ' , $ instanceids );
287
+ }
267
288
268
289
// Update hash.
269
290
$ hash = hash (lib::HASH_ALGO , serialize ($ this ->get_data ()));
@@ -273,22 +294,23 @@ public function update_instance(\stdClass $instancedata, bool $isnewinstance) {
273
294
}
274
295
275
296
/**
276
- * Convert the form data to something settable to the persistent.
297
+ * Convert the form data from the raw format returned by the form into a more usable format where each element group is
298
+ * actually grouped.
299
+ *
277
300
* Data comes in as
278
301
* {
279
302
* userlist_name : ['John', 'Andy'],
280
303
* userlist_age : [12, 14]
281
304
* }
282
305
* Converts to
283
306
* [
284
- * { name : 'John', age : 12 },
285
- * { name : 'Andy', age : 14 }
307
+ * { userlist_name : 'John', userlist_age : 12 },
308
+ * { userlist_name : 'Andy', userlist_age : 14 }
286
309
* ]
287
310
*
288
311
* @param \stdClass $data Form data as returned by moodleform::get_data().
289
- * @param string $prefix A prefix to put at the front of names when adding to data.
290
312
*/
291
- protected function convert (\stdClass $ data, $ prefix = '' ) {
313
+ protected function from_repeatable (\stdClass $ data ) {
292
314
$ deletehidden = 'delete-hidden ' ;
293
315
$ deletehidden = isset ($ data ->$ deletehidden ) ? $ data ->$ deletehidden : [];
294
316
@@ -305,24 +327,52 @@ protected function convert(\stdClass $data, $prefix = '') {
305
327
}
306
328
307
329
foreach ($ columndefs as $ columndef ) {
308
- $ name = $ columndef -> get ( ' shortname ' );
309
- $ formname = self :: FORM_PREFIX . $ name ;
330
+ $ names = $ this -> get_element_names ( $ columndef );
331
+ $ formname = $ names -> ulelementname ;
310
332
foreach ($ data ->$ formname as $ i => $ val ) {
311
333
if (isset ($ deletehidden [$ i ])) {
312
334
continue ;
313
335
}
314
- $ savename = $ prefix . $ name ;
315
- $ defs [$ i ]->$ savename = $ val ;
336
+ $ defs [$ i ]->$ formname = $ val ;
316
337
}
317
338
}
318
339
319
- // Re-sequence array indexes.
320
- $ defs = array_values ($ defs );
321
-
340
+ $ defs = array_values ($ defs ); // Re-sequence array indexes.
322
341
$ data ->data = $ defs ;
323
342
$ data ->numrows = count ($ defs );
324
343
}
325
344
345
+ /**
346
+ * Swaps the prefixes of the data.
347
+ *
348
+ * @param \stdClass $data
349
+ * @param string $fromprefix
350
+ * @param string $toprefix
351
+ * @return \stdClass
352
+ */
353
+ protected function swap_prefix (\stdClass $ data , string $ fromprefix , string $ toprefix ): \stdClass {
354
+ $ newdata = new \stdClass ();
355
+ foreach ($ data as $ name => $ value ) {
356
+ $ newname = str_replace ($ fromprefix , $ toprefix , $ name );
357
+ $ newdata ->$ newname = $ value ;
358
+ }
359
+ return $ newdata ;
360
+ }
361
+
362
+ /**
363
+ * Called after updating cms type to perform any extra saving required by datasource.
364
+ *
365
+ * @param mixed $data
366
+ */
367
+ public function config_on_update ($ data ) {
368
+ $ categories = $ this ->cfhandler ->get_categories_with_fields ();
369
+ // Add a category if creating.
370
+ if (count ($ categories ) === 0 ) {
371
+ $ this ->cfhandler ->create_category ('' );
372
+ }
373
+ $ this ->update_config_hash ();
374
+ }
375
+
326
376
/**
327
377
* Get configuration data for exporting.
328
378
*
@@ -387,9 +437,60 @@ public function config_on_delete() {
387
437
* Called when deleting a CMS instance.
388
438
*/
389
439
public function instance_on_delete () {
390
- $ list = cms_userlist::get_from_cmsid ($ this ->cms ->get ('id ' ));
391
- if (isset ($ list )) {
392
- $ list ->delete ();
440
+ $ ids = $ this ->get_instance_ids ();
441
+ foreach ($ ids as $ id ) {
442
+ $ this ->cfhandler ->delete_instance ($ id );
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Get the various names for the element.
448
+ *
449
+ * @param field_controller $columndef Custom field containing the definition of the list column elements.
450
+ * @param int $rownum
451
+ *
452
+ * @return \stdClass An object with the various names for the column.
453
+ * - shortname: The actual name of the column. Use in the mustache template. e.g. 'mytextarea'.
454
+ * - cfelementname: The form name provided by the custom field. e.g. 'customfield_mytextarea_editor'.
455
+ * - ulelementname: The form name to be used in the userlist mod instance form. e.g 'userlist_mytextarea_editor'.
456
+ */
457
+ public function get_element_names (field_controller $ columndef , int $ rownum = 0 ): \stdClass {
458
+ $ names = new \stdClass ();
459
+ $ datacontroller = data_controller::create (0 , (object )['instanceid ' => 0 ], $ columndef );
460
+ $ names ->shortname = $ columndef ->get ('shortname ' );
461
+ $ names ->cfelementname = $ datacontroller ->get_form_element_name ();
462
+ $ names ->ulelementname = str_replace (self ::CUSTOMFIELD_PREFIX , self ::FORM_PREFIX , $ names ->cfelementname );
463
+ return $ names ;
464
+ }
465
+
466
+ /**
467
+ * Obtain a unqiue instance ID to use to store the row in the custom field table.
468
+ *
469
+ * @param int $rownum
470
+ * @return int
471
+ */
472
+ protected function get_instance_id (int $ rownum ): int {
473
+ $ ids = $ this ->get_instance_ids ();
474
+ if (!isset ($ ids [$ rownum ])) {
475
+ $ nextid = $ this ->cms ->get_custom_data ('userlistmaxinstanceid ' ) + 1 ;
476
+ $ this ->cms ->set_custom_data ('userlistmaxinstanceid ' , $ nextid );
477
+ $ ids [$ rownum ] = $ nextid ;
478
+ $ this ->cms ->set_custom_data ('userlistinstanceids ' , $ ids );
479
+ $ this ->cms ->save ();
480
+ }
481
+ return $ ids [$ rownum ];
482
+ }
483
+
484
+ /**
485
+ * Gets the custom fields instance IDs for this CMS instance. Indexed by row number.
486
+ *
487
+ * @return array
488
+ */
489
+ protected function get_instance_ids (): array {
490
+ $ ids = $ this ->cms ->get_custom_data ('userlistinstanceids ' );
491
+ if (is_null ($ ids )) {
492
+ $ ids = [];
393
493
}
494
+ return (array ) $ ids ;
394
495
}
395
496
}
0 commit comments