diff --git a/include/ajax.forms.php b/include/ajax.forms.php index bd71f392461773fbbfd3875fbb187657c782ac3c..47ace2a54aecae50695a91abda04ced0db72ba71 100644 --- a/include/ajax.forms.php +++ b/include/ajax.forms.php @@ -60,12 +60,8 @@ class DynamicFormsAjaxAPI extends AjaxController { 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( (int) $item_id))) + $list = DynamicList::lookup($list_id); + if (!$list || !($item = $list->getItem( (int) $item_id))) Http::response(404, 'No such list item'); include(STAFFINC_DIR . 'templates/list-item-properties.tmpl.php'); @@ -73,12 +69,8 @@ class DynamicFormsAjaxAPI extends AjaxController { 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( (int) $item_id))) + $list = DynamicList::lookup($list_id); + if (!$list || !($item = $list->getItem( (int) $item_id))) Http::response(404, 'No such list item'); if (!$item->setConfiguration()) diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index f097a2e4defbf4b3701310730382e8486c123e7b..a2a3027c59aaacda9b3eda408523352e5a39ed75 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -436,7 +436,7 @@ class DynamicFormField extends VerySimpleModel { } function isDeletable() { - return ($this->get('edit_mask') & 1) == 0; + return (($this->get('edit_mask') & 1) == 0); } function isNameForced() { return $this->get('edit_mask') & 2; @@ -449,11 +449,11 @@ class DynamicFormField extends VerySimpleModel { } function isChangeable() { - return ($this->get('edit_mask') & 16) == 0; + return (($this->get('edit_mask') & 16) == 0); } - function isConfigurable() { - return ($this->get('edit_mask') & 32) == 0; + function isEditable() { + return (($this->get('edit_mask') & 32) == 0); } /** diff --git a/include/class.list.php b/include/class.list.php index 89564ad6263ef30ff4889cfdf10e75c01439d947..462c1407914300f515266ce80f2794e5d120122c 100644 --- a/include/class.list.php +++ b/include/class.list.php @@ -46,7 +46,8 @@ interface CustomList { function getSortMode(); function getListOrderBy(); - function isBuiltIn(); + function allowAdd(); + function hasAbbrev(); function update($vars, &$errors); function delete(); @@ -82,98 +83,41 @@ interface CustomListItem { /* - * Base class for Built-in Custom Lists + * Base class for Custom List handlers * - * Built-in custom lists are lists that can be extended but within the - * constrains of system defined parameters. + * Custom list handler extends custom list and might store data outside the + * typical dynamic list store. * */ -abstract class BuiltInCustomList implements CustomList { - static $sort_modes = array( - 'Alpha' => 'Alphabetical', - '-Alpha' => 'Alphabetical (Reversed)', - 'SortCol' => 'Manually Sorted' - ); +abstract class CustomListHandler { - var $config = null; + var $_list; - function __construct() { - $this->config = new Config('CL'.$this->getId()); + function __construct($list) { + $this->_list = $list; } - abstract function getId(); - abstract function getName(); - abstract function getPluralName(); + function __call($name, $args) { - abstract function getInfo(); + $rv = null; + if ($this->_list && is_callable(array($this->_list, $name))) + $rv = $args + ? call_user_func_array(array($this->_list, $name), $args) + : call_user_func(array($this->_list, $name)); - abstract function getNumItems(); - 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; + return $rv; } - function isBuiltIn() { - return true; - } - - function set($field, $value) { - - if (!$this->config) - return false; - - return $this->config->set($field, $value); - } - - abstract function update($vars, &$errors); - - // Built-in list cannot be deleted - function delete() { - return false; - } - - // Built-in list is defined - not created. - static function create($vars, &$errors) { - return false; - } - - static function lookup($id) { - - if (!($list=static::getLists()) - // Built-in list exists - || !isset($list[$id]) - // Handler exits - || !($handler = $list[$id]['handler']) - // It's a collable handler - || !class_exists($handler)) - return null; - - return new $handler(); - } - - static function getLists() { - - $list['status'] = array ( //Ticket statuses - 'name' => 'Ticket Status', - 'handler' => 'TicketStatusList', - 'icon' => 'icon-flag', - ); - - return $list; + function update($vars, &$errors) { + return $this->_list->update($vars, $errors); } + abstract function getNumItems(); + abstract function getAllItems(); + abstract function getItems($criteria); + abstract function getItem($id); + abstract function addItem($vars, &$errors); } /** @@ -197,16 +141,23 @@ class DynamicList extends VerySimpleModel implements CustomList { // Required fields static $fields = array('name', 'name_plural', 'sort_mode', 'notes'); + // Supported masks + const MASK_EDIT = 0x0001; + const MASK_ADD = 0x0002; + const MASK_DELETE = 0x0004; + const MASK_ABBREV = 0x0008; var $_items; var $_form; + var $_config; - function getId() { - return $this->get('id'); + function __construct() { + call_user_func_array(array('parent', '__construct'), func_get_args()); + $this->_config = new Config('list.'.$this->getId()); } - function isBuiltIn() { - return false; + function getId() { + return $this->get('id'); } function getInfo() { @@ -314,6 +265,34 @@ class DynamicList extends VerySimpleModel implements CustomList { return $this->_form; } + function isDeleteable() { + return !$this->hasMask(static::MASK_DELETE); + } + + function isEditable() { + return !$this->hasMask(static::MASK_EDIT); + } + + function allowAdd() { + return !$this->hasMask(static::MASK_ADD); + } + + function hasAbbrev() { + return !$this->hasMask(static::MASK_ABBREV); + } + + protected function hasMask($mask) { + return 0 !== ($this->get('masks') & $mask); + } + + protected function clearMask($mask) { + return $this->set('masks', $this->get('masks') & ~$mask); + } + + protected function setFlag($mask) { + return $this->set('mask', $this->get('mask') | $mask); + } + private function createConfigurationForm() { $form = DynamicForm::create(array( @@ -328,8 +307,16 @@ class DynamicList extends VerySimpleModel implements CustomList { return $this->getConfigurationForm($autocreate); } + function getConfiguration() { + return JsonDataParser::parse($this->_config->get('configuration')); + } + function update($vars, &$errors) { - $required = array('name'); + + $required = array(); + if ($this->isEditable()) + $required = array('name'); + 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)); @@ -397,9 +384,45 @@ class DynamicList extends VerySimpleModel implements CustomList { static function create($ht=false, &$errors=array()) { $inst = parent::create($ht); $inst->set('created', new SqlFunction('NOW')); + + if (isset($ht['properties'])) { + $inst->save(); + $ht['properties']['type'] = 'L'.$inst->getId(); + $form = DynamicForm::create($ht['properties']); + $form->save(); + } + + if (isset($ht['configuration'])) { + $inst->save(); + $c = new Config('list.'.$inst->getId()); + $c->set('configuration', JsonDataEncoder::encode($ht['configuration'])); + } + + if (isset($ht['items'])) { + $inst->save(); + foreach ($ht['items'] as $i) { + $i['list_id'] = $inst->getId(); + $item = DynamicListItem::create($i); + $item->save(); + } + } + return $inst; } + static function lookup($id) { + + if (!($list = parent::lookup($id))) + return null; + + if (($config = $list->getConfiguration())) { + if (($lh=$config['handler']) && class_exists($lh)) + $list = new $lh($list); + } + + return $list; + } + static function getSelections() { $selections = array(); foreach (DynamicList::objects() as $list) { @@ -441,7 +464,8 @@ class DynamicListItem extends VerySimpleModel implements CustomListItem { var $_config; var $_form; - const ENABLED = 0x0001; + const ENABLED = 0x0001; + const INTERNAL = 0x0002; protected function hasStatus($flag) { return 0 !== ($this->get('status') & $flag); @@ -456,7 +480,7 @@ class DynamicListItem extends VerySimpleModel implements CustomListItem { } function isInternal() { - return false; + return $this->hasStatus(self::INTERNAL); } function isEnableable() { @@ -575,22 +599,47 @@ class DynamicListItem extends VerySimpleModel implements CustomListItem { $this->set('list_id', null); return $this->save(); } + + static function create($ht=false, &$errors=array()) { + + if (isset($ht['properties']) && is_array($ht['properties'])) + $ht['properties'] = JsonDataEncoder::encode($ht['properties']); + + $inst = parent::create($ht); + $inst->save(true); + + // Auto-config properties if any + if ($ht['configuration'] && is_array($ht['configuration'])) { + $config = $inst->getConfiguration(); + if (($form = $inst->getConfigurationForm())) { + foreach ($form->getFields() as $f) { + if (!isset($ht['configuration'][$f->get('name')])) + continue; + + if (is_array($ht['configuration'][$f->get('name')])) + $val = JsonDataEncoder::encode( + $ht['configuration'][$f->get('name')]); + else + $val = $ht['configuration'][$f->get('name')]; + + $config[$f->get('id')] = $val; + } + } + + $inst->set('properties', JsonDataEncoder::encode($config)); + } + + return $inst; + } } /* * Ticket status List * - * */ -class TicketStatusList extends BuiltInCustomList { - - var $ht = array( - 'id' => 'status', - 'name' => 'Status', - 'name_plural' => 'Statuses', - ); +class TicketStatusList extends CustomListHandler { // Fields of interest we need to store static $config_fields = array('sort_mode', 'notes'); @@ -598,30 +647,6 @@ class TicketStatusList extends BuiltInCustomList { var $_items; var $_form; - function getId() { - return $this->ht['id']; - } - - function getName() { - return $this->ht['name']; - } - - function getPluralName() { - return $this->ht['name_plural']; - } - - function getSortMode() { - return $this->ht['sort_mode']; - } - - function getNotes() { - return $this->ht['notes']; - } - - function getInfo() { - return $this->config->getInfo() + $this->ht; - } - function getNumItems() { return TicketStatus::objects()->count(); } @@ -650,13 +675,13 @@ class TicketStatusList extends BuiltInCustomList { if (!is_int($val)) $val = array('name' => $val); - return TicketStatus::lookup($val); + return TicketStatus::lookup($val, $this); } function addItem($vars, &$errors) { $item = TicketStatus::create(array( - 'flags' => 0, //Disable until configured. + 'flags' => 0, 'sort' => $vars['sort'], 'name' => $vars['value'], )); @@ -667,49 +692,6 @@ class TicketStatusList extends BuiltInCustomList { return $item; } - - function hasProperties() { - return ($this->getForm()); - } - - function getListOrderBy() { - switch ($this->getSortMode()) { - case 'Alpha': - return 'name'; - case '-Alpha': - return '-name'; - case 'SortCol': - default: - return 'sort'; - } - } - - function update($vars, &$errors) { - - foreach (static::$config_fields as $f) { - if (!isset($vars[$f])) continue; - - if (parent::set($f, $vars[$f])) - $this->ht[$f] = $vars[$f]; - } - - return true; - } - - function getForm() { - - if (!isset($this->_form)) { - $o = DynamicForm::objects()->filter(array('type'=>'S')); - if ($o && $o[0]) - $this->_form = $o[0]; - else // Auto-load the data - $this->_form = self::__load(); - } - - return $this->_form; - } - - static function __load() { require_once(INCLUDE_DIR.'class.i18n.php'); @@ -744,35 +726,13 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { 'pk' => array('id'), ); - // Major statuses (states) - static $_states = array( 1 => 'open', 'closed', 'archived', 'deleted'); - - // Supported flags (TODO: move to configurable custom list) - static $_flags = array( - 'onhold' => array( - 'flag' => 1, - 'name' => 'Onhold', - 'states' => array('open'), - ), - 'overdue' => array( - 'flag' => 2, - 'name' => 'Overdue', - 'states' => array('open'), - ), - 'answered' => array( - 'flag' => 4, - 'name' => 'Answered', - 'states' => array('open'), - ) - ); - + var $_list; var $_form; var $_config; var $_settings; - - const ENABLED = 0x0001; - const INTERNAL = 0x0002; // Forbid deletion or name and status change. + const ENABLED = 0x0001; + const INTERNAL = 0x0002; // Forbid deletion or name and status change. @@ -793,14 +753,19 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { return $this->set($field, $this->get($field) | $flag); } + protected function hasProperties() { + return ($this->_config->get('properties')); + } + function getForm() { return $this->getConfigurationForm(); } function getConfigurationForm() { - if (!$this->_form) { - $this->_form = DynamicForm::lookup(array('type'=>'S')); + if (!$this->_form && $this->_list) { + $this->_form = DynamicForm::lookup( + array('type'=>'L'.$this->_list->getId())); } return $this->_form; @@ -825,7 +790,7 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { } function isEnableable() { - return ($this->getForm()); + return $this->hasProperties(); } function isDeletable() { @@ -871,12 +836,12 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { $id = $f->get('id'); switch($name) { case 'flags': - foreach (static::$_flags as $k => $v) + foreach (TicketFlagField::$_flags as $k => $v) if ($this->hasFlag('flags', $v['flag'])) - $this->_settings[$id][] = $k; + $this->_settings[$id][$k] = $v['name']; break; case 'state': - $this->_settings[$id] = $this->get('state'); + $this->_settings[$id][$this->get('state')] = $this->get('state'); break; default: if (!$this->_settings[$id] && $this->_settings[$name]) @@ -892,15 +857,20 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { function setConfiguration(&$errors=array()) { $properties = array(); foreach ($this->getConfigurationForm()->getFields() as $f) { + if ($this->isInternal() //Item is internal. + && !$f->isEditable()) + continue; $val = $f->getClean(); + $errors = array_merge($errors, $f->errors()); + if ($f->errors()) continue; $name = mb_strtolower($f->get('name')); switch ($name) { case 'flags': if ($val && is_array($val)) { $flags = 0; - foreach ($val as $v) { - if (isset(static::$_flags[$v])) - $flags += static::$_flags[$v]['flag']; + foreach ($val as $k => $v) { + if (isset(TicketFlagField::$_flags[$k])) + $flags += TicketFlagField::$_flags[$k]['flag']; elseif (!$f->errors()) $f->addError('Unknown or invalid flag', $name); } @@ -910,15 +880,15 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { } break; case 'state': - $val = $f->to_database($val); - if ($val && in_array($val, static::$_states)) - $this->set('state', $val); + if ($val && is_array($val)) + $this->set('state', key($val)); else $f->addError('Unknown or invalid state', $name); break; default: //Custom properties the user might add. $properties[$f->get('id')] = $f->to_php($val); } + // Add field specific validation errors (warnings) $errors = array_merge($errors, $f->errors()); } @@ -965,6 +935,18 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { return $this->toString(); } + + static function lookup($var, $list= false) { + + if (!($item = parent::lookup($var))) + return null; + + $item->_list = $list; + + return $item; + } + + static function __create($ht, &$error=false) { global $ost; diff --git a/include/staff/dynamic-list.inc.php b/include/staff/dynamic-list.inc.php index ef9f9c5a02c594aa55926bbb9eedffff674522d5..f67e5d8ea9c2d3a92861aca0d309b8d4a31354c2 100644 --- a/include/staff/dynamic-list.inc.php +++ b/include/staff/dynamic-list.inc.php @@ -48,7 +48,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) <td width="180" class="required">Name:</td> <td> <?php - if ($list && $list->isBuiltIn()) + if ($list && !$list->isEditable()) echo $list->getName(); else { echo sprintf('<input size="50" type="text" name="name" @@ -63,7 +63,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) <td width="180">Plural Name:</td> <td> <?php - if ($list && $list->isBuiltIn()) + if ($list && !$list->isEditable()) echo $list->getPluralName(); else echo sprintf('<input size="50" type="text" @@ -229,7 +229,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) <th></th> <th>Value</th> <?php - if (!$list || !$list->isBuiltIn()) { ?> + if (!$list || $list->hasAbbrev()) { ?> <th>Abbrev <em style="display:inline">— Abbreviations and such</em></th> <?php } ?> @@ -272,7 +272,7 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) ?> </td> <?php - if (!$list->isBuiltIn()) { ?> + if ($list->hasAbbrev()) { ?> <td><input type="text" size="30" name="abbrev-<?php echo $id; ?>" value="<?php echo $i->getAbbrev(); ?>"/></td> <?php @@ -302,20 +302,24 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) </tr> <?php } } - for ($i=0; $i<$newcount; $i++) { ?> + + if (!$list || $list->allowAdd()) { + for ($i=0; $i<$newcount; $i++) { ?> <tr> <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()) { ?> + if (!$list || $list->hasAbbrev()) { ?> <td><input type="text" size="30" name="abbrev-new-<?php echo $i; ?>"/></td> <?php } ?> <td> </td> <td> </td> </tr> - <?php } ?> + <?php + } + }?> </tbody> </table> </div> diff --git a/include/staff/dynamic-lists.inc.php b/include/staff/dynamic-lists.inc.php index 9a818ec5d521bb8d60e6c9edf5899860f5818dd2..e6c69cbb2173a72095be42efdfb32e8359163530 100644 --- a/include/staff/dynamic-lists.inc.php +++ b/include/staff/dynamic-lists.inc.php @@ -12,9 +12,6 @@ $pageNav = new Pagenate($count, $page, PAGE_LIMIT); $pageNav->setURL('lists.php'); $showing=$pageNav->showing().' Dynamic Lists'; -// Get built-in list -$builtInList = BuiltInCustomList::getLists(); - ?> <form action="lists.php" method="POST" name="lists"> <?php csrf_token(); ?> @@ -22,25 +19,6 @@ $builtInList = BuiltInCustomList::getLists(); <input type="hidden" id="action" name="a" value="" > <table class="list" border="0" cellspacing="1" cellpadding="0" width="940"> <caption>Custom Lists</caption> - <?php - if ($builtInList) { ?> - <thead> - <tr> - <th width="7"> </th> - <th colspan=3> Built-In Lists</th> - </tr> - </thead> - <tbody> - <?php foreach ($builtInList as $id => $list) { ?> - <tr> - <td><i class="<?php echo $list['icon']; ?>"></i></td> - <td colspan=3><a href="?id=<?php echo $id; ?>"><?php echo $list['name']; ?></a></td> - </tr> - <?php } - ?> - </tbody> - <?php - } ?> <thead> <tr> <th width="7"> </th> @@ -50,7 +28,7 @@ $builtInList = BuiltInCustomList::getLists(); </tr> </thead> <tbody> - <?php foreach (DynamicList::objects()->order_by('name') + <?php foreach (DynamicList::objects()->order_by('-type', 'name') ->limit($pageNav->getLimit()) ->offset($pageNav->getStart()) as $list) { $sel = false; @@ -58,11 +36,19 @@ $builtInList = BuiltInCustomList::getLists(); $sel = true; ?> <tr> <td> + <?php + if ($list->isDeleteable()) { ?> <input width="7" type="checkbox" class="ckb" name="ids[]" value="<?php echo $list->getId(); ?>" - <?php echo $sel?'checked="checked"':''; ?>></td> + <?php echo $sel?'checked="checked"':''; ?>> + <?php + } else { + echo ' '; + } + ?> + </td> <td><a href="?id=<?php echo $list->getId(); ?>"><?php echo - $list->getName(); ?></a></td> + $list->getPluralName() ?: $list->getName(); ?></a></td> <td><?php echo $list->get('created'); ?></td> <td><?php echo $list->get('updated'); ?></td> </tr> diff --git a/include/staff/templates/list-item-properties.tmpl.php b/include/staff/templates/list-item-properties.tmpl.php index 6c6f09656ae9fe1e58fcf622612c8e73b1adc1ac..08b6a381527b7a88480a5a12356ec3cd8c23a1dd 100644 --- a/include/staff/templates/list-item-properties.tmpl.php +++ b/include/staff/templates/list-item-properties.tmpl.php @@ -19,10 +19,11 @@ <?php echo csrf_token(); $config = $item->getConfiguration(); + $internal = $item->isInternal(); foreach ($item->getConfigurationForm()->getFields() as $f) { $name = $f->get('id'); if (isset($config[$name])) - $f->value = $config[$name]; + $f->value = $f->to_php($config[$name]); else if ($f->get('default')) $f->value = $f->get('default'); ?> @@ -33,7 +34,7 @@ </td><td> <span style="display:inline-block;width:100%"> <?php - if ($item->isInternal() && $f->isConfigurable()) + if ($internal && !$f->isEditable()) $f->render('view'); else { $f->render(); diff --git a/include/upgrader/streams/core/8f99b8bf-cbf8c933.patch.sql b/include/upgrader/streams/core/8f99b8bf-cbf8c933.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..6d1b62c33c19e7845cdeba77e7d2db4c181014da --- /dev/null +++ b/include/upgrader/streams/core/8f99b8bf-cbf8c933.patch.sql @@ -0,0 +1,29 @@ +/** + * @version v1.9.3 + * @signature cbf8c933d6d2eaaa971042eb2efce247 + * @title Add custom ticket status support + * + */ + +ALTER TABLE `%TABLE_PREFIX%list` + ADD `masks` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `sort_mode`, + ADD `type` VARCHAR( 16 ) NULL DEFAULT NULL AFTER `masks`, + ADD INDEX ( `type` ); + +CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%ticket_status` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(60) NOT NULL DEFAULT '', + `state` varchar(16) NOT NULL DEFAULT 'open', + `mode` int(11) unsigned NOT NULL DEFAULT '0', + `flags` int(10) unsigned NOT NULL DEFAULT '0', + `sort` int(11) unsigned NOT NULL DEFAULT '0', + `notes` text NOT NULL, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) DEFAULT CHARSET=utf8; + +UPDATE `%TABLE_PREFIX%config` + SET `value` = 'cbf8c933d6d2eaaa971042eb2efce247' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/scp/lists.php b/scp/lists.php index 84fe3bdc600274e58e371a430cab28affe082d09..562a1ffd741afe7f23dae306d10df104200801cb 100644 --- a/scp/lists.php +++ b/scp/lists.php @@ -5,10 +5,7 @@ require_once(INCLUDE_DIR.'class.list.php'); $list=null; if ($_REQUEST['id']) { - if (is_numeric($_REQUEST['id'])) - $list = DynamicList::lookup($_REQUEST['id']); - else - $list = BuiltInCustomList::lookup($_REQUEST['id']); + $list = DynamicList::lookup($_REQUEST['id']); if ($list) $form = $list->getForm(); @@ -140,7 +137,7 @@ if($_POST) { break; } - if ($list) { + if ($list && $list->allowAdd()) { for ($i=0; isset($_POST["sort-new-$i"]); $i++) { if (!$_POST["value-new-$i"]) continue;