1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-09 16:26:59 +02:00

Add Setup>Fields>Export/Import support for Repeater fields. processwire/processwire-issues#416

This commit is contained in:
Ryan Cramer
2017-11-02 06:33:46 -04:00
parent ab6d158cf9
commit 7b19df0175
4 changed files with 159 additions and 20 deletions

View File

@@ -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);
}
/**

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;