diff --git a/wire/core/Fieldgroup.php b/wire/core/Fieldgroup.php index b5040c69..d31f130f 100644 --- a/wire/core/Fieldgroup.php +++ b/wire/core/Fieldgroup.php @@ -177,7 +177,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte * #pw-internal * * @param Field $field - * @return Fieldgroup $this + * @return Fieldgroup|WireArray $this * */ public function finishRemove(Field $field) { @@ -194,7 +194,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte * #pw-group-manipulation * * @param Field|string|int $field Field object, name or id. - * @return Fieldgroup $this + * @return bool|Fieldgroup|WireArray * */ public function softRemove($field) { @@ -379,7 +379,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte * * @param string $key Name of property to set * @param string|int|object $value Value of property - * @return Fieldgroup $this + * @return Fieldgroup|WireArray $this * @throws WireException if passed invalid data * */ @@ -448,7 +448,9 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte * */ public function getExportData() { - return $this->wire('fieldgroups')->getExportData($this); + /** @var Fieldgroups $fieldgroups */ + $fieldgroups = $this->wire('fieldgroups'); + return $fieldgroups->getExportData($this); } /** @@ -469,7 +471,9 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte * */ public function setImportData(array $data) { - return $this->wire('fieldgroups')->setImportData($this, $data); + /** @var Fieldgroups $fieldgroups */ + $fieldgroups = $this->wire('fieldgroups'); + return $fieldgroups->setImportData($this, $data); } /** @@ -640,7 +644,9 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte * */ public function getTemplates() { - return $this->wire('fieldgroups')->getTemplates($this); + /** @var Fieldgroups $fieldgroups */ + $fieldgroups = $this->wire('fieldgroups'); + return $fieldgroups->getTemplates($this); } /** diff --git a/wire/core/Fieldgroups.php b/wire/core/Fieldgroups.php index aa7a7f1c..f1b3dbb3 100644 --- a/wire/core/Fieldgroups.php +++ b/wire/core/Fieldgroups.php @@ -147,20 +147,11 @@ class Fieldgroups extends WireSaveableItemsLookup { if($item->id && $item->removedFields) { foreach($this->wire('templates') as $template) { - if($template->fieldgroup->id !== $item->id) continue; - foreach($item->removedFields as $field) { - // make sure the field is valid to delete from this template - if(($field->flags & Field::flagGlobal) && !$template->noGlobal) { - throw new WireException("Field '$field' may not be removed from fieldgroup '{$item->name}' because it is globally required (Field::flagGlobal)"); - } - - if($field->flags & Field::flagPermanent) { - throw new WireException("Field '$field' may not be removed from fieldgroup '{$item->name}' because it is permanent."); - } - + $error = $this->isFieldNotRemoveable($field, $item, $template); + if($error !== false) throw new WireException("$error Save of fieldgroup changes aborted."); if($field->type) $field->type->deleteTemplateField($template, $field); $item->finishRemove($field); } @@ -206,7 +197,7 @@ class Fieldgroups extends WireSaveableItemsLookup { * Also deletes the references in fieldgroups_fields table * * @param Saveable|Fieldgroup $item - * @return Fieldgroups $this + * @return bool * @throws WireException * */ @@ -218,7 +209,10 @@ class Fieldgroups extends WireSaveableItemsLookup { } if(count($templates)) { - throw new WireException("Can't delete fieldgroup '{$item->name}' because it is in use by template(s): " . implode(', ', $templates)); + throw new WireException( + "Can't delete fieldgroup '{$item->name}' because it is in use by template(s): " . + implode(', ', $templates) + ); } return parent::___delete($item); @@ -272,7 +266,8 @@ class Fieldgroups extends WireSaveableItemsLookup { $contexts = $fieldgroup->getFieldContextArray(); $numSaved = 0; foreach($contexts as $fieldID => $context) { - $field = $fieldgroup->getFieldContext($fieldID); + $field = $fieldgroup->getFieldContext((int) $fieldID); + if(!$field) continue; if($this->wire('fields')->saveFieldgroupContext($field, $fieldgroup)) $numSaved++; } return $numSaved; @@ -454,5 +449,35 @@ class Fieldgroups extends WireSaveableItemsLookup { return $return; } + /** + * Is the given Field not allowed to be removed from given Template? + * + * #pw-internal + * + * @param Field $field + * @param Template $template + * @param Fieldgroup $fieldgroup + * @return bool|string Returns error message string if not removeable or boolean false if it is removeable + * + */ + public function isFieldNotRemoveable(Field $field, Fieldgroup $fieldgroup, Template $template = null) { + + if(is_null($template)) $template = $this->wire('templates')->get($fieldgroup->name); + + if(($field->flags & Field::flagGlobal) && (!$template || !$template->noGlobal)) { + return + "Field '$field' may not be removed from fieldgroup '{$this->name}' " . + "because it is globally required (Field::flagGlobal)."; + } + + if($field->flags & Field::flagPermanent) { + return + "Field '$field' may not be removed from fieldgroup '{$this->name}' " . + "because it is permanent (Field::flagPermanent)."; + } + + return false; + } + } diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module index 4ac39b41..f7e36523 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module @@ -1718,6 +1718,113 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { return $field; */ } + + /** + * Export configuration values for external consumption + * + * Use this method to externalize any config values when necessary. + * For example, internal IDs should be converted to GUIDs where possible. + * Most Fieldtype modules can use the default implementation already provided here. + * + * #pw-group-configuration + * + * @param Field $field + * @param array $data + * @return array + * + */ + public function ___exportConfigData(Field $field, array $data) { + + $data = parent::___exportConfigData($field, $data); + + $template = $this->wire('templates')->get((int) $data['template_id']); + + $data['template_id'] = 0; + $data['parent_id'] = 0; + $data['repeaterFields'] = array(); + $data['fieldContexts'] = array(); + + foreach($field->get('repeaterFields') as $fid) { + $f = $this->wire('fields')->get((int) $fid); + if(!$f) continue; + $data['repeaterFields'][] = $f->name; + $data['fieldContexts'][$f->name] = $template->fieldgroup->getFieldContextArray($f->id); + } + + return $data; + } + + /** + * Convert an array of exported data to a format that will be understood internally + * + * This is the opposite of the exportConfigData() method. + * Most modules can use the default implementation provided here. + * + * #pw-group-configuration + * + * @param Field $field + * @param array $data + * @return array Data as given and modified as needed. Also included is $data[errors], an associative array + * indexed by property name containing errors that occurred during import of config data. + * + */ + public function ___importConfigData(Field $field, array $data) { + + if(!$field->type instanceof FieldtypeRepeater) return $data; + + $errors = array(); + $repeaterFields = array(); + $saveFieldgroup = false; + $saveFieldgroupContext = false; + $template = $field->id ? $this->getRepeaterTemplate($field) : null; + + if(!empty($data['repeaterFields'])) { + foreach($data['repeaterFields'] as $name) { + $f = $this->wire('fields')->get($name); + if(!$f || !$f instanceof Field) { + $errors[] = "Unable to locate field to add to repeater: $name"; + continue; + } + $repeaterFields[] = $f->id; + } + $data['repeaterFields'] = $repeaterFields; + } + + if($template && !empty($data['fieldContexts'])) { + foreach($data['fieldContexts'] as $name => $contextData) { + $f = $this->wire('fields')->get($name); + if(!$f || !$f instanceof Field) continue; + if($template->fieldgroup->hasField($f)) { + $f = $template->fieldgroup->getFieldContext($f->name); + } + $template->fieldgroup->add($f); + $saveFieldgroup = true; + if(!empty($contextData)) { + $template->fieldgroup->setFieldContextArray($f->id, $contextData); + $saveFieldgroupContext = true; + } + } + } + + if($template) { + if($saveFieldgroupContext) { + $template->fieldgroup->saveContext(); + } + if($saveFieldgroup) { + $template->fieldgroup->save(); + } + } + + unset($data['fieldContexts']); + + $data = parent::___importConfigData($field, $data); + + if(count($errors)) { + $data['errors'] = array_merge($data['errors'], array('repeaterFields' => $errors)); + } + + return $data; + } /** * Return configuration fields definable for each FieldtypePage diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module index 9ff119c5..ecbafa1a 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module @@ -777,6 +777,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { $depthName = "depth_repeater{$page->id}"; if($input->$deleteName == $page->id) { + // @todo add check to Fieldgroups::isFieldNotRemoveable() before attempting remove $value->remove($page); $numChanges++; continue;