Passed
Push — develop ( 5f0710...340c0b )
by Neill
12:23 queued 14s
created

App::getGridDefinition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 3
ccs 0
cts 3
cp 0
crap 6
1
<?php
2
/**
3
 * @link http://www.newicon.net/neon
4
 * @copyright Copyright (c) 2018 Newicon Ltd
5
 * @license http://www.newicon.net/neon/license/
6
 */
7
8
namespace neon\phoebe;
9
10
use neon\core\helpers\Arr;
11
use neon\core\interfaces\IDataMapProvider;
12
use neon\phoebe\services\PhoebeService;
13
use neon\phoebe\services\adapters\common\models\PhoebeClass as PhoebeClassModel;
14
use neon\phoebe\services\adapters\common\models\PhoebeObject as PhoebeObjectModel;
15
use neon\core\helpers\Html;
16
17
/**
18
 * The neon phoebe app class
19
 */
20
class App extends \neon\core\BaseApp
21
implements IDataMapProvider
22
{
23
	public $requires = 'dds';
24
25
	/**
26
	 * The phoebe service reference. One per app creation.
27
	 * @var neon\phoebe\services\PhoebeService
0 ignored issues
show
Bug introduced by
The type neon\phoebe\neon\phoebe\services\PhoebeService was not found. Did you mean neon\phoebe\services\PhoebeService? If so, make sure to prefix the type with \.
Loading history...
28
	 */
29
	public $phoebeService = null;
30
31
	/**
32
	 * @inheritdoc
33
	 */
34
	public function getName()
35
	{
36
		return 'Phoebe';
37
	}
38
39
	/**
40
	 * Override in config with an array to change menu options
41
	 * for e.g:
42
	 * ```php
43
	 * 'menu' => [
44
	 *     [
45
	 *         'label' => 'App Forms',
46
	 *         'order' => 1020,
47
	 *         'url' => ['/phoebe/appforms/index/index'],
48
	 *         'visible' => function() { // rules },
49
	 *     ],
50
	 * ]
51
	 * ```
52
	 * @see getMenu
53
	 * @see setMenu
54
	 * @var null
55
	 */
56
	protected $_menu = null;
57
58
	/**
59
	 * Enable default phoebe menu to be overridden
60
	 * @param $menu
61
	 */
62
	public function setMenu($menu)
63
	{
64
		$this->_menu = $menu;
65
	}
66
67
	/**
68
	 * @inheritdoc
69
	 */
70
	public function getDefaultMenu()
71
	{
72
		return [
73
			[
74
				'label' => 'Forms',
75
				'order' => 1400,
76
				'url' => ['/phoebe/appforms/index/index'],
77
				'visible' => neon()->user->hasRole('neon-administrator'),
78
				'active' => is_route('/phoebe/appforms/*'),
79
			]
80
		];
81
	}
82
83
	/**
84
	 * @inheritdoc
85
	 */
86
	public function getSettings()
87
	{
88
		return [];
89
	}
90
91
	/**
92
	 * @inheritdoc
93
	 */
94
	public function getDataMap($key, $query='', $filters=[], $fields=[], $start=0, $length=100)
95
	{
96
		// convert generic form builder filters to phoebe class ones
97
		$filters = Arr::replaceKeys($filters, ['uuid'=> 'class_type']);
98
		switch ($key) {
99
			// changed from Class to phoebe_class but need both for backwards compatibility
100
			case 'Class': case 'phoebe_class':
101
				return PhoebeClassModel::listClassTypes($query, $filters);
102
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
103
			// changed from Object to phoebe_object but need both for backwards compatibility
104
			case 'Object': case 'phoebe_object':
105
				return PhoebeObjectModel::listObjects($query, $filters);
106
			break;
107
			default:
108
				return [];
109
			break;
110
		}
111
	}
112
113
	/**
114
	 * @inheritdoc
115
	 */
116
	public function getDataMapTypes()
117
	{
118
		return [
119
			'phoebe_class' => 'Phoebe Class',
120
			'phoebe_object' => 'Phoebe Object'
121
		];
122
	}
123
124
	/**
125
	 * @inheritdoc
126
	 */
127
	public function getMapResults($requestKey)
128
	{
129
		// TODO: Implement getMapResults() method.
130
		return [];
131
	}
132
133
	/**
134
	 * @inheritdoc
135
	 */
136
	public function makeMapLookupRequest($key, $ids, $fields = [])
137
	{
138
		// TODO: Implement makeMapLookupRequest() method.
139
	}
140
141
	/**
142
	 * Get the phoebe service
143
	 * @return \neon\phoebe\interfaces\IPhoebeService
144
	 */
145 8
	public function getService()
146
	{
147 8
		if (!$this->phoebeService) {
148 2
			$this->phoebeService = new PhoebeService();
0 ignored issues
show
Documentation Bug introduced by
It seems like new neon\phoebe\services\PhoebeService() of type neon\phoebe\services\PhoebeService is incompatible with the declared type neon\phoebe\neon\phoebe\services\PhoebeService of property $phoebeService.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
149
		}
150 8
		return $this->phoebeService;
151
	}
152
153
	/**
154
	 * return neon\phoebe\interfaces\IPhoebeType
155
	 */
156 2
	public function getIPhoebeType($phoebeType)
157
	{
158 2
		return $this->getService()->getPhoebeType($phoebeType);
159
	}
160
161
	/**
162
	 * TODO - 20180507 - Tidy some of the form functions away into
163
	 * somewhere else - e.g. the PhoebeObject/Class/Type. That way you can make the
164
	 * methods specific to the type of form that is created
165
	 */
166
167
	/**
168
	 * Get hold of a form definition given the phoebe and class type
169
	 * Applies a cache per php request (cacheArray - a php in memory component)
170
	 * @param string $phoebeType  e.g. 'daedalus'
171
	 * @param string $classType  e.g. 'mytable'
172
	 * @param array $fields  if available for the phoebeType, the set of fields
173
	 *   that you want to display and in what order
174
	 * @return array
175
	 */
176
	public function getFormDefinition($phoebeType, $classType, array $fields=[])
177
	{
178
		$cacheKey = md5(serialize(func_get_args()));
179
		return neon()->cacheArray->getOrSet($cacheKey, function() use ($classType, $phoebeType, $fields) {
180
			$class = $this->getService()->getPhoebeType($phoebeType)->getClass($classType);
181
			return $class ? $class->getClassFormDefinition($fields) : [];
182
		});
183
	}
184
185
	/**
186
	 * Strings used for setting form meta data
187
	 * @var string
188
	 */
189
	private $formMetaPrefix = '_mt';
190
	private $formMetaDataSourceString = 'ds_';
191
	private $formMetaReturnUrlString = 'ru_';
192
	private $formMetaUuidString = 'ud_';
193
194
	/**
195
	 * Get a Phoebe form
196
	 *
197
	 * TODO - 13/07/2018 NJ - Move these into a more appropriate place
198
	 *
199
	 * Depending on settings, this can return an empty form or, so long as this
200
	 * is not a posted request, a form filled with the object from the database
201
	 * Note: if you are using a different name for the form, you must pass this
202
	 * in in the options
203
	 *
204
	 * @param string $phoebeType  the phoebe type required for e.g. 'daedalus'
205
	 * @param string $classType the phoebe class type
206
	 * @param array $options  a set of options to set on the form
207
	 *   'id' => string - an id for the form
208
	 *   'label' => the form label. Omit for the default, or set to '' to remove,
209
	 *     or to the new label string.
210
	 *   'fields' => if available for the phoebe type, the set of fields that you want to
211
	 *     display and in what order as ['field1',...]
212
	 *   'fieldDefaults' => any defaults for the form as ['field'=>default]
213
	 *   'mapFilters' => if available for the phoebe type, filters for any fields that get
214
	 *     their data through a dataMapProvider. These should be as
215
	 *     ['formField1'=>['tableField1'=>'value1', ...], 'formField2'=>...]
216
	 *   'fieldProperties' => if available for the phoebe type, set provided properties for
217
	 *     the form fields as ['field'=>['property'=>'value']]
218
	 *   'save' => ['label' - the default submit button label]
219
	 *   'buttons' => an array of buttons as [['name', 'label', 'class'],...]
220
	 *     These will override the default submit button.
221
	 *   'enableAjaxValidation' => boolean  whether or not to use AJAX validation
222
	 *   'enableAjaxSubmission' => boolean  whether or not to use AJAX submission
223
	 *   'ajaxValidationUrl' => the URL to use for AJAX validation
224
	 *   'readOnly' => [true|false]  if set and true the form is read only
225
	 *   'printOnly' => [true|false]  if set and true the form is printable
226
	 *
227
	 * Additional meta information options can be passed.
228
	 *   'name' => pass in the form name if not defined by the classType
229
	 *   'action' => where the form should go to for processing
230
	 *   'uuid' => if the instance object already exists, set the UUID here. If this isn't
231
	 *      a posted form, the form will be loaded from the database
232
	 *   'dataSources' => if required, a set of data sources that the object needs
233
	 *      for saving itself. These are [key=>value] pairs
234
	 *   'returnUrl' => the return URL after processing the form
235
	 * @param array $action  if set then set this action on the form
236
	 * @return \neon\core\form\Form
237
	 * @throws
238
	 */
239
	public function getForm($phoebeType, $classType, $options)
240
	{
241
		$form = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $form is dead and can be removed.
Loading history...
242
		$fieldCount = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $fieldCount is dead and can be removed.
Loading history...
243
		$readOnly = empty($options['readOnly']) ? false : $options['readOnly'];
244
		$printOnly = empty($options['printOnly']) ? false : $options['printOnly'];
245
		$fields = empty($options['fields']) ? [] : $options['fields'];
246
247
		// see if we this is for an existing object and if so determine if it
248
		// had any overrides we need to take care of
249
		$phoebe = $this->getIPhoebeType($phoebeType);
250
		$object = null;
251
		$formDefinition = [];
252
		if (!empty($options['uuid'])) {
253
			$object = $phoebe->getObject($options['uuid']);
254
			if (!$object)
255
				throw new \RuntimeException('The requested form object was not found. Uuid='.$options['uuid']);
256
			$class = $object->getIPhoebeClass();
257
			$formDefinition = $class->getClassFormDefinition($fields);
258
		} else {
259
			// otherwise get a clean class
260
			if ($classType) {
261
				$formDefinition = $this->getFormDefinition($phoebeType, $classType, $fields);
262
				if (!count($formDefinition))
263
					throw new \RuntimeException('The requested form type was not found. Type='.$classType);
264
			}
265
		}
266
		$fieldCount = count($formDefinition['fields']);
267
		$form = new \neon\core\form\Form($formDefinition);
268
269
270
		// temporary code to cover problems with differences between appForms and ddsForms
271
		if ($phoebeType !== 'applicationForm' && !empty($options['initialiseFromDds']))
272
			throw new \RuntimeException('Only applicationForms work with initialiseFromDds');
273
274
		// set the form id
275
		if (!empty($options['id']))
276
			$form->setId($options['id']);
277
		$formId = $form->getId();
278
		if ($printOnly) {
279
			$form->printOnly = true;
280
			$form->setId('PrintOnly'.$formId);
281
		} else if ($readOnly) {
282
			$form->readOnly = true;
283
			$form->setId('ReadOnly'.$formId);
284
		}
285
286
		// set the forms label - allow clearing of it via empty string
287
		if (isset($options['label']))
288
			$form->setLabel($options['label']);
289
290
291
		// check to see if we are not posting from the client, populate it from the database
292
		// !set the form name before checking data set!
293
		if (!empty($options['name']))
294
			$form->setName($options['name']);
295
		if (!empty($options['initialiseFromDds']) && (!$object || !$object->data)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $object->data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
296
			if (!$object)
297
				$object = $phoebe->createStubObject($classType);
0 ignored issues
show
Bug introduced by
The method createStubObject() does not exist on neon\phoebe\interfaces\IPhoebeType. It seems like you code against a sub-type of said class. However, the method does not exist in neon\phoebe\interfaces\d...lus\IPhoebeDaedalusType. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

297
				/** @scrutinizer ignore-call */ 
298
    $object = $phoebe->createStubObject($classType);
Loading history...
298
			$object->initialiseFromDds($options['initialiseFromDds']);
299
		}
300
		if ($object && !$form->hasRequestData()) {
301
			$form->loadFromDb($object->data);
302
		}
303
304
		if (!($printOnly || $readOnly)) {
305
			if (!empty($options['uuid']))
306
				$form->addFieldHidden("{$this->formMetaPrefix}{$this->formMetaUuidString}", ['value'=>$options['uuid']]);
307
308
			// set any additional data sources
309
			if (!empty($options['dataSources'])) {
310
				foreach ($options['dataSources'] as $key=>$value) {
311
					$form->addFieldHidden("{$this->formMetaPrefix}{$this->formMetaDataSourceString}$key", ['value'=>$value]);
312
					// for dds forms, set the values on the form too for initial display
313
					if ($phoebeType == 'daedalus') {
314
						if ($form->hasField($key))
315
							$form->getField($key)->setValue($value);
316
					}
317
				}
318
			}
319
320
			// set any map filters
321
			// NJ 20180617 - I think this should change the definition above and not be here
322
			// so that way it can work on appForms as well as ddsForms
323
			if (!empty($options['mapFilters'])) {
324
				foreach ($options['mapFilters'] as $key=>$filters) {
325
					$f = $form->getField($key);
326
					if ($f && isset($f->dataMapFilters))
0 ignored issues
show
Bug Best Practice introduced by
The property dataMapFilters does not exist on neon\core\form\fields\Field. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property dataMapFilters does not exist on neon\core\form\Form. Since you implemented __get, consider adding a @property annotation.
Loading history...
327
						$f->dataMapFilters = $filters;
0 ignored issues
show
Bug Best Practice introduced by
The property dataMapFilters does not exist on neon\core\form\fields\Field. Since you implemented __set, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property dataMapFilters does not exist on neon\core\form\Form. Since you implemented __set, consider adding a @property annotation.
Loading history...
328
				}
329
			}
330
331
			// set any map fields
332
			// NJ 20180617 - I think this should change the definition above and not be here
333
			// so that way it can work on appForms as well as ddsForms
334
			if (!empty($options['mapFields'])) {
335
				foreach ($options['mapFields'] as $key=>$mapFields) {
336
					$f = $form->getField($key);
337
					if ($f && isset($f->dataMapFields))
0 ignored issues
show
Bug Best Practice introduced by
The property dataMapFields does not exist on neon\core\form\Form. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property dataMapFields does not exist on neon\core\form\fields\Field. Since you implemented __get, consider adding a @property annotation.
Loading history...
338
						$f->dataMapFields = $mapFields;
0 ignored issues
show
Bug Best Practice introduced by
The property dataMapFields does not exist on neon\core\form\Form. Since you implemented __set, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property dataMapFields does not exist on neon\core\form\fields\Field. Since you implemented __set, consider adding a @property annotation.
Loading history...
339
				}
340
			}
341
342
			if (!empty($options['fieldDefaults'])) {
343
				foreach ($options['fieldDefaults'] as $field=>$default) {
344
					$f = $form->getField($field);
345
					if ($f) {
346
						$f->value = $default;
0 ignored issues
show
Bug Best Practice introduced by
The property value does not exist on neon\core\form\Form. Since you implemented __set, consider adding a @property annotation.
Loading history...
347
					}
348
				}
349
			}
350
351
			if (!empty($options['fieldProperties'])) {
352
				foreach ($options['fieldProperties'] as $field=>$properties) {
353
					$f = $form->getField($field);
354
					if ($f) {
355
						foreach ($properties as $property => $value) {
356
							$f->$property = $value;
357
						}
358
					}
359
				}
360
			}
361
362
			// set the forms action
363
			if (!empty($options['action']))
364
				$form->setAction($options['action']);
365
366
			// set whether or not ajax validation is required
367
			if (isset($options['enableAjaxValidation']))
368
				$form->enableAjaxValidation = (boolean) $options['enableAjaxValidation'];
369
370
			// set whether or not ajax submission is required
371
			if (isset($options['enableAjaxSubmission']))
372
				$form->enableAjaxSubmission = (boolean) $options['enableAjaxSubmission'];
373
374
			// set where the form will check itself via ajaxValidation
375
			if (!empty($options['ajaxValidationUrl']))
376
				$form->validationUrl = $options['ajaxValidationUrl'];
377
378
			// set where the browser will go to after completion of the form
379
			if (!empty($options['returnUrl'])) {
380
				$form->addFieldHidden("{$this->formMetaPrefix}{$this->formMetaReturnUrlString}", ['value'=>$options['returnUrl']]);
381
			}
382
383
			if (!empty($options['buttons'])) {
384
				foreach ($options['buttons'] as $button) {
385
					$btnOptions = [
386
						'order' => $fieldCount++,
387
						'label' => !empty($button['label']) ? $button['label'] : 'Label Required',
388
						'attributes' => [
389
							'class' => !empty($button['class']) ? $button['class'] : null
390
						]
391
					];
392
					$form->addFieldSubmit($button['name'], $btnOptions);
393
				}
394
			} else {
395
				// sort out the default submit button
396
				$submitOptions = [
397
					'order' => $fieldCount++,
398
					'label' => !empty($options['save']['label']) ? $options['save']['label'] : _t('Submit Form')
399
				];
400
				$form->addFieldSubmit('Save', $submitOptions);
401
			}
402
		}
403
		return $form;
404
	}
405
406
	/**
407
	 * Save a phoebe form. This saves generically to daedalus and applicationForm forms.
408
	 *
409
	 * TODO - 13/07/2018 NJ - Move these into a more appropriate place
410
	 *
411
	 * @param string $phoebeType - the phoebe type of the form e.g. daedalus
412
	 * @param string $classType - the name of the form class type e.g. myTable
413
	 * @param object $form - the form if already created or null if it will be here
414
	 * @param string $formName - the form name if not the same as the class type
415
	 * @param string $metaInfo - any additional information that should be used in
416
	 *   the saving. The values would have been saved on the form during creation
417
	 *   if passed in then (@see getForm) and the fields were rendered.
418
	 *   These values if provided override and are any of [
419
	 *     'uuid' => the object uuid if editing
420
	 *     'dataSources' => any data sources that should be set on the saved object
421
	 *     'returnUrl' => where the form should go to after saving
422
	 *   ].
423
	 * @param string &$uuid - the saved object uuid either passed in or set on return
424
	 * @param array &$changeLogUuids - the object change log uuids that occured during the
425
	 *   saving. This will be 'edit' and possibly 'add' if the object was new
426
	 * @return bool - returns true if saved correctly or the set of errors if the
427
	 *   form couldn't validate
428
	 */
429
	public function saveForm($phoebeType, $classType, $form=null, $formName=null, $metaInfo=null, &$uuid=null, &$changeLogUuids=[])
430
	{
431
		$changeLogUuids = [];
432
		if (!$form) {
433
			$definition = $this->getFormDefinition($phoebeType, $classType);
434
			$form = new \neon\core\form\Form($definition);
435
		}
436
		if (!empty($formName))
437
			$form->setName($formName);
438
		if ($form->processRequest()) {
439
			$data = $form->getData();
440
			// prevent multiple renders resulting in multiple saves of the form
441
			static $alreadySubmitted = [];
442
			// allow odd case of submissions of different types of form within one post
443
			// from a controller using this directly. Don't include the data in the key as
444
			// this can change between submission and rerenders of the template.
445
			$submittedCheck = md5($phoebeType.$classType);
446
			if (!array_key_exists($submittedCheck, $alreadySubmitted)) {
447
				$meta = $this->formMergeMetaInformation($form, $metaInfo);
448
				$phoebe = $this->getIPhoebeType($phoebeType);
449
				$object = null;
450
				if (!empty($meta['uuid'])) {
451
					$object = $phoebe->getObject($meta['uuid']);
452
				} else if ($phoebeType == 'daedalus' && $form->hasField('_uuid')) {
453
					$object = $phoebe->getObject($form->getField('_uuid')->getValue());
0 ignored issues
show
Bug introduced by
It seems like $form->getField('_uuid')->getValue() can also be of type array and array; however, parameter $objectId of neon\phoebe\interfaces\IPhoebeType::getObject() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

453
					$object = $phoebe->getObject(/** @scrutinizer ignore-type */ $form->getField('_uuid')->getValue());
Loading history...
454
				} else {
455
					$object = $phoebe->addObject($classType);
456
					$changeLogUuids['add'] = $object->getChangeLogUuid();
457
				}
458
				$uuid = $object->uuid;
459
				if (!empty($meta['dataSources']))
460
					$object->setDataSources($meta['dataSources']);
0 ignored issues
show
Bug introduced by
The method setDataSources() does not exist on neon\phoebe\interfaces\IPhoebeObject. Since it exists in all sub-types, consider adding an abstract or default implementation to neon\phoebe\interfaces\IPhoebeObject. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

460
					$object->/** @scrutinizer ignore-call */ 
461
              setDataSources($meta['dataSources']);
Loading history...
461
				if ($object->editObject($data) === true) {
462
					$alreadySubmitted[$submittedCheck] = $uuid;
463
					$changeLogUuids['edit'] = $object->getChangeLogUuid();
464
					if (!empty($meta['returnUrl']))
465
						neon()->response->redirect(html_entity_decode($meta['returnUrl']));
466
					return true;
467
				}
468
			} else {
469
				$uuid = $alreadySubmitted[$submittedCheck];
470
				return true;
471
			}
472
		}
473
		return $form->getErrors();
474
	}
475
476
	/**
477
	 * Get hold of an object grid definition given the phoebe and class type
478
	 * @param string $phoebeType  e.g. 'daedalus'
479
	 * @param string $classType  e.g. 'mytable'
480
	 * @param boolean $includeData  set to true to include the data field (can be big)
481
	 * @param boolean $includeDeleted  set to true to include the deleted column
482
	 * @return array
483
	 */
484
	public function getGridDefinition($phoebeType, $classType, $additionalColumns = [])
485
	{
486
		$class = $this->getService()->getPhoebeType($phoebeType)->getClass($classType);
487
		return $class ? $class->getObjectGridDefinition($additionalColumns) : [];
488
	}
489
490
491
	/**
492
	 * Extract out any form meta information added. Any non empty meta information
493
	 * passed in via the method takes precedence over anything returned from the form.
494
	 * This is to allow for extra security in the cases where the data can be derived
495
	 * on the server too.
496
	 * @param Form $form
497
	 * @param array $metaInfo - an array of any of 'uuid', 'returnUrl', 'dataSources'
498
	 * @return array
499
	 */
500
	protected function formMergeMetaInformation($form, $meta)
501
	{
502
		$data = $form->getData();
503
		foreach ($data as $k=>$v) {
504
			if (strpos($k, $this->formMetaPrefix) === 0) {
505
				$key = substr($k, strlen($this->formMetaPrefix));
506
				if ($key == $this->formMetaUuidString && empty($meta['uuid'])) {
507
					$meta['uuid'] = $v;
508
				} else if (strpos($key, $this->formMetaDataSourceString) === 0) {
509
					$itemKey = substr($key, strlen($this->formMetaDataSourceString));
510
					if (empty($meta['dataSources'][$itemKey]))
511
						$meta['dataSources'][$itemKey] = $v;
512
				} else if ($key==$this->formMetaReturnUrlString && empty($meta['returnUrl'])) {
513
					$meta['returnUrl'] = $v;
514
				}
515
			}
516
		}
517
		// capture the case where the form posted a uuid but the server didn't know
518
		// to create the uuid field as it didn't know the uuid at that point
519
		if (empty($meta['uuid'])) {
520
			$postedUuid = $form->getRawRequestData($this->formMetaPrefix.$this->formMetaUuidString);
521
			$meta['uuid'] = $postedUuid ? Html::sanitise($postedUuid) : null;
522
		}
523
		return $meta;
524
	}
525
526
}
527