Skip to content

Commit

Permalink
Updates to the PageTable field per issue #498. Also added ability for…
Browse files Browse the repository at this point in the history
… PageTable to support multiple templates rather than just one. When multiple templates are in use, it gives you a different 'add' button for each of them.
  • Loading branch information
ryancramerdesign committed May 28, 2014
1 parent a5b84e0 commit 36ec37d
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 43 deletions.
40 changes: 40 additions & 0 deletions wire/core/Field.php
Expand Up @@ -402,6 +402,46 @@ public function __isset($key) {
return isset($this->settings[$key]);
}

/**
* Return field label for current language
*
* This is different from $this->label in that it knows about languages (when installed).
*
* @param Page|Language $language Optionally specify a language. If not specified user's current language is used.
* @return string
*
*/
public function getLabel($language = null) {
if(is_null($language)) $language = $this->wire('languages') ? $this->wire('user')->language : null;
if($language) {
$label = $this->get("label$language");
if(!strlen($label)) $label = $this->label;
} else {
$label = $this->label;
}
if(!strlen($label)) $label = $this->name;
return $label;
}

/**
* Return field description for current language
*
* This is different from $this->description in that it knows about languages (when installed).
*
* @param Page|Language $language Optionally specify a language. If not specified user's current language is used.
* @return string
*
*/
public function getDescription($language = null) {
if(is_null($language)) $language = $this->wire('languages') ? $this->wire('user')->language : null;
if($language) {
$description = $this->get("description$language");
if(!strlen($description)) $description = $this->description;
} else {
$description = $this->description;
}
return $description;
}

}

11 changes: 11 additions & 0 deletions wire/core/Interfaces.php
Expand Up @@ -407,3 +407,14 @@ interface WireDatabase {
*/
public function isOperator($str);
}

/**
* Interface for Process modules that can edit pages (ProcessPageEdit being the most obvious)
*
*/
interface WirePageEditor {
/**
* @return Page The current page being edited
*/
public function getPage();
}
5 changes: 3 additions & 2 deletions wire/core/Pagefile.php
Expand Up @@ -144,7 +144,7 @@ public function set($key, $value) {
* Set a description, optionally parsing JSON language-specific descriptions to separate properties
*
* @param string $value
* @param Language Langage to set it for. Omit to determine automatically.
* @param Page|Language Langage to set it for. Omit to determine automatically.
* @return this
*
*/
Expand Down Expand Up @@ -243,7 +243,8 @@ public function description($language = null, $value = null) {
if(empty($v)) continue;
$values[$lang->id] = $v;
}
$value = json_encode($values);
$flags = defined("JSON_UNESCAPED_UNICODE") ? JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES : 0; // more fulltext friendly
$value = json_encode($values, $flags);

} else {
// no languages present so just return string with description
Expand Down
22 changes: 22 additions & 0 deletions wire/core/Template.php
Expand Up @@ -579,6 +579,28 @@ public function getParentPage($checkAccess = false) {
return $foundParent;
}

/**
* Return template label for current language, or specified language if provided
*
* If no template label, return template name.
* This is different from $this->label in that it knows about languages (when installed)
* and it will always return something. If there's no label, you'll still get the name.
*
* @param Page|Language $language Optional, if not used then user's current language is used
* @return string
*
*/
public function getLabel($language = null) {
if(is_null($language)) $language = $this->wire('languages') ? $this->wire('user')->language : null;
if($language) {
$label = $this->get("label$language");
if(!strlen($label)) $label = $this->label;
} else {
$label = $this->label;
}
if(!strlen($label)) $label = $this->name;
return $label;
}

}

Expand Down
45 changes: 32 additions & 13 deletions wire/modules/Fieldtype/FieldtypePageTable.module
Expand Up @@ -211,8 +211,14 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
protected function isValidItem(Page $page, Field $field, Page $item) {
if($item->template->id != $field->template_id) return false;
return true;
$template_id = $field->template_id;
if(is_array($template_id)) {
if(in_array($item->template->id, $template_id)) return true;
} else {
// old style for backwards compatibility
if($template_id == $item->template->id) return true;
}
return false;
}

/**
Expand Down Expand Up @@ -276,9 +282,18 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
*
*/
public function ___wakeupValue(Page $page, Field $field, $value) {
if(!is_array($value) || !count($value) || !$field->template_id) return $this->getBlankValue($page, $field);
$template = $this->wire('templates')->get($field->template_id);
$items = $this->wire('pages')->getById($value, $template);
if(!is_array($value) || !count($value) || empty($field->template_id)) return $this->getBlankValue($page, $field);
$template_id = $field->template_id;
if(!is_array($template_id)) {
$template_id = $template_id ? array($template_id) : array();
}
if(count($template_id) == 1) {
$template = $this->wire('templates')->get(reset($template_id));
$items = $this->wire('pages')->getById($value, $template);
} else {
$items = $this->wire('pages')->getById($value);
}

return $items;
}

Expand Down Expand Up @@ -307,15 +322,19 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {

$inputfields = parent::___getConfigInputfields($field);

$f = $this->wire('modules')->get('InputfieldSelect');
$f = $this->wire('modules')->get('InputfieldAsmSelect');
$f->attr('name', 'template_id');
$f->label = $this->_('Select a template for items');
$f->addOption(0, '');
foreach($this->wire('templates') as $template) $f->addOption($template->id, $template->name);
$f->attr('value', (int) $field->template_id);
$f->label = $this->_('Select one or more templates for items');
foreach($this->wire('templates') as $template) {
if($template->flags & Template::flagSystem) continue;
$f->addOption($template->id, $template->name);
}
$value = $field->template_id;
if(!is_array($value)) $value = $value ? array($value) : array();
$f->attr('value', $value);
$f->required = true;
$f->description = $this->_('This is the template that will be used by pages managed from this field. You may wish to create a new template specific to the needs of this field.');
$f->notes = $this->_('Please hit Save after selecting a template and the remaining configuration on the Input tab will contain more context.');
$f->description = $this->_('These are the templates that will be used by pages managed from this field. You may wish to create a new template specific to the needs of this field.'); // Templates selection description
$f->notes = $this->_('Please hit Save after selecting a template and the remaining configuration on the Input tab will contain more context.'); // Templates selection n otes
$inputfields->add($f);

$f = $this->wire('modules')->get('InputfieldPageListSelect');
Expand All @@ -332,7 +351,7 @@ class FieldtypePageTable extends FieldtypeMulti implements Module {
$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.');
$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);
Expand Down
Expand Up @@ -34,6 +34,14 @@ class InputfieldPageTable extends Inputfield {
*/
protected $nativeLabels = array();

/**
* Array of Template objects used for each row
*
* @var array
*
*/
protected $rowTemplates = array();

/**
* Initialize and establish default values
*
Expand All @@ -42,7 +50,7 @@ class InputfieldPageTable extends Inputfield {

// fieldtype and inputfield config settings
$this->set('parent_id', 0);
$this->set('template_id', 0);
$this->set('template_id', 0); // placeholder only
$this->set('columns', '');
$this->set('nameFormat', '');

Expand Down Expand Up @@ -75,7 +83,7 @@ class InputfieldPageTable extends Inputfield {

// make sure we've got enough info to generate a table
$errors = array();
if(!$this->template_id) $errors[] = $this->_('Please configure this field with a template selection before using it.');
if(!count($this->rowTemplates)) $errors[] = $this->_('Please configure this field with a template selection before using it.');
if(!$this->columns) $errors[] = $this->_('Please enter one or more columns in your field settings before using this field.');
if(count($errors)) return "<p class='ui-state-error'>" . implode('<br />', $errors) . "</p>";

Expand All @@ -91,20 +99,25 @@ class InputfieldPageTable extends Inputfield {
}

// render the table
$out = $this->renderTable($columns);
$out = $this->renderTable($columns);

// render an 'Add New' button
$button = $this->wire('modules')->get('InputfieldButton');
$editID = (int) $this->wire('input')->get('id');
$editID = (int) $this->wire('input')->get('id');
$parentID = $this->parent_id ? $this->parent_id : $editID;
$button->icon = 'plus-circle';
$button->value = $this->_x('Add New', 'button');

$url = "../add/?modal=1&template_id=$this->template_id&parent_id=$parentID";
if($this->nameFormat) $url .= "&name_format=" . $this->wire('sanitizer')->entities($this->nameFormat);
$out .= "<div class='InputfieldPageTableAdd' data-url='$url'>" . $button->render() . "</div>";
// render the 'Add New' buttons for each template
$btn = '';
foreach($this->rowTemplates as $template) {
$button = $this->wire('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";
if($this->nameFormat) $url .= "&name_format=" . $this->wire('sanitizer')->entities($this->nameFormat);
$btn .= "<span class='InputfieldPageTableAdd' data-url='$url'>" . $button->render() . "</span>";
}
$out .= "<div class='InputfieldPageTableButtons ui-helper-clearfix'>$btn</div>";

if(!$this->wire('config')->ajax) {
if(!$this->wire('config')->ajax) {
$url = "./?id=$editID&InputfieldPageTableField=$this->name";
$out = "<div class='InputfieldPageTableContainer' data-url='$url'>$out</div>";
// input for sorting purposes
Expand All @@ -130,8 +143,8 @@ class InputfieldPageTable extends Inputfield {
$value = $this->attr('value');
if(!count($value)) return ''; // if nothing in the value, just return blank
$this->wire('modules')->get('MarkupAdminDataTable'); // for styles
$language = $this->wire('languages') ? $this->wire('user')->language : '';
$template = $this->template_id ? $this->wire('templates')->get((int) $this->template_id) : null;
// $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();

Expand All @@ -151,7 +164,7 @@ class InputfieldPageTable extends Inputfield {
if(!$parentField) $parentField = $this->wire('fields')->get($parentFieldName);

if($parentField) {
$label = $parentField->get("label$language|label|name");
$label = $parentField->getLabel();

} else if(isset($this->nativeLabels[$parentFieldName])) {
$label = $this->nativeLabels[$parentFieldName];
Expand All @@ -167,7 +180,7 @@ class InputfieldPageTable extends Inputfield {
if(!$field) $field = $this->wire('fields')->get($fieldName);

if($field) {
$label .= $field->get("label$language|label|name");
$label .= $field->getLabel();
$fields[$column] = $field;

} else if(isset($this->nativeLabels[$fieldName])) {
Expand Down Expand Up @@ -400,6 +413,24 @@ class InputfieldPageTable extends Inputfield {
return $this;
}

/**
* Set a property to this Inputfield
*
*/
public function set($key, $value) {
if($key == 'template_id' && $value) {
// convert template_id to $this->rowTemplates array
if(!is_array($value)) $value = array($value);
foreach($value as $id) {
$template = $this->wire('templates')->get($id);
if($template) $this->rowTemplates[$id] = $template;
}
return $this;
} else {
return parent::set($key, $value);
}
}

/**
* Set an attribute to the Inputfield
*
Expand Down Expand Up @@ -427,12 +458,22 @@ class InputfieldPageTable extends Inputfield {
*/
protected function getConfigDefaultColumns() {
$out = '';
if(!$this->template_id) return $out;
$template = $this->wire('templates')->get((int) $this->template_id);
if(!$template) return $out;
if(!count($this->rowTemplates)) return $out;

$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]++;
}
}

// sort most used to least used
if(count($this->rowTemplates) > 1) arsort($fieldCounts);

$n = 0;
foreach($template->fieldgroup as $field) {
$out .= $field->name . "\n";
foreach(array_keys($fieldCounts) as $fieldName) {
$out .= $fieldName . "\n";
if(++$n >= 5) break;
}
return trim($out);
Expand All @@ -455,12 +496,12 @@ class InputfieldPageTable extends Inputfield {
$columns = $this->columns ? $this->columns : $this->getConfigDefaultColumns();
$f->attr('value', $columns);

$template = $this->template_id ? $this->wire('templates')->get((int) $this->template_id) : null;

if($template) {
if(count($this->rowTemplates)) {
$options = array();
foreach($template->fieldgroup as $item) $options[$item->name] = $item->name;
$f->notes .= $this->_('Custom fields assigned to your selected template include the following:') . ' **';
foreach($this->rowTemplates as $template) {
foreach($template->fieldgroup as $item) $options[$item->name] = $item->name;
}
$f->notes .= $this->_('Custom fields assigned to your selected templates include the following:') . ' **';
$f->notes .= implode(', ', $options) . '**';
} else {
$f->notes .= $this->_('To see a list of possible custom fields here, select a template on the Details tab, Save, and come back here.');
Expand Down
Expand Up @@ -43,7 +43,7 @@ protected function checkAjax() {
if(!$fieldName) return;

$processPage = $this->wire('page');
if($processPage->process != 'ProcessPageEdit') return; // die('process is not ProcessPageEdit');
if(!is_a($processPage->process, 'WirePageEditor', true)) return; // not ProcessPageEdit or compatible

$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');
Expand Down
3 changes: 2 additions & 1 deletion wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module
Expand Up @@ -16,7 +16,7 @@
*
*/

class ProcessPageEdit extends Process {
class ProcessPageEdit extends Process implements WirePageEditor {

protected $form;
protected $page;
Expand Down Expand Up @@ -1144,6 +1144,7 @@ class ProcessPageEdit extends Process {
* Returns true if this page may be ajax saved (user has access), or false if not
*
* @param Page $page
* @param string $fieldName Optional field name
* @return bool
*
*/
Expand Down

3 comments on commit 36ec37d

@owzim
Copy link

@owzim owzim commented on 36ec37d May 29, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Wow, this is crazy! I was just working on a Module that supports multiple templates. I hooked into InputfieldPageTable::render and did not came across the limitation that FieldtypePageTable::isValidItem wasn't hookable, so the items with different templates other than the template assigned to the field wouldn't show up.

My efforts are now obsolete and this is a great addition.

@owzim
Copy link

@owzim owzim commented on 36ec37d May 29, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tested it, great! Funny that I was going for the exact same implementation.

@ryancramerdesign
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. Glad you like it!

Please sign in to comment.