1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-13 18:24:57 +02:00

Add the following new hooks to $templates, $fields and $fieldgroups: renameReady($item, $oldName, $newName); renamed($item, $oldName, $newName); Added the following new hooks to $fieldgroups: fieldAdded($fieldgroup, $field); fieldRemoved($fieldgroup, $field); Make template selectable for Page fields in InputfieldSelector, plus other minor updates.

This commit is contained in:
Ryan Cramer
2022-01-21 12:21:05 -05:00
parent a7a4055632
commit 39f9cf42f2
7 changed files with 176 additions and 58 deletions

View File

@@ -7,12 +7,16 @@
* #pw-body For full details on all methods available in a Fieldgroup, be sure to also see the `WireArray` class.
* #pw-var $fieldgroups
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* @method int saveContext(Fieldgroup $fieldgroup)
* @method array getExportData(Fieldgroup $fieldgroup)
* @method array setImportData(Fieldgroup $fieldgroup, array $data)
*
* @method void fieldRemoved(Fieldgroup $fieldgroup, Field $field)
* @method void fieldAdded(Fieldgroup $fieldgroup, Field $field)
* @method void sorted(Fieldgroup $fieldgroup, array $oldOrder, array $newOrder)
*
*
*/
@@ -25,8 +29,12 @@ class Fieldgroups extends WireSaveableItemsLookup {
* @var FieldgroupsArray
*
*/
protected $fieldgroupsArray;
protected $fieldgroupsArray;
/**
* Init
*
*/
public function init() {
$this->fieldgroupsArray = $this->wire(new FieldgroupsArray());
$this->load($this->fieldgroupsArray);
@@ -142,54 +150,80 @@ class Fieldgroups extends WireSaveableItemsLookup {
*/
public function ___save(Saveable $item) {
$database = $this->wire('database');
$database = $this->wire()->database;
/** @var Fieldgroup $fieldgroup */
$fieldgroup = $item;
$datas = array();
$fieldsAdded = array();
$fieldsRemoved = array();
/** @var Fieldgroup $item */
if($fieldgroup->id && $fieldgroup->removedFields) {
if($item->id && $item->removedFields) {
foreach($this->wire('templates') as $template) {
if($template->fieldgroup->id !== $item->id) continue;
foreach($item->removedFields as $field) {
foreach($this->wire()->templates as $template) {
if($template->fieldgroup->id !== $fieldgroup->id) continue;
foreach($fieldgroup->removedFields as $field) {
// make sure the field is valid to delete from this template
$error = $this->isFieldNotRemoveable($field, $item, $template);
$error = $this->isFieldNotRemoveable($field, $fieldgroup, $template);
if($error !== false) throw new WireException("$error Save of fieldgroup changes aborted.");
if($field->type) $field->type->deleteTemplateField($template, $field);
$item->finishRemove($field);
$fieldgroup->finishRemove($field);
$fieldsRemoved[] = $field;
}
}
$item->resetRemovedFields();
$fieldgroup->resetRemovedFields();
}
$contextData = array();
if($item->id) {
// save context data
$query = $database->prepare("SELECT fields_id, data FROM fieldgroups_fields WHERE fieldgroups_id=:item_id");
$query->bindValue(":item_id", (int) $item->id, \PDO::PARAM_INT);
if($fieldgroup->id) {
// load context data to populate back after fieldgroup save
$sql = 'SELECT fields_id, data FROM fieldgroups_fields WHERE fieldgroups_id=:fieldgroups_id';
$query = $database->prepare($sql);
$query->bindValue(':fieldgroups_id', (int) $fieldgroup->id, \PDO::PARAM_INT);
$query->execute();
/** @noinspection PhpAssignmentInConditionInspection */
while($row = $query->fetch(\PDO::FETCH_ASSOC)) {
$contextData[$row['fields_id']] = $row['data'];
$fields_id = (int) $row['fields_id'];
$datas[$fields_id] = $row['data'];
}
$query->closeCursor();
}
$result = parent::___save($fieldgroup);
// identify any fields added
foreach($fieldgroup as $field) {
if(!array_key_exists($field->id, $datas)) {
$fieldsAdded[] = $field;
}
}
$result = parent::___save($item);
if(count($contextData)) {
if(count($datas)) {
// restore context data
foreach($contextData as $fields_id => $data) {
$fieldgroups_id = (int) $item->id;
$fields_id = (int) $fields_id;
$query = $database->prepare("UPDATE fieldgroups_fields SET data=:data WHERE fieldgroups_id=:fieldgroups_id AND fields_id=:fields_id"); // QA
$query->bindValue(":data", $data, \PDO::PARAM_STR);
$fieldgroups_id = (int) $fieldgroup->id;
foreach($datas as $fields_id => $data) {
$sql = "UPDATE fieldgroups_fields SET data=:data WHERE fieldgroups_id=:fieldgroups_id AND fields_id=:fields_id";
$query = $database->prepare($sql);
if($data === null) {
$query->bindValue(":data", null, \PDO::PARAM_NULL);
} else {
$query->bindValue(":data", $data, \PDO::PARAM_STR);
}
$query->bindValue(":fieldgroups_id", $fieldgroups_id, \PDO::PARAM_INT);
$query->bindValue(":fields_id", $fields_id, \PDO::PARAM_INT);
$query->execute();
}
}
// trigger any fields added
foreach($fieldsAdded as $field) {
$this->fieldAdded($fieldgroup, $field);
}
// trigger any fieldsl removed
foreach($fieldsRemoved as $field) {
$this->fieldRemoved($fieldgroup, $field);
}
return $result;
}
@@ -270,7 +304,7 @@ class Fieldgroups extends WireSaveableItemsLookup {
foreach($contexts as $fieldID => $context) {
$field = $fieldgroup->getFieldContext((int) $fieldID);
if(!$field) continue;
if($this->wire('fields')->saveFieldgroupContext($field, $fieldgroup)) $numSaved++;
if($this->wire()->fields->saveFieldgroupContext($field, $fieldgroup)) $numSaved++;
}
return $numSaved;
}
@@ -486,5 +520,28 @@ class Fieldgroups extends WireSaveableItemsLookup {
return false;
}
/**
* Hook called when field has been added to fieldgroup
*
* #pw-hooker
*
* @param Fieldgroup $fieldgroup
* @param Field $field
* @since 3.0.193
*
*/
public function ___fieldAdded(Fieldgroup $fieldgroup, Field $field) { }
/**
* Hook called when field has been removed from fieldgroup
*
* #pw-hooker
*
* @param Fieldgroup $fieldgroup
* @param Field $field
* @since 3.0.193
*
*/
public function ___fieldRemoved(Fieldgroup $fieldgroup, Field $field) { }
}

View File

@@ -6,7 +6,7 @@
* Wire Data Access Object, provides reusable capability for loading, saving, creating, deleting,
* and finding items of descending class-defined types.
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* @method WireArray load(WireArray $items, $selectors = null)
@@ -20,6 +20,8 @@
* @method void added(Saveable $item) #pw-hooker
* @method void deleted(Saveable $item) #pw-hooker
* @method void cloned(Saveable $item, Saveable $copy) #pw-hooker
* @method void renameReady(Saveable $item, $oldName, $newName)
* @method void renamed(Saveable $item, $oldName, $newName)
*
*
*/
@@ -256,6 +258,17 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
$this->saveReady($item);
$data = $item->getTableData();
$binds = array();
$namePrevious = false;
if($id && $item->isChanged('name')) {
$query = $database->prepare("SELECT name FROM `$table` WHERE id=:id");
$query->bindValue(':id', $id, \PDO::PARAM_INT);
$query->execute();
$oldName = $query->fetchColumn();
$query->closeCursor();
if($oldName != $item->name) $namePrevious = $oldName;
if($namePrevious) $this->renameReady($item, $namePrevious, $item->name);
}
foreach($data as $key => $value) {
if(!$this->saveItemKey($key)) continue;
@@ -292,6 +305,7 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
}
if($result) {
if($namePrevious) $this->renamed($item, $namePrevious, $item->name);
$this->saved($item);
$this->resetTrackChanges();
} else {
@@ -475,6 +489,16 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
*/
public function ___cloneReady(Saveable $item, Saveable $copy) { }
/**
* Hook that runs right before item is to be renamed.
*
* @param Saveable $item
* @param string $oldName
* @param string $newName
*
*/
public function ___renameReady(Saveable $item, $oldName, $newName) { }
/**
* Hook that runs right after an item has been saved.
*
@@ -517,8 +541,6 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
/**
* Hook that runs right after an item has been cloned.
*
* Unlike after(delete), it has already been confirmed that the item was indeed deleted.
*
* @param Saveable $item
* @param Saveable $copy
*
@@ -526,6 +548,18 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
public function ___cloned(Saveable $item, Saveable $copy) {
$this->log("Cloned '$item->name' to '$copy->name'", $item);
}
/**
* Hook that runs right after an item has been renamed.
*
* @param Saveable $item
* @param string $oldName
* @param string $newName
*
*/
public function ___renamed(Saveable $item, $oldName, $newName) {
$this->log("Renamed $oldName to $newName", $item);
}
/**
* Enables use of $apivar('name') or wire()->apivar('name')

View File

@@ -5,7 +5,7 @@
*
* Provides same functionality as WireSaveableItems except that this class includes joining/modification of a related lookup table
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
*/
@@ -40,12 +40,13 @@ abstract class WireSaveableItemsLookup extends WireSaveableItems {
*/
protected function getLoadQuery($selectors = null) {
$query = parent::getLoadQuery($selectors);
$database = $this->wire('database');
$database = $this->wire()->database;
$table = $database->escapeTable($this->getTable());
$lookupTable = $database->escapeTable($this->getLookupTable());
$lookupField = $database->escapeCol($this->getLookupField());
$query->select("$lookupTable.$lookupField"); // QA
$query->leftjoin("$lookupTable ON $lookupTable.{$table}_id=$table.id ")->orderby("sort"); // QA
$query->leftjoin("$lookupTable ON $lookupTable.{$table}_id=$table.id ")->orderby("sort");
// $query->leftjoin("$lookupTable ON $lookupTable.{$table}_id=$table.id ")->orderby("$table.id, $lookupTable.sort");
return $query;
}
@@ -60,9 +61,9 @@ abstract class WireSaveableItemsLookup extends WireSaveableItems {
*
*/
protected function ___load(WireArray $items, $selectors = null) {
$database = $this->wire()->database;
$query = $this->getLoadQuery($selectors);
$database = $this->wire('database');
$sql = $query->getQuery();
$stmt = $database->prepare($sql);
$stmt->execute();
@@ -70,6 +71,7 @@ abstract class WireSaveableItemsLookup extends WireSaveableItems {
while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
/** @var HasLookupItems $item */
$item = $this->makeBlankItem();
$lookupValue = $row[$lookupField];
unset($row[$lookupField]);
@@ -93,6 +95,7 @@ abstract class WireSaveableItemsLookup extends WireSaveableItems {
$stmt->closeCursor();
$items->setTrackChanges(true);
return $items;
}
@@ -125,7 +128,7 @@ abstract class WireSaveableItemsLookup extends WireSaveableItems {
throw new WireException("$class::save() requires an item that implements HasLookupItems interface");
}
$database = $this->wire('database');
$database = $this->wire()->database;
$lookupTable = $database->escapeTable($this->getLookupTable());
$lookupField = $database->escapeCol($this->getLookupField());
$table = $database->escapeTable($this->getTable());
@@ -141,15 +144,18 @@ abstract class WireSaveableItemsLookup extends WireSaveableItems {
$item_id = (int) $item->id; // reload, in case it was 0 before
$sort = 0;
if($item_id) foreach($item->getLookupItems() as $key => $value) {
$value_id = (int) $value->id;
$query = $database->prepare("INSERT INTO $lookupTable SET {$table}_id=:item_id, $lookupField=:value_id, sort=:sort");
$query->bindValue(":item_id", $item_id);
$query->bindValue(":value_id", $value_id);
$query->bindValue(":sort", $sort);
$query->execute();
if($item_id) {
$sql = "INSERT INTO $lookupTable SET {$table}_id=:item_id, $lookupField=:value_id, sort=:sort";
$query = $database->prepare($sql);
foreach($item->getLookupItems() as $key => $value) {
$value_id = (int) $value->id;
$query->bindValue(":item_id", $item_id, \PDO::PARAM_INT);
$query->bindValue(":value_id", $value_id, \PDO::PARAM_INT);
$query->bindValue(":sort", $sort, \PDO::PARAM_INT);
$query->execute();
$sort++;
}
$this->resetTrackChanges();
$sort++;
}
return $result;
@@ -163,7 +169,7 @@ abstract class WireSaveableItemsLookup extends WireSaveableItems {
*
*/
public function ___delete(Saveable $item) {
$database = $this->wire('database');
$database = $this->wire()->database;
$lookupTable = $database->escapeTable($this->getLookupTable());
$table = $database->escapeTable($this->getTable());
$item_id = (int) $item->id;

View File

@@ -1055,7 +1055,7 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
$value = date('Y-m-d H:i:s', $value);
} else if(in_array($subfield, array('template', 'templates_id'))) {
$template = $this->templates->get($subfield);
$template = $this->templates->get($value);
$value = $template ? $template->id : 0;
$subfield = 'templates_id';
@@ -1235,6 +1235,9 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
*
*/
public function ___getSelectorInfo(Field $field, array $data = array()) {
$pages = $this->wire()->pages;
$templates = $this->wire()->templates;
$info = parent::___getSelectorInfo($field, $data);
if(!isset($data['level'])) $data['level'] = 0;
@@ -1245,29 +1248,34 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
$fieldgroups = array();
$template_ids = self::getTemplateIDs($field);
$parent_id = $field->get('parent_id');
$templateOptions = array();
if($template_ids) {
// determine fieldgroup(s) from template setting
// template_id can be int or array of ints
foreach($template_ids as $tid) {
$template = $this->wire('templates')->get((int) $tid);
if($template) $fieldgroups[] = $template->fieldgroup;
$template = $templates->get((int) $tid);
if(!$template) continue;
$fieldgroups[] = $template->fieldgroup;
$templateOptions[$template->id] = $template->getLabel();
}
} else if($parent_id) {
// determine fieldgroup(s) from family settings
$parent = $this->wire('pages')->get((int) $parent_id);
$parent = $pages->get((int) $parent_id);
if($parent->id) {
foreach($parent->template->childTemplates as $template_id) {
$template = $this->wire('templates')->get((int) $template_id);
$template = $templates->get((int) $template_id);
if(!$template) continue;
$fieldgroups[$template->fieldgroup->id] = $template->fieldgroup;
$templateOptions[$template->id] = $template->getLabel();
}
foreach($this->wire('templates') as $template) {
foreach($templates as $template) {
if(!in_array($parent->template->id, $template->parentTemplates)) continue;
if(!$this->wire('pages')->count("parent=$parent_id, template=$template->id, include=all")) continue;
$fieldgroups[$template->fieldgroup->id] = $template->fieldgroup;
if(!$pages->count("parent=$parent_id, template=$template->id, include=all")) continue;
$fieldgroups[$template->fieldgroup->id] = $template->fieldgroup;
$templateOptions[$template->id] = $template->getLabel();
}
}
}
@@ -1276,6 +1284,12 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
// if no fieldgorups found, then we have no choice but to use all fields
//$fieldgroups[] = $this->wire('fields');
}
if(!count($templateOptions)) {
foreach($templates as $template) {
$templateOptions[$template->id] = $template->getLabel();
}
}
foreach($fieldgroups as $fieldgroup) {
foreach($fieldgroup as $f) {
@@ -1290,7 +1304,14 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
'name' => 'count',
'label' => $this->_('Count'), // Label for 'count' property of a PageArray
'input' => 'number',
);
);
$subfields['templates_id'] = array(
'name' => 'templates_id',
'label' => $this->_('Template'), // Label for 'template' property of a Page
'input' => 'select',
'options' => $templateOptions,
);
$info['subfields'] = $subfields;

View File

@@ -270,7 +270,7 @@ function pwModalWindow(href, options, size) {
$iframe.refresh();
};
$iframe.setTitle = function(title) {
$iframe.dialog('option', 'title', title);
$iframe.dialog('option', 'title', jQuery('<textarea />').text(title).html());
};
return $iframe;

File diff suppressed because one or more lines are too long

View File

@@ -2958,7 +2958,7 @@ class ProcessTemplate extends Process implements ConfigurableModule {
$template->noParents = 1;
$template->parentTemplates = array();
} else {
$template->noParents = 0;
if($template->noParents != 0) $template->noParents = 0;
}
$sortfield = $sanitizer->name($input->post('sortfield'));