1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-14 10:45:54 +02:00

Minor code improvements to PageTable, plus add PR #266

Co-authored-by: jmartsch <jmartsch@gmail.com>
This commit is contained in:
Ryan Cramer
2023-06-16 11:45:58 -04:00
parent fde9c1c2e2
commit 3fd4073f34
4 changed files with 262 additions and 150 deletions

View File

@@ -7,7 +7,7 @@
* Code by Ryan Cramer
* Sponsored by Avoine
*
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
* https://processwire.com
*
*/
@@ -21,7 +21,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
'summary' => 'A fieldtype containing a group of editable pages.',
'installs' => 'InputfieldPageTable',
'autoload' => true,
);
);
}
/**
@@ -29,13 +29,25 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function init() {
$pages = $this->wire('pages');
$pages = $this->wire()->pages;
$pages->addHookAfter('delete', $this, 'hookPagesDelete');
$pages->addHookAfter('deleteReady', $this, 'hookPagesDeleteReady');
$pages->addHookAfter('trashed', $this, 'hookPagesTrashed');
$pages->addHookAfter('unpublished', $this, 'hookPagesUnpublished');
$pages->addHookAfter('published', $this, 'hookPagesPublished');
$pages->addHookAfter('cloned', $this, 'hookPagesCloned');
parent::init();
}
/**
* Get class name to use Field objects of this type (must be class that extends Field class)
*
* @param array $a Field data from DB (if needed)
* @return string Return class name or blank to use default Field class
*
*/
public function getFieldClass(array $a = array()) {
return 'PageTableField';
}
/**
@@ -47,12 +59,13 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function hookPagesDelete(HookEvent $event) {
$page = $event->arguments(0);
foreach($this->wire('fields') as $field) {
$database = $this->wire()->database;
$page = $event->arguments(0); /** @var Page $page */
foreach($this->wire()->fields as $field) {
if(!$field->type instanceof FieldtypePageTable) continue;
$table = $this->wire('database')->escapeTable($field->table);
$table = $database->escapeTable($field->table);
$sql = "DELETE FROM `$table` WHERE pages_id=:pages_id OR data=:data";
$query = $this->wire('database')->prepare($sql);
$query = $database->prepare($sql);
$query->bindValue(':pages_id', (int) $page->id);
$query->bindValue(':data', (int) $page->id);
$query->execute();
@@ -70,8 +83,12 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function hookPagesDeleteReady(HookEvent $event) {
$page = $event->arguments(0);
$pages = $this->wire()->pages;
$page = $event->arguments(0); /** @var Page $page */
foreach($page->template->fieldgroup as $field) {
/** @var PageTableField $field */
if(!$field->type instanceof FieldtypePageTable) continue;
if(is_null($field->trashOnDelete) && !is_null($field->autoTrash)) $field->trashOnDelete = $field->autoTrash;
if(!$field->parent_id || !$field->trashOnDelete) continue;
@@ -81,18 +98,18 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
/** @var Page $item */
$deleted = false;
if($field->trashOnDelete == 2) {
$this->wire('pages')->message("Auto Delete PageTable Item: $item->url", Notice::debug);
$pages->message("Auto Delete PageTable Item: $item->url", Notice::debug);
try {
$this->wire('pages')->delete($item);
$pages->delete($item);
$deleted = true;
} catch(\Exception $e) {
$this->wire('pages')->error($e->getMessage(), Notice::debug);
$pages->error($e->getMessage(), Notice::debug);
}
}
if(!$deleted) {
if($item->isTrash()) continue;
$this->wire('pages')->message("Auto Trash PageTable Item: $item->url", Notice::debug);
$this->wire('pages')->trash($item);
$pages->message("Auto Trash PageTable Item: $item->url", Notice::debug);
$pages->trash($item);
}
}
}
@@ -105,15 +122,19 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function hookPagesTrashed(HookEvent $event) {
$page = $event->arguments(0);
$pages = $this->wire()->pages;
$page = $event->arguments(0); /** @var Page $page */
foreach($page->template->fieldgroup as $field) {
if(!$field->type instanceof FieldtypePageTable) continue;
/** @var PageTableField $field */
if(!$field->parent_id || !$field->unpubOnTrash) continue;
$value = $page->getUnformatted($field->name);
if(!wireCount($value)) continue;
foreach($value as $item) {
/** @var Page $item */
$this->wire('pages')->message("Auto Unpublish PageTable Item: $item->url", Notice::debug);
$pages->message("Auto Unpublish PageTable Item: $item->url", Notice::debug);
$of = $item->of();
$item->of(false);
$item->addStatus(Page::statusUnpublished);
@@ -130,10 +151,15 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function hookPagesUnpublished(HookEvent $event) {
$page = $event->arguments(0);
if($this->wire('pages')->cloning) return;
$pages = $this->wire()->pages;
$page = $event->arguments(0); /** @var Page $page */
if($pages->cloning) return;
foreach($page->template->fieldgroup as $field) {
if(!$field->type instanceof FieldtypePageTable) continue;
/** @var PageTableField $field */
if(!$field->parent_id || !$field->unpubOnUnpub) continue;
$value = $page->getUnformatted($field->name);
if(!wireCount($value)) continue;
@@ -142,10 +168,10 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$of = $item->of();
$item->of(false);
if($field->unpubOnUnpub == 2) {
$this->wire('pages')->message("Auto Hide PageTable Item: $item->url", Notice::debug);
$pages->message("Auto Hide PageTable Item: $item->url", Notice::debug);
$item->addStatus(Page::statusHidden);
} else {
$this->wire('pages')->message("Auto Unpublish PageTable Item: $item->url", Notice::debug);
$pages->message("Auto Unpublish PageTable Item: $item->url", Notice::debug);
$item->addStatus(Page::statusUnpublished);
}
$item->save();
@@ -161,9 +187,13 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function hookPagesPublished(HookEvent $event) {
$pages = $this->wire()->pages;
$page = $event->arguments(0);
foreach($page->template->fieldgroup as $field) {
if(!$field->type instanceof FieldtypePageTable) continue;
/** @var PageTableField $field */
if(!$field->parent_id || $field->unpubOnUnpub != 2) continue;
$value = $page->getUnformatted($field->name);
if(!wireCount($value)) continue;
@@ -172,7 +202,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
if(!$item->hasStatus(Page::statusHidden)) continue;
$of = $item->of();
$item->of(false);
$this->wire('pages')->message("Auto Un-hide PageTable Item: $item->url", Notice::debug);
$pages->message("Auto Un-hide PageTable Item: $item->url", Notice::debug);
$item->removeStatus(Page::statusHidden);
$item->save();
$item->of($of);
@@ -193,8 +223,9 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
static $clonedIDs = array();
$page = $event->arguments(0);
$copy = $event->arguments(1);
$pages = $this->wire()->pages;
$page = $event->arguments(0); /** @var Page $page */
$copy = $event->arguments(1); /** @var Page $copy */
if($page) {} // ignore
@@ -203,27 +234,28 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
foreach($copy->template->fieldgroup as $field) {
if(!$field->type instanceof FieldtypePageTable) continue;
/** @var PageTableField $field */
//if(!$field->parent_id) continue; // let that be handled manually since recursive clones are already an option
$parent = $field->parent_id ? $this->wire('pages')->get($field->parent_id) : $copy;
$parent = $field->parent_id ? $pages->get($field->parent_id) : $copy;
$value = $copy->getUnformatted($field->name);
if(!wireCount($value)) continue;
$newValue = $this->wire('pages')->newPageArray();
$newValue = $pages->newPageArray();
foreach($value as $item) {
try {
$newItem = null;
if(!$field->parent_id && $copy->numChildren) {
// value was already cloned by API with recursive option?
$newItem = $this->wire('pages')->get("parent=$copy, name=$item->name, include=all");
$newItem = $pages->get("parent=$copy, name=$item->name, include=all");
if(!$newItem->id) $newItem = null;
}
if(!$newItem) $newItem = $this->wire('pages')->clone($item, $parent);
if(!$newItem) $newItem = $pages->clone($item, $parent);
if($newItem->id) {
$newValue->add($newItem);
$this->wire('pages')->message("Cloned item $item->path", Notice::debug);
$pages->message("Cloned item $item->path", Notice::debug);
}
} catch(\Exception $e) {
$this->wire('pages')->error("Error cloning $item->path");
$this->wire('pages')->error($e->getMessage(), Notice::debug);
$pages->error("Error cloning $item->path");
$pages->error($e->getMessage(), Notice::debug);
}
}
$copy->set($field->name, $newValue);
@@ -238,12 +270,13 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function ready() {
if( $this->wire('config')->ajax &&
$this->wire('input')->get('InputfieldPageTableField') &&
$this->wire('user')->isLoggedin() &&
$this->wire('page')->template == 'admin') {
$config = $this->wire()->config;
if( $config->ajax &&
$this->wire()->input->get('InputfieldPageTableField') &&
$this->wire()->user->isLoggedin() &&
$this->wire()->page->template->name === 'admin') {
// handle ajax request to render table
require_once($this->wire('config')->paths->InputfieldPageTable . 'InputfieldPageTableAjax.php');
require_once($config->paths('InputfieldPageTable') . 'InputfieldPageTableAjax.php');
new InputfieldPageTableAjax();
}
}
@@ -275,14 +308,14 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function getMatchQuery($query, $table, $subfield, $operator, $value) {
return $this->wire('modules')->get('FieldtypePage')->getMatchQuery($query, $table, $subfield, $operator, $value);
return $this->wire()->fieldtypes->FieldtypePage->getMatchQuery($query, $table, $subfield, $operator, $value);
}
/**
* Get the Inputfield used for input by PageTable
*
* @param Page $page
* @param Field $field
* @param Field|PageTableField $field
* @return Inputfield
*
*/
@@ -307,12 +340,16 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
* @param Page $page
* @param Field $field
* @param int|object|string|WireArray $value
* @return int|object|PageArray|string|WireArray
* @return PageArray
*
*/
public function sanitizeValue(Page $page, Field $field, $value) {
if(is_array($value) && wireCount($value)) $value = $this->wakeupValue($page, $field, $value);
if(!$value instanceof PageArray) return $this->wire('pages')->newPageArray();
if(is_array($value) && wireCount($value)) {
$value = $this->wakeupValue($page, $field, $value);
}
if(!$value instanceof PageArray) {
return $this->wire()->pages->newPageArray();
}
foreach($value as $item) {
if($this->isValidItem($page, $field, $item)) continue;
$value->remove($item);
@@ -324,13 +361,12 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
* Return true or false as to whether the item is valid for this PageTable
*
* @param Page $page
* @param Field $field
* @param Field|PageTableField $field
* @param Page $item
* @return bool
*
*/
protected function isValidItem(Page $page, Field $field, Page $item) {
if($page) {} // ignore
$template_id = $field->get('template_id');
if(is_array($template_id)) {
if(in_array($item->template->id, $template_id)) return true;
@@ -350,7 +386,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function getBlankValue(Page $page, Field $field) {
return $this->wire('pages')->newPageArray();
return $this->wire()->pages->newPageArray();
}
/**
@@ -363,7 +399,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function ___formatValue(Page $page, Field $field, $value) {
$formatted = $this->wire('pages')->newPageArray();
$formatted = $this->wire()->pages->newPageArray();
if(!$value instanceof PageArray) return $formatted;
foreach($value as $item) {
if($item->status >= Page::statusHidden) continue;
@@ -377,7 +413,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
* Prep a value for storage
*
* @param Page $page
* @param Field $field
* @param Field|PageTableField $field
* @param PageArray $value
* @throws WireException
* @return array
@@ -385,9 +421,15 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*/
public function ___sleepValue(Page $page, Field $field, $value) {
$sleepValue = array();
if(!$value instanceof PageArray) return $sleepValue;
if($field->get('sortfields')) $value->sort($field->get('sortfields'));
if($value->data('notSaveable')) throw new WireException("Field '$field->name' from page $page->id is not saveable because it is a formatted value.");
if(!$value instanceof PageArray) {
return $sleepValue;
}
if($field->get('sortfields')) {
$value->sort($field->get('sortfields'));
}
if($value->data('notSaveable')) {
throw new WireException("Field '$field->name' from page $page->id is not saveable because it is a formatted value.");
}
foreach($value as $item) {
if(!$item->id) continue;
if(!$this->isValidItem($page, $field, $item)) continue;
@@ -400,14 +442,16 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
* Wake up a stored value
*
* @param Page $page
* @param Field $field
* @param Field|PageTableField $field
* @param array $value
* @return PageArray
*
*/
public function ___wakeupValue(Page $page, Field $field, $value) {
if(!is_array($value) || !wireCount($value) || empty($field->template_id)) return $this->getBlankValue($page, $field);
if(!is_array($value) || !wireCount($value) || empty($field->template_id)) {
return $this->getBlankValue($page, $field);
}
$template_id = $field->get('template_id');
@@ -416,7 +460,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
}
if(wireCount($template_id) == 1) {
$template = $this->wire('templates')->get(reset($template_id));
$template = $this->wire()->templates->get(reset($template_id));
} else {
$template = null;
}
@@ -424,13 +468,14 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$loadOptions = array('cache' => false);
if($template) $loadOptions['template'] = $template;
$items = $this->wire('pages')->getById($value, $loadOptions);
$items = $this->wire()->pages->getById($value, $loadOptions);
$sanitizer = $this->wire()->sanitizer;
$sortfields = $field->get('sortfields');
if($sortfields) {
$sorts = array();
foreach(explode(',', $sortfields) as $sortfield) {
$sorts[] = $this->wire('sanitizer')->name(trim($sortfield));
$sorts[] = $sanitizer->name(trim($sortfield));
}
if(wireCount($sorts)) $items->sort($sorts);
}
@@ -452,7 +497,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function ___getSelectorInfo(Field $field, array $data = array()) {
$info = $this->wire('modules')->get('FieldtypePage')->getSelectorInfo($field, $data);
$info = $this->wire()->fieldtypes->FieldtypePage->getSelectorInfo($field, $data);
$info['operators'] = array(); // force it to be non selectable, subfields only
return $info;
}
@@ -469,12 +514,13 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function ___exportConfigData(Field $field, array $data) {
$data = $this->wire('fieldtypes')->get('FieldtypePage')->exportConfigData($field, $data);
$data = $this->wire()->fieldtypes->FieldtypePage->exportConfigData($field, $data);
if(isset($data['template_id']) && is_array($data['template_id'])) {
// convert template IDs to names
$templates = $this->wire()->templates;
$names = array();
foreach($data['template_id'] as $id) {
$template = $this->wire('templates')->get((int) $id);
$template = $templates->get((int) $id);
if($template) $names[] = $template->name;
}
$data['template_id'] = $names;
@@ -492,12 +538,15 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function ___importConfigData(Field $field, array $data) {
$templates = $this->wire()->templates;
$templateIDs = array();
if(!empty($data['template_id'])) {
if(!is_array($data['template_id'])) $data['template_id'] = array($data['template_id']);
$errorTemplates = array();
foreach($data['template_id'] as $name) {
$template = $this->wire('templates')->get($name);
$template = $templates->get($name);
if($template) {
$templateIDs[] = $template->id;
} else {
@@ -509,8 +558,10 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$data['errors']['template_id'] = "Unable to find template(s): " . implode(', ', $errorTemplates);
}
}
$data = $this->wire('fieldtypes')->get('FieldtypePage')->importConfigData($field, $data);
$data = $this->wire()->fieldtypes->FieldtypePage->importConfigData($field, $data);
$data['template_id'] = $templateIDs;
return $data;
}
@@ -526,29 +577,43 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function findOrphans(Page $page, Field $field) {
$orphans = $this->wire('pages')->newPageArray();
$pages = $this->wire()->pages;
$orphans = $pages->newPageArray();
if($field->get('parent_id')) return $orphans;
$templateID = $field->get('template_id');
if(!$templateID) return $orphans; // we need at least a template to do this
if(!is_array($templateID)) $templateID = array($templateID);
$value = $page->getUnformatted($field->name);
if(!$value instanceof PageArray) $value = $this->wire('pages')->newPageArray();
if(!$value instanceof PageArray) $value = $pages->newPageArray();
if($page->numChildren <= $value->count()) return $orphans; // nothing new
$templateNames = array();
$templates = $this->wire()->templates;
foreach($templateID as $id) {
$template = $this->wire('templates')->get($id);
$template = $templates->get($id);
if($template) $templateNames[] = $template->name;
}
$selector = "include=unpublished, template=" . implode('|', $templateNames);
if($value->count()) $selector .= ", id!=$value";
foreach($page->children($selector) as $item) $orphans->add($item);
foreach($page->children($selector) as $item) {
$orphans->add($item);
}
return $orphans;
}
/**
* Return configuration fields definable for each FieldtypePage
*
* @param Field $field
* @param Field|PageTableField $field
* @return InputfieldWrapper
*
*/
@@ -563,11 +628,10 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$inputfields = parent::___getConfigInputfields($field);
/** @var InputfieldAsmSelect $f */
$f = $this->wire('modules')->get('InputfieldAsmSelect');
$f = $inputfields->InputfieldAsmSelect;
$f->attr('name', 'template_id');
$f->label = $this->_('Select one or more templates for items');
foreach($this->wire('templates') as $template) {
foreach($this->wire()->templates as $template) {
if($template->flags & Template::flagSystem) continue;
$f->addOption($template->id, $template->name);
}
@@ -579,8 +643,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$f->notes = $this->_('Please hit Save after selecting a template and the remaining configuration on the Input tab will contain more context.'); // Templates selection notes
$inputfields->add($f);
/** @var InputfieldPageListSelect $f */
$f = $this->wire('modules')->get('InputfieldPageListSelect');
$f = $inputfields->InputfieldPageListSelect;
$f->attr('name', 'parent_id');
$f->label = $this->_('Select a parent for items');
$f->description = $this->_('All items created and managed from this field will live under the parent you select here.');
@@ -589,20 +652,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$f->attr('value', (int) $field->get('parent_id'));
$inputfields->add($f);
/*
$f = $this->wire('modules')->get('InputfieldCheckbox');
$f->attr('name', 'autoTrash');
$f->attr('value', 1);
if($field->autoTrash) $f->attr('checked', 'checked');
$f->label = $this->_('Trash items when page is deleted?');
$f->description = $this->_('When checked, items created/managed by a given page will be automatically trashed when that page is deleted. If not checked, the items will remain under the parent you selected above.'); // autoTrash option description
$f->notes = $this->_('This option applies only if you have selected a parent above.');
$f->collapsed = Inputfield::collapsedBlank;
$inputfields->add($f);
*/
/** @var InputfieldFieldset $fieldset */
$fieldset = $this->wire('modules')->get('InputfieldFieldset');
$fieldset = $inputfields->InputfieldFieldset;
$fieldset->label = $this->_('Page behaviors');
$fieldset->showIf = 'parent_id!=""';
$inputfields->add($fieldset);
@@ -613,10 +663,9 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
'delete' => $this->_('Delete them'),
'unpub' => $this->_('Unpublish them'),
'hide' => $this->_('Hide them'),
);
);
/** @var InputfieldRadios $f */
$f = $this->wire('modules')->get('InputfieldRadios');
$f = $inputfields->InputfieldRadios;
$f->attr('name', 'trashOnDelete');
$f->label = $this->_('Delete');
$f->description = sprintf($this->_('What should happen to "%s" items when the containing page is permanently deleted?'), $field->name);
@@ -626,8 +675,9 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$f->attr('value', (int) $field->get('trashOnDelete')); // aka autoTrash
$f->columnWidth = 33;
$fieldset->add($f);
unset($f);
$f = $this->wire('modules')->get('InputfieldRadios');
$f = $inputfields->InputfieldRadios;
$f->attr('name', 'unpubOnTrash');
$f->label = $this->_('Trash');
$f->description = sprintf($this->_('What should happen to "%s" items when the containing page is trashed?'), $field->name);
@@ -636,8 +686,9 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$f->attr('value', (int) $field->get('unpubOnTrash'));
$f->columnWidth = 33;
$fieldset->add($f);
unset($f);
$f = $this->wire('modules')->get('InputfieldRadios');
$f = $inputfields->InputfieldRadios;
$f->attr('name', 'unpubOnUnpub');
$f->label = $this->_('Unpublish');
$f->description = sprintf($this->_('What should happen to "%s" items when the containing page is unpublished?'), $field->name);
@@ -648,8 +699,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$f->columnWidth = 33;
$fieldset->add($f);
/** @var InputfieldText $f */
$f = $this->wire('modules')->get('InputfieldText');
$f = $inputfields->InputfieldText;
$f->attr('name', 'sortfields');
$f->label = $this->_('Sort fields');
$f->description = $this->_('Enter the field name that you want your table to sort by. For a descending sort, precede the field name with a hyphen, i.e. "-date" rather than "date".'); // sort description 1
@@ -661,7 +711,6 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
return $inputfields;
}
}
require_once(__DIR__ . '/PageTableField.php');

View File

@@ -0,0 +1,26 @@
<?php namespace ProcessWire;
/**
* Page Table Field (for FieldtypePageTable)
*
* Configured with FieldtypePageTable
* ==================================
* @property int|bool $autoTrash Deprecated, replaced by trashOnDelete
* @property int $trashOnDelete
* @property int $unpubOnTrash
* @property int $unpubOnUnpub
* @property int|array $template_id
* @property int $parent_id
* @property string $sortfields
*
* Configured with InputfieldPageTable
* ===================================
* @property string $columns
* @property string $nameFormat
* @property int $noclose
*
* @since 3.0.221
*
*/
class PageTableField extends Field {
}

View File

@@ -7,7 +7,7 @@
* Code by Ryan Cramer
* Sponsored by Avoine
*
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
* https://processwire.com
*
* @todo add renderValue support (perhaps delegating to Fieldtype::markupValue), likewise for repeaters.
@@ -31,7 +31,7 @@ class InputfieldPageTable extends Inputfield {
'summary' => __('Inputfield to accompany FieldtypePageTable', __FILE__), // Module Summary
'version' => 14,
'requires' => 'FieldtypePageTable'
);
);
}
/**
@@ -103,14 +103,15 @@ class InputfieldPageTable extends Inputfield {
'parent' => $this->_x('Parent', 'th'),
'numChildren' => $this->_x('Children', 'th'),
'status' => $this->_x('Status', 'th'),
);
);
parent::init();
}
public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
$this->addClass('InputfieldNoFocus', 'wrapClass');
$this->wire('modules')->get('JqueryUI')->use('modal');
$jQueryUI = $this->wire()->modules->get('JqueryUI'); /** @var JqueryUI $jQueryUI */
$jQueryUI->use('modal');
return parent::renderReady($parent, $renderValueMode);
}
@@ -122,6 +123,12 @@ class InputfieldPageTable extends Inputfield {
*/
public function ___render() {
$sanitizer = $this->wire()->sanitizer;
$modules = $this->wire()->modules;
$process = $this->wire()->process;
$config = $this->wire()->config;
$input = $this->wire()->input;
// make sure we've got enough info to generate a table
$errors = array();
if(!count($this->rowTemplates)) $errors[] = $this->_('Please configure this field with a template selection before using it.');
@@ -131,6 +138,7 @@ class InputfieldPageTable extends Inputfield {
// determine what columns we'll show in the table
$columnsString = $this->columns ? $this->columns : $this->getConfigDefaultColumns();
$columns = array();
foreach(explode("\n", $columnsString) as $column) {
$width = 0;
if(strpos($column, '=') !== false) list($column, $width) = explode('=', $column);
@@ -143,38 +151,42 @@ class InputfieldPageTable extends Inputfield {
$out = $this->renderTable($columns);
if($this->renderValueMode) return $out;
$editID = (int) $this->wire('input')->get('id');
if(!$editID && $this->wire('process') instanceof WirePageEditor) $editID = $this->wire('process')->getPage()->id;
$editID = (int) $input->get('id');
if(!$editID && $process instanceof WirePageEditor) $editID = $process->getPage()->id;
$parentID = $this->parent_id ? $this->parent_id : $editID;
// render the 'Add New' buttons for each template
$btn = '';
foreach($this->rowTemplates as $template) {
/** @var Template $template */
$button = $this->wire('modules')->get('InputfieldButton');
/** @var InputfieldButton $button */
$button = $modules->get('InputfieldButton');
$button->icon = 'plus-circle';
$button->value = count($this->rowTemplates) == 1 ? $this->_x('Add New', 'button') : $template->getLabel();
$url = $this->wire('config')->urls->admin . "page/add/?modal=1&template_id=$template->id&parent_id=$parentID&context=PageTable";
if($this->nameFormat) $url .= "&name_format=" . $this->wire('sanitizer')->entities($this->nameFormat);
$url = $config->urls->admin . "page/add/?modal=1&template_id=$template->id&parent_id=$parentID&context=PageTable";
if($this->nameFormat) $url .= "&name_format=" . $sanitizer->entities($this->nameFormat);
$btn .= "<span class='InputfieldPageTableAdd' data-url='$url'>" . $button->render() . "</span>";
}
if(count($this->rowTemplates) > 1) $btn = "<small>$btn</small>";
$out .= "<div class='InputfieldPageTableButtons ui-helper-clearfix'>$btn</div>";
if(!$this->wire('input')->get('InputfieldPageTableField')) {
if(!$input->get('InputfieldPageTableField')) {
$url = "./?id=$editID&InputfieldPageTableField=$this->name";
$out = "<div class='InputfieldPageTableContainer' data-url='$url' data-noclose='$this->noclose'>$out</div>";
// input for sorting purposes
$value = $this->wire('sanitizer')->entities($this->attr('value'));
$name = $this->wire('sanitizer')->entities($this->attr('name'));
$value = $sanitizer->entities($this->attr('value'));
$name = $sanitizer->entities($this->attr('name'));
$out .= "<input type='hidden' name='$name' class='InputfieldPageTableSort' value='$value' />";
$out .= "<input type='hidden' name='{$name}__delete' class='InputfieldPageTableDelete' value='' />";
if($this->orphans && count($this->orphans)) {
$out .= "<p class='InputfieldPageTableOrphans'>";
$out .= "<span>" . $this->_('Children were found that may be added to this table. Check the box next to any you would like to add.') . "</span> ";
if(count($this->orphans) > 1) $out .= "<br /><a class='InputfieldPageTableOrphansAll' href='#'>" . $this->_('Select all') . "</a> ";
if(count($this->orphans) > 1) {
$out .= "<br /><a class='InputfieldPageTableOrphansAll' href='#'>" . $this->_('Select all') . "</a> ";
}
foreach($this->orphans as $item) {
$label = $item->title;
if(!strlen($label)) $label = $item->name;
@@ -208,18 +220,19 @@ class InputfieldPageTable extends Inputfield {
*
*/
protected function ___renderTable(array $columns) {
$fields = $this->wire()->fields;
$this->needsEditColumn = false;
/** @var PageArray $value */
$value = $this->attr('value');
$this->wire('modules')->get('MarkupAdminDataTable'); // for styles
$this->wire()->modules->get('MarkupAdminDataTable'); // for styles
if(!count($value)) return ''; // if nothing in the value, just return blank
// $template = $this->template_id ? $this->wire('templates')->get((int) $this->template_id) : null;
$template = count($this->rowTemplates) > 0 ? reset($this->rowTemplates) : null;
$fields = array();
$labels = array();
$fieldsByCol = array();
$labelsByCol = array();
// populate $fields and $labels
// populate $fieldsByCol and $labelsByCol
foreach($columns as $column => $width) {
$field = null;
@@ -232,7 +245,7 @@ class InputfieldPageTable extends Inputfield {
list($parentFieldName, $fieldName) = explode('.', $column);
if($template) $parentField = $template->fieldgroup->getFieldContext($parentFieldName);
if(!$parentField) $parentField = $this->wire('fields')->get($parentFieldName);
if(!$parentField) $parentField = $fields->get($parentFieldName);
if($parentField) {
$label = $parentField->getLabel();
@@ -248,11 +261,11 @@ class InputfieldPageTable extends Inputfield {
}
if($template) $field = $template->fieldgroup->getFieldContext($fieldName);
if(!$field) $field = $this->wire('fields')->get($fieldName);
if(!$field) $field = $fields->get($fieldName);
if($field) {
$label .= $field->getLabel();
$fields[$column] = $field;
$fieldsByCol[$column] = $field;
} else if(isset($this->nativeLabels[$fieldName])) {
$label .= $this->nativeLabels[$fieldName];
@@ -261,11 +274,11 @@ class InputfieldPageTable extends Inputfield {
$label .= $column;
}
$labels[$column] = $label;
$labelsByCol[$column] = $label;
}
$out = $this->renderTableBody($value, $columns, $fields); // render order intentional
$out = $this->renderTableHead($columns, $labels) . $out;
$out = $this->renderTableBody($value, $columns, $fieldsByCol); // render order intentional
$out = $this->renderTableHead($columns, $labelsByCol) . $out;
return $out;
}
@@ -281,7 +294,8 @@ class InputfieldPageTable extends Inputfield {
protected function renderTableHead(array $columns, array $labels) {
/** @var MarkupAdminDataTable $module */
$module = $this->wire('modules')->get('MarkupAdminDataTable');
$module = $this->wire()->modules->get('MarkupAdminDataTable');
$sanitizer = $this->wire()->sanitizer;
$classes = array();
foreach(array('class', 'addClass', 'responsiveClass', 'responsiveAltClass') as $key) {
$value = $module->settings($key);
@@ -296,7 +310,7 @@ class InputfieldPageTable extends Inputfield {
foreach($columns as $column => $width) {
$attr = $width ? " style='width: $width%'" : '';
$label = $labels[$column];
$out .= "<th$attr>" . $this->wire('sanitizer')->entities($label) . "</th>";
$out .= "<th$attr>" . $sanitizer->entities($label) . "</th>";
}
if(!$this->renderValueMode) $out .= "<th>&nbsp;</th>";
@@ -411,12 +425,14 @@ class InputfieldPageTable extends Inputfield {
$fieldName = $column;
$subfieldName = '';
if(strpos($column, '.') !== false) list($fieldName, $subfieldName) = explode('.', $column);
if(strpos($column, '.') !== false) {
list($fieldName, $subfieldName) = explode('.', $column);
}
if(isset($fields[$column])) {
// custom
/** @var Field $field */
$field = $fields[$column];
$field = $fields[$column]; /** @var Field $field */
$v = $item->getFormatted($fieldName);
$value = (string) $field->type->markupValue($item, $field, $v, $subfieldName);
@@ -446,7 +462,7 @@ class InputfieldPageTable extends Inputfield {
*/
protected function renderItemLink(Page $item, $out, $url = '') {
if(!$url) $url = $this->getItemEditURL($item);
return "<a class='InputfieldPageTableEdit' data-url='$url' href='#'>$out</a>";
return "<a class='InputfieldPageTableEdit' data-url='$url' href='$url'>$out</a>";
}
/**
@@ -457,7 +473,7 @@ class InputfieldPageTable extends Inputfield {
*
*/
protected function getItemEditURL(Page $item) {
return $this->wire('config')->urls->admin . "page/edit/?id=$item->id&modal=1&context=PageTable";
return $this->wire()->config->urls->admin . "page/edit/?id=$item->id&modal=1&context=PageTable";
}
/**
@@ -485,8 +501,9 @@ class InputfieldPageTable extends Inputfield {
$value = (string) $object;
}
$value = $this->wire('sanitizer')->entities(strip_tags($value));
$value = $this->wire()->sanitizer->entities(strip_tags($value));
$value = nl2br($value);
return $value;
}
@@ -499,14 +516,14 @@ class InputfieldPageTable extends Inputfield {
*/
public function ___processInput(WireInputData $input) {
$pages = $this->wire()->pages;
$name = $this->attr('name');
$deleteName = $name . '__delete';
$deleteIDs = explode('|', $input->$deleteName);
$ids = explode('|', $input->$name);
/** @var PageArray $value */
$value = $this->attr('value');
$sorted = $this->wire('pages')->newPageArray();
$value = $this->attr('value'); /** @var PageArray $value */
$sorted = $pages->newPageArray();
$changed = false;
// trash items that have been deleted
@@ -516,7 +533,7 @@ class InputfieldPageTable extends Inputfield {
if($id != $item->id) continue;
if(!$item->deleteable()) continue;
$value->remove($item);
$this->wire('pages')->trash($item);
$pages->trash($item);
$changed = true;
}
}
@@ -536,7 +553,7 @@ class InputfieldPageTable extends Inputfield {
// check for orphans that may have been added
$orphanInputName = $name . '__add_orphan';
$orphanIDs = $input->$orphanInputName;
if($orphanIDs && count($orphanIDs) && $this->orphans) {
if(is_array($orphanIDs) && count($orphanIDs) && $this->orphans) {
$numOrphansAdded = 0;
foreach($orphanIDs as $orphanID) {
foreach($this->orphans as $orphan) {
@@ -560,7 +577,7 @@ class InputfieldPageTable extends Inputfield {
// check if we need to setup a name format for any pages
foreach($value as $n => $item) {
$name = $this->wire('pages')->setupPageName($item, array('format' => $this->nameFormat));
$name = $pages->setupPageName($item, array('format' => $this->nameFormat));
if($name) {
$this->message("Auto assigned name '$name' to item #" . ($n+1), Notice::debug);
$item->save();
@@ -579,11 +596,12 @@ class InputfieldPageTable extends Inputfield {
*
*/
public function set($key, $value) {
if($key == 'template_id' && $value) {
if($key === 'template_id' && $value) {
// convert template_id to $this->rowTemplates array
$templates = $this->wire()->templates;
if(!is_array($value)) $value = array($value);
foreach($value as $id) {
$template = $this->wire('templates')->get($id);
$template = $templates->get($id);
if($template) $this->rowTemplates[$id] = $template;
}
return $this;
@@ -605,8 +623,10 @@ class InputfieldPageTable extends Inputfield {
*/
public function setAttribute($key, $value) {
if($key == 'value') {
if($value === null) $value = $this->wire('pages')->newPageArray();
if(!$value instanceof PageArray) throw new WireException('This Inputfield only accepts a PageArray for its value attribute.');
if($value === null) $value = $this->wire()->pages->newPageArray();
if(!$value instanceof PageArray) {
throw new WireException('This Inputfield only accepts a PageArray for its value attribute.');
}
}
return parent::setAttribute($key, $value);
}
@@ -628,8 +648,11 @@ class InputfieldPageTable extends Inputfield {
$fieldCounts = array();
foreach($this->rowTemplates as $template) {
foreach($template->fieldgroup as $field) {
if(!isset($fieldCounts[$field->name])) $fieldCounts[$field->name] = 1;
else $fieldCounts[$field->name]++;
if(!isset($fieldCounts[$field->name])) {
$fieldCounts[$field->name] = 1;
} else {
$fieldCounts[$field->name]++;
}
}
}
@@ -653,11 +676,16 @@ class InputfieldPageTable extends Inputfield {
public function ___getConfigInputfields() {
$inputfields = parent::___getConfigInputfields();
$f = $this->wire('modules')->get('InputfieldTextarea');
$f = $inputfields->InputfieldTextarea;
$f->attr('name', 'columns');
$f->label = $this->_('Table fields to display in admin');
$f->description = $this->_('Enter the names of the fields (1 per line) that you want to display as columns in the table. To specify a column width for the field, specify "field_name=30" where "30" is the width (in percent) of the column. When specifying widths, make the total of all columns add up to 100.'); // Columns description
$f->notes = $this->_('You may specify any native or custom field. You may also use subfields (field.subfield) with fields that contain multiple properties, like page references.') . ' '; // Columns notes
$f->description =
$this->_('Enter the names of the fields (1 per line) that you want to display as columns in the table.') . ' ' .
$this->_('To specify a column width for the field, specify "field_name=30" where "30" is the width (in percent) of the column.') . ' ' .
$this->_('When specifying widths, make the total of all columns add up to 100.');
$f->notes =
$this->_('You may specify any native or custom field.') . ' ' .
$this->_('You may also use subfields (field.subfield) with fields that contain multiple properties, like page references.') . ' ';
$columns = $this->columns ? $this->columns : $this->getConfigDefaultColumns();
$f->attr('value', $columns);
@@ -674,17 +702,24 @@ class InputfieldPageTable extends Inputfield {
$inputfields->add($f);
$f = $this->wire('modules')->get('InputfieldText');
$f = $inputfields->InputfieldText;
$f->attr('name', 'nameFormat');
$f->attr('value', $this->nameFormat);
$f->label = $this->_('Automatic Page Name Format');
$f->description = $this->_('When populated, pages will be created automatically using this name format whenever a user clicks the "Add New" button. If left blank, the user will be asked to enter a name for the page before it is created.'); // page name format description
$f->notes = $this->_('If the name format contains any non-alphanumeric characters, it is considered to be a [PHP date](http://www.php.net/manual/en/function.date.php) format. If it contains only alphanumeric characters then it will be used directly, with a number appended to the end (when necessary) to ensure uniqueness.'); // page name format notes
$f->description =
$this->_('When populated, pages will be created automatically using this name format whenever a user clicks the "Add New" button.') . ' ' . // page name format description 1
$this->_('If left blank, the user will be asked to enter a name for the page before it is created.'); // page name format description 2
$f->notes =
sprintf(
$this->_('If the name format contains any non-alphanumeric characters, it is considered to be a [PHP date](%s) format.'),
'https://www.php.net/manual/en/datetime.format.php'
). ' ' .
$this->_('If it contains only alphanumeric characters then it will be used directly, with a number appended to the end (when necessary) to ensure uniqueness.'); // page name format notes
$f->notes .= ' ' . $this->_('Example: **Ymd:His** is a good name format for date/time based page names.');
$f->collapsed = Inputfield::collapsedBlank;
$inputfields->add($f);
$f = $this->wire('modules')->get('InputfieldRadios');
$f = $inputfields->InputfieldRadios;
$f->attr('name', 'noclose');
$f->label = $this->_('Modal edit window behavior');
$f->addOption(0, $this->_('Automatically close on save (default)'));

View File

@@ -7,7 +7,7 @@
* Code by Ryan Cramer
* Sponsored by Avoine
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
* https://processwire.com
*
* @method void checkAjax()
@@ -37,20 +37,22 @@ class InputfieldPageTableAjax extends Wire {
*/
protected function ___checkAjax() {
$input = $this->wire('input');
$pages = $this->wire()->pages;
$input = $this->wire()->input;
$fieldName = $input->get('InputfieldPageTableField');
if(!$fieldName) return;
$processPage = $this->wire('page');
$processPage = $this->wire()->page;
if(!in_array('WirePageEditor', wireClassImplements((string) $processPage->process))) return; // not ProcessPageEdit or compatible
$field = $this->wire('fields')->get($this->wire('sanitizer')->fieldName($fieldName));
$field = $this->wire()->fields->get($this->wire()->sanitizer->fieldName($fieldName));
if(!$field || !$field->type instanceof FieldtypePageTable) return; // die('field does not exist or is not FieldtypePageTable');
$pageID = (int) $input->get('id');
if(!$pageID) return; // die('page ID not specified');
$page = $this->wire('pages')->get($pageID);
$page = $pages->get($pageID);
if(!$page->id) return;
if(!$page->editable($field->name)) return;
@@ -59,7 +61,7 @@ class InputfieldPageTableAjax extends Wire {
// check for new item that should be added
$itemID = (int) $input->get('InputfieldPageTableAdd');
if($itemID) $this->addItem($page, $field, $this->wire('pages')->get($itemID));
if($itemID) $this->addItem($page, $field, $pages->get($itemID));
$sort = $input->get('InputfieldPageTableSort');
if(strlen("$sort")) $this->sortItems($page, $field, $sort);
@@ -79,7 +81,7 @@ class InputfieldPageTableAjax extends Wire {
if(!$inputfield) return;
echo $inputfield->render();
if($this->notes) {
echo "<p class='notes'>" . $this->wire('sanitizer')->entities($this->notes) . "</p>";
echo "<p class='notes'>" . $this->wire()->sanitizer->entities($this->notes) . "</p>";
$this->notes = '';
}
exit;
@@ -96,7 +98,7 @@ class InputfieldPageTableAjax extends Wire {
*/
protected function addItem(Page $page, Field $field, Page $item) {
// add an item and save the field
if(!$item->id || $item->createdUser->id != $this->wire('user')->id) return false;
if(!$item->id || $item->createdUser->id !== $this->wire()->user->id) return false;
$value = $page->getUnformatted($field->name);