Skip to content
Snippets Groups Projects
Commit 049f0c61 authored by Peter Rotich's avatar Peter Rotich
Browse files

Add custom list item interface

Add an interface for custom list items.
Implement CustomListItem for built-in and custom lists.
parent cb9e3ce1
Branches
Tags
No related merge requests found
......@@ -58,15 +58,27 @@ class DynamicFormsAjaxAPI extends AjaxController {
$ent->delete();
}
function getListItemProperties($item_id) {
if (!($item = DynamicListItem::lookup($item_id)))
function getListItemProperties($list_id, $item_id) {
if (is_numeric($list_id))
$list = DynamicList::lookup($list_id);
else
$list = BuiltInCustomList::lookup($list_id);
if (!($item = $list->getItem($item_id)))
Http::response(404, 'No such list item');
include(STAFFINC_DIR . 'templates/list-item-properties.tmpl.php');
}
function saveListItemProperties($item_id) {
if (!($item = DynamicListItem::lookup($item_id)))
function saveListItemProperties($list_id, $item_id) {
if (is_numeric($list_id))
$list = DynamicList::lookup($list_id);
else
$list = BuiltInCustomList::lookup($list_id);
if (!($item = $list->getItem($item_id)))
Http::response(404, 'No such list item');
if (!$item->setConfiguration())
......
......@@ -448,6 +448,14 @@ class DynamicFormField extends VerySimpleModel {
return $this->get('edit_mask') & 8;
}
function isChangeable() {
return ($this->get('edit_mask') & 16) == 0;
}
function isConfigurable() {
return ($this->get('edit_mask') & 32) == 0;
}
/**
* Used when updating the form via the admin panel. This represents
* validation on the form field template, not data entered into a form
......
......@@ -36,10 +36,14 @@ interface CustomList {
function getAllItems();
function getItems($criteria);
function getItem($id);
function addItem($vars, &$errors);
function getForm(); // Config form
function hasProperties();
function getSortModes();
function getSortMode();
function getListOrderBy();
function isBuiltIn();
......@@ -48,8 +52,35 @@ interface CustomList {
function delete();
static function create($vars, &$errors);
static function lookup($id);
}
/*
* Custom list item interface
*/
interface CustomListItem {
function getId();
function getValue();
function getAbbrev();
function getSortOrder();
function getConfiguration();
function getConfigurationForm();
function isEnabled();
function isDeletable();
function isEnableable();
function isInternal();
function enable();
function disable();
function update($vars, &$errors);
function delete();
}
/*
* Base class for Built-in Custom Lists
*
......@@ -81,11 +112,15 @@ abstract class BuiltInCustomList implements CustomList {
abstract function getAllItems();
abstract function getItems($criteria);
abstract function addItem($vars, &$errors);
abstract function getForm(); // Config form
abstract function hasProperties();
abstract function getListOrderBy();
abstract function getSortMode();
function getSortModes() {
return static::$sort_modes;
}
......@@ -186,8 +221,12 @@ class DynamicList extends VerySimpleModel implements CustomList {
return static::$sort_modes;
}
function getSortMode() {
return $this->sort_mode;
}
function getListOrderBy() {
switch ($this->sort_mode) {
switch ($this->getSortMode()) {
case 'Alpha': return 'value';
case '-Alpha': return '-value';
case 'SortCol': return 'sort';
......@@ -234,7 +273,15 @@ class DynamicList extends VerySimpleModel implements CustomList {
return $this->_items;
}
function addItem($vars) {
function getItem($id) {
return DynamicListItem::lookup(array(
'id' => $id,
'list_id' => $this->getId()));
}
function addItem($vars, &$errors) {
$item = DynamicListItem::create(array(
'list_id' => $this->getId(),
......@@ -250,16 +297,30 @@ class DynamicList extends VerySimpleModel implements CustomList {
return $item;
}
function getConfigurationForm() {
function getConfigurationForm($autocreate=false) {
if (!$this->_form) {
$this->_form = DynamicForm::lookup(
array('type'=>'L'.$this->get('id')));
$this->_form = DynamicForm::lookup(array('type'=>'L'.$this->getId()));
if (!$this->_form
&& $autocreate
&& $this->createConfigurationForm())
return $this->getConfigurationForm(false);
}
return $this->_form;
}
function getForm() {
return $this->getConfigurationForm();
private function createConfigurationForm() {
$form = DynamicForm::create(array(
'type' => 'L'.$this->getId(),
'title' => $this->getName() . ' Properties'
));
return $form->save(true);
}
function getForm($autocreate=true) {
return $this->getConfigurationForm($autocreate);
}
function update($vars, &$errors) {
......@@ -295,6 +356,39 @@ class DynamicList extends VerySimpleModel implements CustomList {
return false;
}
private function createForm() {
$form = DynamicForm::create(array(
'type' => 'L'.$this->getId(),
'title' => $this->getName() . ' Properties'
));
return $form->save(true);
}
static function add($vars, &$errors) {
$required = array('name');
$ht = array();
foreach (static::$fields as $f) {
if (in_array($f, $required) && !$vars[$f])
$errors[$f] = sprintf('%s is required', mb_convert_case($f, MB_CASE_TITLE));
elseif(isset($vars[$f]))
$ht[$f] = $vars[$f];
}
if (!$ht || $errors)
return false;
// Create the list && form
if (!($list = self::create($ht))
|| !$list->save(true)
|| !$list->createConfigurationForm())
return false;
return $list;
}
static function create($ht=false, &$errors=array()) {
$inst = parent::create($ht);
$inst->set('created', new SqlFunction('NOW'));
......@@ -326,7 +420,7 @@ FormField::addFieldTypes('Custom Lists', array('DynamicList', 'getSelections'));
* sort - (int) If sorting by this field, represents the numeric sort order
* that this item should come in the dropdown list
*/
class DynamicListItem extends VerySimpleModel {
class DynamicListItem extends VerySimpleModel implements CustomListItem {
static $meta = array(
'table' => LIST_ITEM_TABLE,
......@@ -356,6 +450,18 @@ class DynamicListItem extends VerySimpleModel {
return $this->set('status', $this->get('status') | $flag);
}
function isInternal() {
return false;
}
function isEnableable() {
return true;
}
function isDeletable() {
return !$this->isInternal();
}
function isEnabled() {
return $this->hasStatus(self::ENABLED);
}
......@@ -371,6 +477,10 @@ class DynamicListItem extends VerySimpleModel {
return $this->get('id');
}
function getListId() {
return $this->get('list_id');
}
function getValue() {
return $this->get('value');
}
......@@ -415,6 +525,10 @@ class DynamicListItem extends VerySimpleModel {
return $this->_form;
}
function getForm() {
return $this->getConfigurationForm();
}
function getVar($name) {
$config = $this->getConfiguration();
$name = mb_strtolower($name);
......@@ -432,7 +546,12 @@ class DynamicListItem extends VerySimpleModel {
return $this->toString();
}
function update($vars, $save = true) {
function update($vars, &$errors=array()) {
if (!$vars['value']) {
$errors['value-'.$this->getId()] = 'Value required';
return false;
}
foreach (array(
'sort' => 'sort',
......@@ -440,13 +559,9 @@ class DynamicListItem extends VerySimpleModel {
'abbrev' => 'extra') as $k => $v) {
if (isset($vars[$k]))
$this->set($v, $vars[$k]);
}
if ($save)
$this->save();
return true;
return $this->save();
}
function delete() {
......@@ -471,9 +586,13 @@ class TicketStatusList extends BuiltInCustomList {
'name' => 'Status',
'name_plural' => 'Statuses',
);
// Fields of interest we need to store
static $config_fields = array('sort_mode', 'notes');
var $_items;
var $_form;
function getId() {
return $this->ht['id'];
}
......
......@@ -22,7 +22,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
<input type="hidden" name="do" value="<?php echo $action; ?>">
<input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
<input type="hidden" name="id" value="<?php echo $info['id']; ?>">
<h2>Custom List: <?php echo $list->getName(); ?></h2>
<h2>Custom List: <?php echo $list ? $list->getName() : 'Add new list'; ?></h2>
<ul class="tabs">
<li><a href="#definition" class="active">
......@@ -48,8 +48,8 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
<td width="180" class="required">Name:</td>
<td>
<?php
if ($list->isBuiltIn())
echo $info['name'];
if ($list && $list->isBuiltIn())
echo $list->getName();
else {
echo sprintf('<input size="50" type="text" name="name"
value="%s"/> <span
......@@ -63,8 +63,8 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
<td width="180">Plural Name:</td>
<td>
<?php
if ($list->isBuiltIn())
echo $info['name_plural'];
if ($list && $list->isBuiltIn())
echo $list->getPluralName();
else
echo sprintf('<input size="50" type="text"
name="name_plural" value="%s"/>',
......@@ -75,7 +75,9 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
<tr>
<td width="180">Sort Order:</td>
<td><select name="sort_mode">
<?php foreach ($list->getSortModes() as $key=>$desc) { ?>
<?php
$sortModes = $list ? $list->getSortModes() : DynamicList::getSortModes();
foreach ($sortModes as $key=>$desc) { ?>
<option value="<?php echo $key; ?>" <?php
if ($key == $info['sort_mode']) echo 'selected="selected"';
?>><?php echo $desc; ?></option>
......@@ -115,7 +117,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
</tr>
</thead>
<tbody class="sortable-rows" data-sort="prop-sort-">
<?php if ($form) foreach ($form->getDynamicFields() as $f) {
<?php if ($list && $form=$list->getForm()) foreach ($form->getDynamicFields() as $f) {
$id = $f->get('id');
$deletable = !$f->isDeletable() ? 'disabled="disabled"' : '';
$force_name = $f->isNameForced() ? 'disabled="disabled"' : '';
......@@ -129,7 +131,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
if ($ferrors['label']) echo '<br/>'; echo $ferrors['label']; ?>
</td>
<td nowrap><select name="type-<?php echo $id; ?>" <?php
if (!$fi->isChangeable()) echo 'disabled="disabled"'; ?>>
if (!$fi->isChangeable() || !$f->isChangeable()) echo 'disabled="disabled"'; ?>>
<?php foreach (FormField::allTypes() as $group=>$types) {
?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php
foreach ($types as $type=>$nfo) {
......@@ -148,6 +150,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
echo $f->get('id'); ?>"
onclick="javascript:
$('#overlay').show();
$('#field-config .body').html('Loading...');
$('#field-config .body').load($(this).attr('href').substr(1));
$('#field-config').show();
return false;
......@@ -161,8 +164,13 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
if ($ferrors['name']) echo '<br/>'; echo $ferrors['name'];
?></font>
</td>
<td><input type="checkbox" name="delete-<?php echo $id; ?>"
<?php echo $deletable; ?>/>
<td>
<?php
if (!$f->isDeletable())
echo '<i class="icon-ban-circle"></i>';
else
echo sprintf('<input type="checkbox" name="delete-prop-%s">', $id);
?>
<input type="hidden" name="prop-sort-<?php echo $id; ?>"
value="<?php echo $f->get('sort'); ?>"/>
</td>
......@@ -220,7 +228,11 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
<tr>
<th></th>
<th>Value</th>
<?php
if (!$list || !$list->isBuiltIn()) { ?>
<th>Abbrev <em style="display:inline">&mdash; Abbreviations and such</em></th>
<?php
} ?>
<th>Disabled</th>
<th>Delete</th>
</tr>
......@@ -242,22 +254,51 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
value="<?php echo $i->getValue(); ?>"/>
<?php if ($list->hasProperties()) { ?>
<a class="action-button" style="float:none;overflow:inherit"
href="#ajax.php/list/item/<?php
echo $id ?>/properties"
href="#ajax.php/list/<?php
echo $list->getId(); ?>/item/<?php echo $id ?>/properties"
onclick="javascript:
$('#overlay').show();
$('#field-config .body').html('Loading...');
$('#field-config .body').load($(this).attr('href').substr(1));
$('#field-config').show();
return false;
"><i class="icon-edit"></i> Properties</a>
<?php } ?></td>
<?php
}
if ($errors["value-$id"])
echo sprintf('<br><span class="error">%s</span>',
$errors["value-$id"]);
?>
</td>
<?php
if (!$list->isBuiltIn()) { ?>
<td><input type="text" size="30" name="abbrev-<?php echo $id; ?>"
value="<?php echo $i->getAbbrev(); ?>"/></td>
<?php
} ?>
<td>
<input type="checkbox" name="disable-<?php echo $id; ?>" <?php
if (!$i->isEnabled()) echo 'checked="checked"'; ?>/></td>
<?php
if ($i->isInternal())
echo '<i class="icon-ban-circle"></i>';
else
echo sprintf('<input type="checkbox" name="disable-%s"
%s %s />',
$id,
!$i->isEnabled() ? ' checked="checked" ' : '',
(!$i->isEnabled() && !$i->isEnableable()) ? ' disabled="disabled" ' : ''
);
?>
</td>
<td>
<input type="checkbox" name="delete-<?php echo $id; ?>"/></td>
<?php
if (!$i->isDeletable())
echo '<i class="icon-ban-circle"></i>';
else
echo sprintf('<input type="checkbox" name="delete-item-%s">', $id);
?>
</td>
</tr>
<?php }
}
......@@ -266,9 +307,13 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info)
<td><?php echo $icon; ?> <em>+</em>
<input type="hidden" name="sort-new-<?php echo $i; ?>"/></td>
<td><input type="text" size="40" name="value-new-<?php echo $i; ?>"/></td>
<?php
if (!$list || !$list->isBuiltIn()) { ?>
<td><input type="text" size="30" name="abbrev-new-<?php echo $i; ?>"/></td>
<td></td>
<td></td>
<?php
} ?>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<?php } ?>
</tbody>
......
<h3>Item Properties &mdash; <?php echo $item->get('value') ?></h3>
<h3>Item Properties &mdash; <?php echo $item->getValue(); ?></h3>
<a class="close" href=""><i class="icon-remove-circle"></i></a>
<hr/>
<form method="post" action="ajax.php/list/item/<?php
echo $item->get('id'); ?>/properties" onsubmit="javascript:
<form method="post" action="ajax.php/list/<?php
echo $list->getId(); ?>/item/<?php
echo $item->getId(); ?>/properties" onsubmit="javascript:
var form = $(this);
$.post(this.action, form.serialize(), function(data, status, xhr) {
if (!data.length) {
......
......@@ -57,8 +57,8 @@ $dispatcher = patterns('',
url_delete('^answer/(?P<entry>\d+)/(?P<field>\d+)$', 'deleteAnswer')
)),
url('^/list/', patterns('ajax.forms.php:DynamicFormsAjaxAPI',
url_get('^item/(?P<id>\d+)/properties$', 'getListItemProperties'),
url_post('^item/(?P<id>\d+)/properties$', 'saveListItemProperties')
url_get('^(?P<list>\w+)/item/(?P<id>\d+)/properties$', 'getListItemProperties'),
url_post('^(?P<list>\w+)/item/(?P<id>\d+)/properties$', 'saveListItemProperties')
)),
url('^/report/overview/', patterns('ajax.reports.php:OverviewReportAjaxAPI',
# Send
......
......@@ -16,112 +16,108 @@ if ($_REQUEST['id']) {
$errors['err'] = 'Unknown or invalid dynamic list ID.';
}
$errors = array();
$max_isort = 0;
if($_POST) {
$fields = array('name', 'name_plural', 'sort_mode', 'notes');
$required = array('name');
switch(strtolower($_POST['do'])) {
case 'update':
if ($list->update($_POST, $errors))
$msg = 'Custom list updated successfully';
elseif ($errors)
$errors['err'] = 'Unable to update custom list. Correct any error(s) below and try again.';
else
$errors['err'] = 'Unable to update custom list. Unknown internal error';
if ($list->getNumItems()) {
if (!$list)
$errors['err'] = 'Unknown or invalid list';
elseif ($list->update($_POST, $errors)) {
// Update items
$items = array();
foreach ($list->getAllItems() as $item) {
$id = $item->getId();
if ($_POST["delete-$id"] == 'on') {
if ($_POST["delete-item-$id"] == 'on' && $item->isDeletable()) {
$item->delete();
continue;
}
$item->update(array(
'value' => $_POST["value-$id"],
'abbrev' => $_POST["abbrev-$id"],
'sort' => $_POST["name-$id"],
),
false);
if ($_POST["disable-$id"] == 'on')
$item->disable();
else
$item->enable();
$ht = array(
'value' => $_POST["value-$id"],
'abbrev' => $_POST["abbrev-$id"],
'sort' => $_POST["sort-$id"],
);
$value = mb_strtolower($ht['value']);
if (!$value)
$errors["value-$id"] = 'Value required';
elseif (in_array($value, $items))
$errors["value-$id"] = 'Value already in-use';
elseif ($item->update($ht, $errors)) {
if ($_POST["disable-$id"] == 'on')
$item->disable();
elseif(!$item->isEnabled() && $item->isEnableable())
$item->enable();
$item->save();
$items[] = $value;
}
$item->save();
$max_isort = max($max_isort, $_POST["sort-$id"]);
}
}
$names = array();
if (!$form) {
$form = DynamicForm::create(array(
'type' => 'L'.$list->getId(),
'title' => $list->getName() . ' Properties'
));
$form->save(true);
}
foreach ($form->getDynamicFields() as $field) {
$id = $field->get('id');
if ($_POST["delete-$id"] == 'on' && $field->isDeletable()) {
$field->delete();
// Don't bother updating the field
continue;
}
if (isset($_POST["type-$id"]) && $field->isChangeable())
$field->set('type', $_POST["type-$id"]);
if (isset($_POST["name-$id"]) && !$field->isNameForced())
$field->set('name', $_POST["name-$id"]);
# TODO: make sure all help topics still have all required fields
foreach (array('sort','label') as $f) {
if (isset($_POST["prop-$f-$id"])) {
$field->set($f, $_POST["prop-$f-$id"]);
// Update properties
if (!$errors && ($form = $list->getForm())) {
$names = array();
foreach ($form->getDynamicFields() as $field) {
$id = $field->get('id');
if ($_POST["delete-prop-$id"] == 'on' && $field->isDeletable()) {
$field->delete();
// Don't bother updating the field
continue;
}
if (isset($_POST["type-$id"]) && $field->isChangeable())
$field->set('type', $_POST["type-$id"]);
if (isset($_POST["name-$id"]) && !$field->isNameForced())
$field->set('name', $_POST["name-$id"]);
foreach (array('sort','label') as $f) {
if (isset($_POST["prop-$f-$id"])) {
$field->set($f, $_POST["prop-$f-$id"]);
}
}
if (in_array($field->get('name'), $names))
$field->addError('Field variable name is not unique', 'name');
if (preg_match('/[.{}\'"`; ]/u', $field->get('name')))
$field->addError('Invalid character in variable name. Please use letters and numbers only.', 'name');
if ($field->get('name'))
$names[] = $field->get('name');
if ($field->isValid())
$field->save();
else
# notrans (not shown)
$errors["field-$id"] = 'Field has validation errors';
// Keep track of the last sort number
$max_sort = max($max_sort, $field->get('sort'));
}
}
if (in_array($field->get('name'), $names))
$field->addError('Field variable name is not unique', 'name');
if (preg_match('/[.{}\'"`; ]/u', $field->get('name')))
$field->addError('Invalid character in variable name. Please use letters and numbers only.', 'name');
if ($field->get('name'))
$names[] = $field->get('name');
if ($field->isValid())
$field->save();
if ($errors)
$errors['err'] = $errors['err'] ?: 'Unable to update custom list items. Correct any error(s) and try again.';
else
# notrans (not shown)
$errors["field-$id"] = 'Field has validation errors';
// Keep track of the last sort number
$max_sort = max($max_sort, $field->get('sort'));
}
$msg = 'Custom list updated successfully';
} elseif ($errors)
$errors['err'] = 'Unable to update custom list. Correct any error(s) below and try again.';
else
$errors['err'] = 'Unable to update custom list. Unknown internal error';
break;
case 'add':
foreach ($fields as $f)
if (in_array($f, $required) && !$_POST[$f])
$errors[$f] = sprintf('%s is required',
mb_convert_case($f, MB_CASE_TITLE));
$list = DynamicList::create(array(
'name'=>$_POST['name'],
'name_plural'=>$_POST['name_plural'],
'sort_mode'=>$_POST['sort_mode'],
'notes'=>$_POST['notes']));
$form = DynamicForm::create(array(
'title'=>$_POST['name'] . ' Properties'
));
if ($errors)
$errors['err'] = 'Unable to create custom list. Correct any error(s) below and try again.';
elseif (!$list->save(true))
if ($list=DynamicList::add($_POST, $errors)) {
$msg = 'Custom list added successfully';
} elseif ($errors) {
$errors['err'] = 'Unable to create custom list. Correct any
error(s) below and try again.';
} else {
$errors['err'] = 'Unable to create custom list: Unknown internal error';
$form->set('type', 'L'.$list->get('id'));
if (!$errors && !$form->save(true))
$errors['err'] = 'Unable to create properties for custom list: Unknown internal error';
else
$msg = 'Custom list added successfully';
}
break;
case 'mass_process':
if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
$errors['err'] = 'You must select at least one API key';
$errors['err'] = 'You must select at least one custom list';
} else {
$count = count($_POST['ids']);
switch(strtolower($_POST['a'])) {
......@@ -152,8 +148,8 @@ if($_POST) {
$list->addItem(array(
'value' => $_POST["value-new-$i"],
'abbrev' =>$_POST["abbrev-new-$i"],
'sort' => $_POST["sort-new-$i"]
));
'sort' => $_POST["sort-new-$i"] ?: ++$max_isort,
), $errors);
}
}
......@@ -162,12 +158,11 @@ if($_POST) {
if (!$_POST["prop-label-new-$i"])
continue;
$field = DynamicFormField::create(array(
'form_id'=>$form->get('id'),
'sort'=>$_POST["prop-sort-new-$i"]
? $_POST["prop-sort-new-$i"] : ++$max_sort,
'label'=>$_POST["prop-label-new-$i"],
'type'=>$_POST["type-new-$i"],
'name'=>$_POST["name-new-$i"],
'form_id' => $form->get('id'),
'sort' => $_POST["prop-sort-new-$i"] ?: ++$max_sort,
'label' => $_POST["prop-label-new-$i"],
'type' => $_POST["type-new-$i"],
'name' => $_POST["name-new-$i"],
));
$field->setForm($form);
if ($field->isValid())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment