mirror of
https://github.com/processwire/processwire.git
synced 2025-08-14 18:55:56 +02:00
Update FieldtypeRepeater to implement the FieldtypeDoesVersions interface, and related updates to InputfieldRepeater. This commit also moves some of the export/import methods to a separate FieldtypeRepeaterPorter class.
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
* /wire/core/Fieldtype.php
|
* /wire/core/Fieldtype.php
|
||||||
* /wire/core/FieldtypeMulti.php
|
* /wire/core/FieldtypeMulti.php
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
* @todo: automatic sorting.
|
* @todo: automatic sorting.
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
class FieldtypeRepeater extends Fieldtype implements ConfigurableModule, FieldtypeDoesVersions {
|
||||||
|
|
||||||
public static function getModuleInfo() {
|
public static function getModuleInfo() {
|
||||||
return array(
|
return array(
|
||||||
@@ -857,7 +857,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
|
|
||||||
if(is_array($value) && !empty($value['parent_id'])) {
|
if(is_array($value) && !empty($value['parent_id'])) {
|
||||||
// this is what we get if there was a record in the DB and the parent has been setup
|
// this is what we get if there was a record in the DB and the parent has been setup
|
||||||
$parent_id = (int) $value['parent_id'];
|
$parent_id = (int) ((string) $value['parent_id']);
|
||||||
|
|
||||||
} else if(empty($value['data']) && empty($value['parent_id']) && $this->useLazyParents($field)) {
|
} else if(empty($value['data']) && empty($value['parent_id']) && $this->useLazyParents($field)) {
|
||||||
// no record in the DB yet and parent will not be created till needed
|
// no record in the DB yet and parent will not be created till needed
|
||||||
@@ -988,34 +988,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function ___exportValue(Page $page, Field $field, $value, array $options = array()) {
|
public function ___exportValue(Page $page, Field $field, $value, array $options = array()) {
|
||||||
|
return $this->porter()->exportValue($page, $field, $value, $options);
|
||||||
$a = array();
|
|
||||||
if(!WireArray::iterable($value)) return $a;
|
|
||||||
|
|
||||||
if(!empty($options['minimal']) || !empty($options['FieldtypeRepeater']['minimal'])) {
|
|
||||||
// minimal export option includes only fields data
|
|
||||||
|
|
||||||
foreach($value as $p) {
|
|
||||||
/** @var Page $p */
|
|
||||||
if($p->isUnpublished()) continue;
|
|
||||||
$v = array();
|
|
||||||
foreach($p->template->fieldgroup as $f) {
|
|
||||||
/** @var Field $f */
|
|
||||||
if(!$p->hasField($f)) continue;
|
|
||||||
$fieldtype = $f->type; /** @var Fieldtype $fieldtype */
|
|
||||||
$v[$f->name] = $fieldtype->exportValue($p, $f, $p->getUnformatted($f->name), $options);
|
|
||||||
}
|
|
||||||
$a[$p->name] = $v;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// regular export
|
|
||||||
/** @var PagesExportImport $exporter */
|
|
||||||
$exporter = $this->wire(new PagesExportImport());
|
|
||||||
$a = $exporter->pagesToArray($value, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1030,136 +1003,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function ___importValue(Page $page, Field $field, $value, array $options = array()) {
|
public function ___importValue(Page $page, Field $field, $value, array $options = array()) {
|
||||||
|
return $this->porter()->importValue($page, $field, $value, $options);
|
||||||
if(empty($value['type']) || $value['type'] != 'ProcessWire:PageArray') {
|
|
||||||
throw new WireException("$field->name: Invalid repeater importValue() \$value argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$page->id) {
|
|
||||||
$page->trackChange($field->name);
|
|
||||||
throw new WireException("$field->name: Repeater will import after page is created");
|
|
||||||
}
|
|
||||||
|
|
||||||
$repeaterParent = $this->getRepeaterPageParent($page, $field);
|
|
||||||
$repeaterTemplate = $this->getRepeaterTemplate($field);
|
|
||||||
$repeaterPageClass = $this->getPageClass();
|
|
||||||
$repeaterPageArrayClass = $this->getPageArrayClass();
|
|
||||||
$parentPath = $repeaterParent->path();
|
|
||||||
$commit = isset($options['commit']) ? (bool) $options['commit'] : true;
|
|
||||||
$messages = array();
|
|
||||||
$numAdded = 0;
|
|
||||||
$changesByField = array();
|
|
||||||
$numUpdated = 0;
|
|
||||||
$numDeleted = 0;
|
|
||||||
$itemsAdded = array();
|
|
||||||
$itemsDeleted = array();
|
|
||||||
$importItemNames = array();
|
|
||||||
$existingValue = $page->get($field->name);
|
|
||||||
|
|
||||||
if(!$existingValue instanceof PageArray) { // i.e. FieldsetPage
|
|
||||||
$existingValue = $existingValue->id ? array($existingValue) : array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$pages = $this->wire()->pages;
|
|
||||||
|
|
||||||
// update paths for local
|
|
||||||
foreach($value['pages'] as $key => $item) {
|
|
||||||
$name = $item['settings']['name'];
|
|
||||||
if(strpos($name, self::repeaterPageNamePrefix) === 0 && count($value['pages']) == 1) {
|
|
||||||
$name = self::repeaterPageNamePrefix . $page->id; // i.e. FieldsetPage
|
|
||||||
$value['pages'][$key]['settings']['name'] = $name;
|
|
||||||
}
|
|
||||||
$path = $parentPath . $name . '/';
|
|
||||||
$importItemNames[$name] = $name;
|
|
||||||
$value['pages'][$key]['path'] = $path;
|
|
||||||
$p = $pages->get($path);
|
|
||||||
if($p->id) continue; // already exists
|
|
||||||
|
|
||||||
// from this point forward, it is assumed we are creating a new repeater item
|
|
||||||
$numAdded++;
|
|
||||||
$page->trackChange($field->name);
|
|
||||||
|
|
||||||
if($commit) {
|
|
||||||
// create new repeater item, ready to be populated
|
|
||||||
/** @var RepeaterPage $p */
|
|
||||||
$p = $this->wire(new $repeaterPageClass());
|
|
||||||
if($repeaterParent->id) $p->parent = $repeaterParent;
|
|
||||||
$p->template = $repeaterTemplate;
|
|
||||||
$p->name = $name;
|
|
||||||
$p->setForPage($page);
|
|
||||||
$p->setForField($field);
|
|
||||||
$p->save();
|
|
||||||
$itemsAdded[$p->id] = $p;
|
|
||||||
if($p->name != $name) $importItemNames[$p->name] = $p->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($page->get('_importType') == 'update') {
|
|
||||||
foreach($existingValue as $p) {
|
|
||||||
if(!isset($importItemNames[$p->name])) {
|
|
||||||
$itemsDeleted[] = $p;
|
|
||||||
$numDeleted++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var RepeaterPageArray $pageArray */
|
|
||||||
$pageArray = $this->wire(new $repeaterPageArrayClass($page, $field));
|
|
||||||
|
|
||||||
$importOptions = array(
|
|
||||||
'commit' => $commit,
|
|
||||||
'create' => true,
|
|
||||||
'update' => true,
|
|
||||||
'delete' => true, // @todo
|
|
||||||
'pageArray' => $pageArray
|
|
||||||
);
|
|
||||||
|
|
||||||
/** @var PagesExportImport $importer */
|
|
||||||
$importer = $this->wire(new PagesExportImport());
|
|
||||||
$pageArray = $importer->arrayToPages($value, $importOptions);
|
|
||||||
|
|
||||||
foreach($pageArray as $p) {
|
|
||||||
$changes = $p->get('_importChanges');
|
|
||||||
if(!count($changes)) continue;
|
|
||||||
if(isset($itemsAdded[$p->id]) || !$p->id) continue;
|
|
||||||
$numUpdated++;
|
|
||||||
foreach($changes as $fieldName) {
|
|
||||||
if(!isset($changesByField[$fieldName])) $changesByField[$fieldName] = 0;
|
|
||||||
$changesByField[$fieldName]++;
|
|
||||||
}
|
|
||||||
$this->wire()->notices->move($p, $pageArray, array('prefix' => "$field->name (id=$p->id): "));
|
|
||||||
}
|
|
||||||
|
|
||||||
if($numDeleted && $commit) {
|
|
||||||
foreach($itemsDeleted as $p) {
|
|
||||||
$pages->delete($p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($numUpdated) {
|
|
||||||
$updateCounts = array();
|
|
||||||
foreach($changesByField as $fieldName => $count) {
|
|
||||||
$updateCounts[] = "$fieldName ($count)";
|
|
||||||
}
|
|
||||||
$messages[] = "$numUpdated page(s) updated – " . implode(', ', $updateCounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($numAdded) $messages[] = "$numAdded new page(s) added";
|
|
||||||
if($numDeleted) $messages[] = "$numDeleted page(s) DELETED";
|
|
||||||
|
|
||||||
foreach($messages as $message) {
|
|
||||||
$pageArray->message("$field->name: $message");
|
|
||||||
}
|
|
||||||
|
|
||||||
$pageArray->resetTrackChanges();
|
|
||||||
|
|
||||||
$totalChanges = $numUpdated + $numAdded + $numDeleted;
|
|
||||||
if(!$totalChanges) {
|
|
||||||
// prevent it from being counted as a change when import code sets the value back to the page
|
|
||||||
$page->setQuietly($field->name, $pageArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $pageArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1223,6 +1067,10 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
|
|
||||||
$repeaterParent = $this->getRepeaterParent($field);
|
$repeaterParent = $this->getRepeaterParent($field);
|
||||||
$parentName = self::repeaterPageNamePrefix . $page->id; // for-page-123
|
$parentName = self::repeaterPageNamePrefix . $page->id; // for-page-123
|
||||||
|
$version = $this->getPageVersionNum($page);
|
||||||
|
|
||||||
|
if($version) $parentName .= "-v$version"; // for-page-123-v1
|
||||||
|
|
||||||
$parent = $repeaterParent->child("name=$parentName, include=all");
|
$parent = $repeaterParent->child("name=$parentName, include=all");
|
||||||
|
|
||||||
if($parent->id || !$create) return $parent;
|
if($parent->id || !$create) return $parent;
|
||||||
@@ -1230,7 +1078,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
$parent = $this->wire()->pages->newPage($repeaterParent->template);
|
$parent = $this->wire()->pages->newPage($repeaterParent->template);
|
||||||
$parent->parent = $repeaterParent;
|
$parent->parent = $repeaterParent;
|
||||||
$parent->name = $parentName;
|
$parent->name = $parentName;
|
||||||
$parent->title = $page->name;
|
$parent->title = $page->name . ($version ? " v$version" : "");
|
||||||
$parent->addStatus(Page::statusSystem);
|
$parent->addStatus(Page::statusSystem);
|
||||||
|
|
||||||
// exit early if a field is in the process of being deleted
|
// exit early if a field is in the process of being deleted
|
||||||
@@ -1385,6 +1233,17 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #pw-internal
|
||||||
|
*
|
||||||
|
* @param Field $field
|
||||||
|
* @return Template
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function _getRepeaterTemplate(Field $field) {
|
||||||
|
return $this->getRepeaterTemplate($field);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the settings for a newly created repeater template
|
* Populate the settings for a newly created repeater template
|
||||||
*
|
*
|
||||||
@@ -1790,7 +1649,14 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
$pages->delete($p, $saveOptions);
|
$pages->delete($p, $saveOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = parent::___savePageField($page, $field);
|
$version = $this->getPageVersionNum($page);
|
||||||
|
$pagesVersions = $version ? $this->wire('pagesVersions') : null;
|
||||||
|
if($version && $pagesVersions) {
|
||||||
|
/** @var PagesVersions $pagesVersions */
|
||||||
|
$result = $pagesVersions->savePageFieldVersion($page, $field, $version);
|
||||||
|
} else {
|
||||||
|
$result = parent::___savePageField($page, $field);
|
||||||
|
}
|
||||||
|
|
||||||
// ensure that any of our cloned page replacements (removes) don't get recorded any follow-up saves
|
// ensure that any of our cloned page replacements (removes) don't get recorded any follow-up saves
|
||||||
$value->resetTrackChanges();
|
$value->resetTrackChanges();
|
||||||
@@ -1972,26 +1838,8 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function ___exportConfigData(Field $field, array $data) {
|
public function ___exportConfigData(Field $field, array $data) {
|
||||||
|
|
||||||
$data = parent::___exportConfigData($field, $data);
|
$data = parent::___exportConfigData($field, $data);
|
||||||
|
$data = $this->porter()->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();
|
|
||||||
|
|
||||||
$a = $field->get('repeaterFields');
|
|
||||||
if(!is_array($a)) $a = array();
|
|
||||||
|
|
||||||
foreach($a 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;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2012,60 +1860,16 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
public function ___importConfigData(Field $field, array $data) {
|
public function ___importConfigData(Field $field, array $data) {
|
||||||
|
|
||||||
if(!$field->type instanceof FieldtypeRepeater) return $data;
|
if(!$field->type instanceof FieldtypeRepeater) return $data;
|
||||||
|
|
||||||
$fields = $this->wire()->fields;
|
|
||||||
|
|
||||||
$errors = array();
|
$errors = array();
|
||||||
$repeaterFields = array();
|
$data = $this->porter()->importConfigData($field, $data, $errors);
|
||||||
$saveFieldgroup = false;
|
|
||||||
$saveFieldgroupContext = false;
|
|
||||||
$template = $field->id ? $this->getRepeaterTemplate($field) : null;
|
|
||||||
|
|
||||||
if(!empty($data['repeaterFields'])) {
|
|
||||||
foreach($data['repeaterFields'] as $name) {
|
|
||||||
$f = $fields->get($name);
|
|
||||||
if(!$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 = $fields->get($name);
|
|
||||||
if(!$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);
|
$data = parent::___importConfigData($field, $data);
|
||||||
|
|
||||||
if(count($errors)) {
|
if(count($errors)) {
|
||||||
|
if(!isset($data['errors'])) $data['errors'] = array();
|
||||||
$data['errors'] = array_merge($data['errors'], array('repeaterFields' => $errors));
|
$data['errors'] = array_merge($data['errors'], array('repeaterFields' => $errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2389,4 +2193,199 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FieldtypeRepeaterPorter
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function porter() {
|
||||||
|
require_once(__DIR__ . '/FieldtypeRepeaterPorter.php');
|
||||||
|
return $this->wire(new FieldtypeRepeaterPorter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Page $page
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function getPageVersionNum(Page $page) {
|
||||||
|
return (int) ((string) $page->get('_repeater_version|_version'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value for given page, field and version
|
||||||
|
*
|
||||||
|
* #pw-internal for FieldtypeDoesVersions interface
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param Field $field
|
||||||
|
* @param int $version
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function getPageFieldVersion(Page $page, Field $field, $version) {
|
||||||
|
$page->setQuietly('_repeater_version', $version);
|
||||||
|
/** @var PagesVersions $pagesVersions */
|
||||||
|
$pagesVersions = $this->wire('pagesVersions');
|
||||||
|
$value = $pagesVersions ? $pagesVersions->getPageFieldVersion($page, $field, $version) : null;
|
||||||
|
// $value['parent_id'] = $this->getRepeaterPageParent($page, $field);
|
||||||
|
// value = $this->wakeupValue($page, $field, $value);
|
||||||
|
// $page->__unset('_repeater_version');
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save version of given page field
|
||||||
|
*
|
||||||
|
* #pw-internal for FieldtypeDoesVersions interface
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param Field $field
|
||||||
|
* @param int $version
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function savePageFieldVersion(Page $page, Field $field, $version) {
|
||||||
|
|
||||||
|
$value = $page->get($field->name); /** @var RepeaterPageArray $value */
|
||||||
|
$page = clone $page;
|
||||||
|
$page->setQuietly('_repeater_version', $version);
|
||||||
|
|
||||||
|
$parent = $this->getRepeaterPageParent($page, $field, false);
|
||||||
|
|
||||||
|
if(!$parent || !$parent->id) {
|
||||||
|
// setup new version
|
||||||
|
$pages = $this->wire()->pages;
|
||||||
|
$parent = $this->getRepeaterPageParent($page, $field, true); // create
|
||||||
|
$cloneOptions = array('uncacheAll' => false);
|
||||||
|
$valueClass = $value->className(true);
|
||||||
|
$valueCopy = new $valueClass($parent, $field);
|
||||||
|
$this->wire($valueCopy);
|
||||||
|
foreach($value as $item) {
|
||||||
|
/** @var RepeaterPage $item */
|
||||||
|
$itemCopy = $pages->clone($item, $parent, false, $cloneOptions);
|
||||||
|
$this->bd("Cloned repeater item $item->name to parent $parent->name");
|
||||||
|
$valueCopy->add($itemCopy);
|
||||||
|
}
|
||||||
|
$valueCopy->resetTrackChanges();
|
||||||
|
$page->set($field->name, $valueCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->savePageField($page, $field);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore version of given page field
|
||||||
|
*
|
||||||
|
* #pw-internal for FieldtypeDoesVersions interface
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param Field $field
|
||||||
|
* @param int $version
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function restorePageFieldVersion(Page $page, Field $field, $version) {
|
||||||
|
if(!$version) return false;
|
||||||
|
|
||||||
|
$pages = $this->wire()->pages;
|
||||||
|
|
||||||
|
/** @var PagesVersions $pagesVersions */
|
||||||
|
$pagesVersions = $this->wire('pagesVersions');
|
||||||
|
if(!$pagesVersions) return false;
|
||||||
|
|
||||||
|
$pageVersionNum = $this->getPageVersionNum($page);
|
||||||
|
|
||||||
|
if($pageVersionNum) {
|
||||||
|
$livePage = $pages->getFresh($page->id);
|
||||||
|
if($pageVersionNum == $version) {
|
||||||
|
$versionPage = $page;
|
||||||
|
} else {
|
||||||
|
$versionPage = $pagesVersions->getPageVersion($page, $version);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$versionPage = $pagesVersions->getPageVersion($page, $version);
|
||||||
|
$livePage = $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
$versionRepeaterParent = $this->getRepeaterPageParent($versionPage, $field, false);
|
||||||
|
$liveRepeaterParent = $this->getRepeaterPageParent($livePage, $field, false);
|
||||||
|
|
||||||
|
if(!$versionRepeaterParent) {
|
||||||
|
$this->error(
|
||||||
|
"Version repeater parent not found for " .
|
||||||
|
"page $page field $field version $version"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($liveRepeaterParent->id) {
|
||||||
|
$this->deleteRepeaterPage($liveRepeaterParent, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
list($name,) = explode("-v$version", $versionRepeaterParent->name, 2);
|
||||||
|
$versionRepeaterParent->addStatus(Page::statusSystemOverride);
|
||||||
|
$versionRepeaterParent->removeStatus(Page::statusSystem);
|
||||||
|
$versionRepeaterParent->name = $name;
|
||||||
|
$versionRepeaterParent->save();
|
||||||
|
$pages->editor()->addStatus($versionRepeaterParent, Page::statusSystem);
|
||||||
|
|
||||||
|
$itemIDs = array();
|
||||||
|
|
||||||
|
foreach($versionRepeaterParent->children('include=all') as $item) {
|
||||||
|
/** @var RepeaterPage $item */
|
||||||
|
if($item->isHidden() || $item->isUnpublished()) continue;
|
||||||
|
$itemIDs[] = $item->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $field->getTable();
|
||||||
|
$sql =
|
||||||
|
"UPDATE $table SET data=:data, count=:count, parent_id=:parent_id " .
|
||||||
|
"WHERE pages_id=:pages_id";
|
||||||
|
|
||||||
|
$query = $this->wire()->database->prepare($sql);
|
||||||
|
$query->bindValue(':data', implode(',', $itemIDs));
|
||||||
|
$query->bindValue(':count', count($itemIDs));
|
||||||
|
$query->bindValue(':parent_id', $versionRepeaterParent->id, \PDO::PARAM_INT);
|
||||||
|
$query->bindValue(':pages_id', $versionPage->id, \PDO::PARAM_INT);
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
$page->offsetUnset($field->name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete version
|
||||||
|
*
|
||||||
|
* #pw-internal for FieldtypeDoesVersions interface
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param Field $field
|
||||||
|
* @param int $version
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function deletePageFieldVersion(Page $page, Field $field, $version) {
|
||||||
|
if(!$version) return false;
|
||||||
|
|
||||||
|
/** @var PagesVersions $pagesVersions */
|
||||||
|
$pagesVersions = $this->wire('pagesVersions');
|
||||||
|
if(!$pagesVersions) return false;
|
||||||
|
|
||||||
|
if($this->getPageVersionNum($page) == $version) {
|
||||||
|
$versionPage = $page;
|
||||||
|
} else {
|
||||||
|
$versionPage = $pagesVersions->getPageVersion($page, $version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$versionPage->id) return false;
|
||||||
|
|
||||||
|
$versionRepeaterParent = $this->getRepeaterPageParent($versionPage, $field, false);
|
||||||
|
if(!$versionRepeaterParent->id) return false;
|
||||||
|
|
||||||
|
return $this->deleteRepeaterPage($versionRepeaterParent, null, true) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,301 @@
|
|||||||
|
<?php namespace ProcessWire;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export and Import tools for FieldtypeRepeater
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class FieldtypeRepeaterPorter extends Wire {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
$template = $this->wire()->templates->get((int) $data['template_id']);
|
||||||
|
|
||||||
|
$data['template_id'] = 0;
|
||||||
|
$data['parent_id'] = 0;
|
||||||
|
$data['repeaterFields'] = array();
|
||||||
|
$data['fieldContexts'] = array();
|
||||||
|
|
||||||
|
$a = $field->get('repeaterFields');
|
||||||
|
if(!is_array($a)) $a = array();
|
||||||
|
|
||||||
|
foreach($a 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
|
||||||
|
* @var array $errors Errors populated to this array
|
||||||
|
* @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, array &$errors) {
|
||||||
|
|
||||||
|
$fieldtype = $field->type;
|
||||||
|
if(!$fieldtype instanceof FieldtypeRepeater) return $data;
|
||||||
|
|
||||||
|
$fields = $this->wire()->fields;
|
||||||
|
|
||||||
|
$repeaterFields = array();
|
||||||
|
$saveFieldgroup = false;
|
||||||
|
$saveFieldgroupContext = false;
|
||||||
|
$template = $field->id ? $fieldtype->_getRepeaterTemplate($field) : null;
|
||||||
|
|
||||||
|
if(!empty($data['repeaterFields'])) {
|
||||||
|
foreach($data['repeaterFields'] as $name) {
|
||||||
|
$f = $fields->get($name);
|
||||||
|
if(!$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 = $fields->get($name);
|
||||||
|
if(!$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']);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export repeater value
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param Field $field
|
||||||
|
* @param RepeaterPageArray $value
|
||||||
|
* @param array $options
|
||||||
|
* - `minimal` (bool): Export a minimal array of just fields and values indexed by repeater page name (default=false)
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function exportValue(Page $page, Field $field, $value, array $options = array()) {
|
||||||
|
|
||||||
|
$a = array();
|
||||||
|
if(!WireArray::iterable($value)) return $a;
|
||||||
|
|
||||||
|
if(!empty($options['minimal']) || !empty($options['FieldtypeRepeater']['minimal'])) {
|
||||||
|
// minimal export option includes only fields data
|
||||||
|
|
||||||
|
foreach($value as $p) {
|
||||||
|
/** @var Page $p */
|
||||||
|
if($p->isUnpublished()) continue;
|
||||||
|
$v = array();
|
||||||
|
foreach($p->template->fieldgroup as $f) {
|
||||||
|
/** @var Field $f */
|
||||||
|
if(!$p->hasField($f)) continue;
|
||||||
|
$fieldtype = $f->type; /** @var Fieldtype $fieldtype */
|
||||||
|
$v[$f->name] = $fieldtype->exportValue($p, $f, $p->getUnformatted($f->name), $options);
|
||||||
|
}
|
||||||
|
$a[$p->name] = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// regular export
|
||||||
|
/** @var PagesExportImport $exporter */
|
||||||
|
$exporter = $this->wire(new PagesExportImport());
|
||||||
|
$a = $exporter->pagesToArray($value, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import repeater value previously exported by exportValue()
|
||||||
|
*
|
||||||
|
* @param Page $page
|
||||||
|
* @param Field $field
|
||||||
|
* @param array $value
|
||||||
|
* @param array $options
|
||||||
|
* @return bool|PageArray
|
||||||
|
* @throws WireException
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function importValue(Page $page, Field $field, $value, array $options = array()) {
|
||||||
|
|
||||||
|
if(empty($value['type']) || $value['type'] != 'ProcessWire:PageArray') {
|
||||||
|
throw new WireException("$field->name: Invalid repeater importValue() \$value argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$page->id) {
|
||||||
|
$page->trackChange($field->name);
|
||||||
|
throw new WireException("$field->name: Repeater will import after page is created");
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldtype = $field->type; /** @var FieldtypeRepeater $fieldtype */
|
||||||
|
$repeaterParent = $fieldtype->getRepeaterPageParent($page, $field);
|
||||||
|
$repeaterTemplate = $fieldtype->_getRepeaterTemplate($field);
|
||||||
|
$repeaterPageClass = $fieldtype->getPageClass();
|
||||||
|
$repeaterPageArrayClass = $fieldtype->getPageArrayClass();
|
||||||
|
$parentPath = $repeaterParent->path();
|
||||||
|
$commit = isset($options['commit']) ? (bool) $options['commit'] : true;
|
||||||
|
$messages = array();
|
||||||
|
$numAdded = 0;
|
||||||
|
$changesByField = array();
|
||||||
|
$numUpdated = 0;
|
||||||
|
$numDeleted = 0;
|
||||||
|
$itemsAdded = array();
|
||||||
|
$itemsDeleted = array();
|
||||||
|
$importItemNames = array();
|
||||||
|
$existingValue = $page->get($field->name);
|
||||||
|
|
||||||
|
if(!$existingValue instanceof PageArray) { // i.e. FieldsetPage
|
||||||
|
$existingValue = $existingValue->id ? array($existingValue) : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pages = $this->wire()->pages;
|
||||||
|
|
||||||
|
// update paths for local
|
||||||
|
foreach($value['pages'] as $key => $item) {
|
||||||
|
$name = $item['settings']['name'];
|
||||||
|
if(strpos($name, FieldtypeRepeater::repeaterPageNamePrefix) === 0 && count($value['pages']) == 1) {
|
||||||
|
$name = FieldtypeRepeater::repeaterPageNamePrefix . $page->id; // i.e. FieldsetPage
|
||||||
|
$value['pages'][$key]['settings']['name'] = $name;
|
||||||
|
}
|
||||||
|
$path = $parentPath . $name . '/';
|
||||||
|
$importItemNames[$name] = $name;
|
||||||
|
$value['pages'][$key]['path'] = $path;
|
||||||
|
$p = $pages->get($path);
|
||||||
|
if($p->id) continue; // already exists
|
||||||
|
|
||||||
|
// from this point forward, it is assumed we are creating a new repeater item
|
||||||
|
$numAdded++;
|
||||||
|
$page->trackChange($field->name);
|
||||||
|
|
||||||
|
if($commit) {
|
||||||
|
// create new repeater item, ready to be populated
|
||||||
|
/** @var RepeaterPage $p */
|
||||||
|
$p = $this->wire(new $repeaterPageClass());
|
||||||
|
if($repeaterParent->id) $p->parent = $repeaterParent;
|
||||||
|
$p->template = $repeaterTemplate;
|
||||||
|
$p->name = $name;
|
||||||
|
$p->setForPage($page);
|
||||||
|
$p->setForField($field);
|
||||||
|
$p->save();
|
||||||
|
$itemsAdded[$p->id] = $p;
|
||||||
|
if($p->name != $name) $importItemNames[$p->name] = $p->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($page->get('_importType') == 'update') {
|
||||||
|
foreach($existingValue as $p) {
|
||||||
|
if(!isset($importItemNames[$p->name])) {
|
||||||
|
$itemsDeleted[] = $p;
|
||||||
|
$numDeleted++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var RepeaterPageArray $pageArray */
|
||||||
|
$pageArray = $this->wire(new $repeaterPageArrayClass($page, $field));
|
||||||
|
|
||||||
|
$importOptions = array(
|
||||||
|
'commit' => $commit,
|
||||||
|
'create' => true,
|
||||||
|
'update' => true,
|
||||||
|
'delete' => true, // @todo
|
||||||
|
'pageArray' => $pageArray
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @var PagesExportImport $importer */
|
||||||
|
$importer = $this->wire(new PagesExportImport());
|
||||||
|
$pageArray = $importer->arrayToPages($value, $importOptions);
|
||||||
|
|
||||||
|
foreach($pageArray as $p) {
|
||||||
|
$changes = $p->get('_importChanges');
|
||||||
|
if(!count($changes)) continue;
|
||||||
|
if(isset($itemsAdded[$p->id]) || !$p->id) continue;
|
||||||
|
$numUpdated++;
|
||||||
|
foreach($changes as $fieldName) {
|
||||||
|
if(!isset($changesByField[$fieldName])) $changesByField[$fieldName] = 0;
|
||||||
|
$changesByField[$fieldName]++;
|
||||||
|
}
|
||||||
|
$this->wire()->notices->move($p, $pageArray, array('prefix' => "$field->name (id=$p->id): "));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($numDeleted && $commit) {
|
||||||
|
foreach($itemsDeleted as $p) {
|
||||||
|
$pages->delete($p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($numUpdated) {
|
||||||
|
$updateCounts = array();
|
||||||
|
foreach($changesByField as $fieldName => $count) {
|
||||||
|
$updateCounts[] = "$fieldName ($count)";
|
||||||
|
}
|
||||||
|
$messages[] = "$numUpdated page(s) updated – " . implode(', ', $updateCounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($numAdded) $messages[] = "$numAdded new page(s) added";
|
||||||
|
if($numDeleted) $messages[] = "$numDeleted page(s) DELETED";
|
||||||
|
|
||||||
|
foreach($messages as $message) {
|
||||||
|
$pageArray->message("$field->name: $message");
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageArray->resetTrackChanges();
|
||||||
|
|
||||||
|
$totalChanges = $numUpdated + $numAdded + $numDeleted;
|
||||||
|
if(!$totalChanges) {
|
||||||
|
// prevent it from being counted as a change when import code sets the value back to the page
|
||||||
|
$page->setQuietly($field->name, $pageArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pageArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -44,6 +44,13 @@ function InputfieldRepeater($) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var insertTimeout = null;
|
var insertTimeout = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page version, if PagesVersions active
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
var pageVersion = 0;
|
||||||
|
|
||||||
|
|
||||||
/*** EVENTS ********************************************************************************************/
|
/*** EVENTS ********************************************************************************************/
|
||||||
@@ -363,6 +370,7 @@ function InputfieldRepeater($) {
|
|||||||
|
|
||||||
if($repeater.hasClass('InputfieldRenderValueMode')) ajaxURL += '&inrvm=1';
|
if($repeater.hasClass('InputfieldRenderValueMode')) ajaxURL += '&inrvm=1';
|
||||||
if($repeater.hasClass('InputfieldNoDraft')) ajaxURL += '&nodraft=1';
|
if($repeater.hasClass('InputfieldNoDraft')) ajaxURL += '&nodraft=1';
|
||||||
|
if(pageVersion) ajaxURL += '&version=' + pageVersion;
|
||||||
|
|
||||||
$spinner.removeClass('fa-arrows').addClass('fa-spin fa-spinner');
|
$spinner.removeClass('fa-arrows').addClass('fa-spin fa-spinner');
|
||||||
repeaterID = repeaterID.replace(/_repeater\d+$/, '').replace('_LPID' + pageID, '');
|
repeaterID = repeaterID.replace(/_repeater\d+$/, '').replace('_LPID' + pageID, '');
|
||||||
@@ -486,6 +494,8 @@ function InputfieldRepeater($) {
|
|||||||
var fieldName = getRepeaterFieldName($inputfieldRepeater);
|
var fieldName = getRepeaterFieldName($inputfieldRepeater);
|
||||||
var $spinner = $addLink.parent().find('.InputfieldRepeaterSpinner');
|
var $spinner = $addLink.parent().find('.InputfieldRepeaterSpinner');
|
||||||
var ajaxURL = ProcessWire.config.InputfieldRepeater.editorUrl + '?id=' + pageID + '&field=' + fieldName;
|
var ajaxURL = ProcessWire.config.InputfieldRepeater.editorUrl + '?id=' + pageID + '&field=' + fieldName;
|
||||||
|
|
||||||
|
if(pageVersion) ajaxURL += '&version=' + pageVersion;
|
||||||
|
|
||||||
$spinner.removeClass($spinner.attr('data-off')).addClass($spinner.attr('data-on'));
|
$spinner.removeClass($spinner.attr('data-off')).addClass($spinner.attr('data-on'));
|
||||||
|
|
||||||
@@ -1576,6 +1586,10 @@ function InputfieldRepeater($) {
|
|||||||
*/
|
*/
|
||||||
function init() {
|
function init() {
|
||||||
|
|
||||||
|
if(typeof ProcessWire.config.PagesVersions !== 'undefined') {
|
||||||
|
pageVersion = ProcessWire.config.PagesVersions.version;
|
||||||
|
}
|
||||||
|
|
||||||
$('.InputfieldRepeater').each(function() {
|
$('.InputfieldRepeater').each(function() {
|
||||||
initRepeater($(this));
|
initRepeater($(this));
|
||||||
});
|
});
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -673,6 +673,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$clonePage = null;
|
$clonePage = null;
|
||||||
$cloneToParent = null;
|
$cloneToParent = null;
|
||||||
$readyPage = null;
|
$readyPage = null;
|
||||||
|
$version = (int) ((string) $this->page->get("_repeater_version|_version"));
|
||||||
|
|
||||||
if($cloneItemID) {
|
if($cloneItemID) {
|
||||||
foreach($value as $item) {
|
foreach($value as $item) {
|
||||||
@@ -683,6 +684,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
}
|
}
|
||||||
if($cloneToParentID && $cloneToParentID != $this->page->id) {
|
if($cloneToParentID && $cloneToParentID != $this->page->id) {
|
||||||
$cloneToParent = $this->wire()->pages->get((int) $cloneToParentID);
|
$cloneToParent = $this->wire()->pages->get((int) $cloneToParentID);
|
||||||
|
if($version) $cloneToParent->setQuietly("_repeater_version", $version);
|
||||||
if($cloneToParent->id && $cloneToParent->hasField($this->field) && $cloneToParent->editable($this->field)) {
|
if($cloneToParent->id && $cloneToParent->hasField($this->field) && $cloneToParent->editable($this->field)) {
|
||||||
// ok
|
// ok
|
||||||
$fieldtype = $this->field->type; /** @var FieldtypeRepeater $fieldtype */
|
$fieldtype = $this->field->type; /** @var FieldtypeRepeater $fieldtype */
|
||||||
@@ -710,8 +712,10 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$notIDs = $this->wire()->sanitizer->intArray(explode(',', trim((string) $this->wire()->input->get('repeater_not'), ',')));
|
$notIDs = $this->wire()->sanitizer->intArray(explode(',', trim((string) $this->wire()->input->get('repeater_not'), ',')));
|
||||||
$readyPage = $this->getNextReadyPage($notIDs);
|
$readyPage = $this->getNextReadyPage($notIDs);
|
||||||
$readyPage->removeStatus(Page::statusHidden);
|
$readyPage->removeStatus(Page::statusHidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($version) $readyPage->set('_repeater_version', $version);
|
||||||
|
|
||||||
if($readyPage) {
|
if($readyPage) {
|
||||||
// ensure editing page doesn't get saved (just in case) since we're removing all items
|
// ensure editing page doesn't get saved (just in case) since we're removing all items
|
||||||
$this->page->addStatus(Page::statusCorrupted);
|
$this->page->addStatus(Page::statusCorrupted);
|
||||||
@@ -820,6 +824,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
|
|
||||||
$this->wire()->config->js('InputfieldRepeater', array(
|
$this->wire()->config->js('InputfieldRepeater', array(
|
||||||
'editorUrl' => $editorUrl,
|
'editorUrl' => $editorUrl,
|
||||||
|
'pageVersion' => (int) ((string) $this->page->get('_version')),
|
||||||
'labels' => array(
|
'labels' => array(
|
||||||
'remove' => $this->_x('Click to delete this item, or double-click to delete all', 'repeater-item-action'),
|
'remove' => $this->_x('Click to delete this item, or double-click to delete all', 'repeater-item-action'),
|
||||||
'removeAll' => $this->_x('Delete all items?', 'repeater-item-action'),
|
'removeAll' => $this->_x('Delete all items?', 'repeater-item-action'),
|
||||||
|
Reference in New Issue
Block a user