Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
274 lines (238 sloc) 7.67 KB
<?php namespace ProcessWire;
/**
* ProcessWire FieldtypeFieldsetOpen and InputfieldFieldsetOpen
*
* Provides a Fieldtype and Inputfield for opening a Fieldset.
*
* For documentation about the fields used in this class, please see:
* /wire/core/Fieldtype.php
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
*
*/
class InputfieldFieldsetOpen extends InputfieldWrapper { }
class FieldtypeFieldsetOpen extends Fieldtype {
/**
* Appended to the name of a 'Close' version of a FieldsetOpen
*
*/
const fieldsetCloseIdentifier = '_END';
public static function getModuleInfo() {
return array(
'title' => 'Fieldset (Open)',
'version' => 101,
'summary' => 'Open a fieldset to group fields. Should be followed by a Fieldset (Close) after one or more fields.',
'permanent' => true,
);
}
/**
* Have the hooks been setup for this Fieldset?
*
*/
protected static $hooked = false;
/**
* Setup hooks to minotor when Fields are added and deleted
*
* But only do it once so that we don't have the same hooks setup when there are multiple Fieldsets
*
*/
public function init() {
if(!self::$hooked && ($this->process == 'ProcessField' || $this->process = 'ProcessTemplate')) {
$this->addProcessHooks();
}
self::$hooked = true;
}
/**
* Add hooks to ProcessField or ProcessTemplate, called only when applicable
*
*/
protected function addProcessHooks() {
$this->addHook('ProcessField::fieldAdded', $this, 'hookFieldAdded');
$this->addHook('ProcessField::fieldDeleted', $this, 'hookFieldDeleted');
$this->addHook('ProcessField::allowFieldInTemplate', $this, 'hookAllowFieldInTemplate');
$this->addHook('ProcessTemplate::fieldAdded', $this, 'hookTemplateFieldAdded');
$this->addHook('ProcessTemplate::fieldRemoved', $this, 'hookTemplateFieldRemoved');
}
public function sanitizeValue(Page $page, Field $field, $value) {
return null;
}
public function getInputfield(Page $page, Field $field) {
$inputfield = $this->wire(new InputfieldFieldsetOpen());
$inputfield->class = $this->className();
return $inputfield;
}
public function savePageField(Page $page, Field $field) {
return true;
}
public function ___getConfigInputfields(Field $field) {
$inputfields = parent::___getConfigInputfields($field);
return $inputfields;
}
public function ___getConfigAdvancedInputfields(Field $field) {
$inputfields = parent::___getConfigAdvancedInputfields($field);
// these really aren't applicable with fieldsets, so remove them
$inputfields->remove($inputfields->get('autojoin'));
$inputfields->remove($inputfields->get('global'));
return $inputfields;
}
public function ___getCompatibleFieldtypes(Field $field) {
$fieldtypes = $this->wire(new Fieldtypes());
foreach($this->wire('fieldtypes') as $fieldtype) {
if($fieldtype instanceof FieldtypeFieldsetOpen) $fieldtypes->add($fieldtype);
}
return $fieldtypes;
}
public function loadPageField(Page $page, Field $field) {
return '';
}
public function getLoadQuery(Field $field, DatabaseQuerySelect $query) {
return $query;
}
/**
* Is given field a fieldset opener?
*
* For hooks to share in determining if this is a field they want to operate on
*
* @param Field $field
* @return bool
*
*/
protected function isFieldset(Field $field) {
return $field->type instanceof FieldtypeFieldsetOpen && !($field->type instanceof FieldtypeFieldsetClose);
}
/**
* Get the Field that closes/terminates the given Fieldset field
*
* @param Field $field
* @param bool $createIfNotExists Create it if it doesn't already exist?
* @return null|Field of type FieldtypeFieldsetClose
*
*/
public function getFieldsetCloseField(Field $field, $createIfNotExists = false) {
if(!$this->isFieldset($field)) return;
$name = $field->name . self::fieldsetCloseIdentifier;
$closer = $this->wire('fields')->get($name);
if(!$closer) {
$closeFieldID = (int) $field->get('closeFieldID');
if($closeFieldID) {
$closer = $this->wire('fields')->get($closeFieldID);
}
}
if(!$closer && $createIfNotExists) {
$closer = $this->wire(new Field());
$closer->type = $this->wire(new FieldtypeFieldsetClose());
$closer->name = $name;
$closer->label = "Close an open fieldset";
$closer->description =
"This field was added automatically to accompany fieldset '$field'. " .
"It should be placed in your template/fieldgroup wherever you want the fieldset to end.";
$closer->set('openFieldID', $field->id);
$closer->save();
}
if($closer && !$field->get('closeFieldID')) {
$field->set('closeFieldID', $closer->id);
$field->save();
}
return $closer;
}
/**
* Hook executed when field is added via ProcessField
*
* @param HookEvent $event
*
*/
public function hookFieldAdded($event) {
$field = $event->arguments[0];
if(!$this->isFieldset($field)) return;
$closer = $this->getFieldsetCloseField($field, true);
$this->message("Also added field '$closer', to accompany '$field'");
}
/**
* Hook executed when field is deleted via ProcessField
*
* @param HookEvent $event
*
*/
public function hookFieldDeleted($event) {
$field = $event->arguments[0];
if(!$this->isFieldset($field)) return;
$closer = $this->getFieldsetCloseField($field);
if($closer) {
$this->wire('fields')->delete($closer);
$this->message("Delete also issued for field '$closer'");
}
}
/**
* Hook executed when field is added to a template via ProcessTemplate
*
* @param HookEvent $event
*
*/
public function hookTemplateFieldAdded($event) {
/** @var Field $field */
/** @var Template $template */
list($field, $template) = $event->arguments;
if(!$this->isFieldset($field)) return;
$closer = $this->getFieldsetCloseField($field);
if(!$closer) return;
if(!$template->fieldgroup->hasField($closer)) {
$template->fieldgroup->add($closer);
$this->message("Also added field '$closer', which should be placed where you want to close fieldset '$field'");
}
}
/**
* Hook executed when field is removed from a template via ProcessTemplate
*
* @param HookEvent $event
*
*/
public function hookTemplateFieldRemoved($event) {
list($field, $template) = $event->arguments;
if(!$this->isFieldset($field)) return;
$closer = $this->getFieldsetCloseField($field);
if(!$closer) return;
$template->fieldgroup->remove($closer);
$this->message("Also removed '$closer', which accompanies '$field'");
}
/**
* Hook to ProcessField::allowFieldInTemplate
*
* @param HookEvent $event
*
*/
public function hookAllowFieldInTemplate(HookEvent $event) {
/** @var Field $field */
// $field = $event->arguments(0);
/** @var Template $template */
// $template = $event->arguments(1);
}
/**
* Hook called by Fields::save() after a field using this type has been renamed
*
* Note that PW already takes care of renaming the field_[name] table.
* Most Fieldtypes don't need to do anything here, but this exists just in case.
*
* #pw-internal
*
* @param Field $field
* @param string $prevName Previous name (current name can be found in $field->name)
*
*/
public function ___renamedField(Field $field, $prevName) {
// avoid ending up in infinite loop, since FieldtypeFieldsetClose extends this
if($this instanceof FieldtypeFieldsetClose) return;
// rename the _END field to match this one
$fields = $this->wire('fields');
$closer = $this->getFieldsetCloseField($field);
if(!$closer) {
$closer = $fields->get($prevName . self::fieldsetCloseIdentifier);
if($closer) {
$closer->name = $field->name . self::fieldsetCloseIdentifier;
$fields->save($closer);
}
}
parent::___renamedField($field, $prevName);
}
}