v7‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT PK\'H XmlParser.phpnu[xml = xml_parser_create(); xml_parser_set_option($this->xml, XML_OPTION_CASE_FOLDING, 0); xml_set_element_handler($this->xml, array($this, 'start'), array($this, 'end')); xml_set_character_data_handler($this->xml, array($this, 'cdata')); foreach ($tables as $table) $this->workers[] = new Am_Record_XmlParser_Worker($table); } function parseString($xmlString) { if (!xml_parse($this->xml, $xmlString)) { throw new Am_Exception_InternalError("XML Error: " . xml_error_string(xml_get_error_code($this->xml)) . " at XML line " . xml_get_current_line_number($this->xml) . " : column " . xml_get_current_column_number($this->xml) ); } } function switchActive($tableName) { $active = $this->getActive(); if ($active) $active->startNestedTables(); $workers = !$active ? $this->workers : $active->getWorkers(); foreach ($workers as $w) { if ($w->getId() == $tableName) { $keys = array(); foreach ($this->active as $active) { $record = $active->getRecord(); if (!$record || !$record->pk()) continue; /* @var $record Am_Record */ $keys[ $record->getTable()->getKeyField() ] = $record->pk(); } $w->setParentKeys($keys); $this->active[] = $w; return; } } // no worked found, install NULL worker $null = new Am_Record_XmlParser_Worker(null); $this->active[] = $null; } function getActive() { end($this->active); return current($this->active); } function start($parser, $tag, $attributes) { $tag = strtolower($tag); $this->path[] = isset($attributes['name']) ? sprintf('%s[name="%s"]', $tag, $attributes['name']) : $tag; $this->attrs[] = $attributes; $this->cdata[] = ""; if ($tag == 'table_data') $this->switchActive($attributes['name']); elseif ($this->active) return $this->getActive()->start($tag, $attributes); } function end($parser, $tag) { array_pop($this->path); $attrs = array_pop($this->attrs); $cdata = array_pop($this->cdata); if ($tag == 'table_data') { if ($this->active) $this->getActive()->endTableData(); array_pop($this->active); } elseif ($this->active) return $this->getActive()->end($tag, $attrs, $cdata); } function cdata($parser, $cdata) { end($this->cdata); $this->cdata[key($this->cdata)] .= $cdata; } } /** * class making record insertions * @package Am_Record */ class Am_Record_XmlParser_Worker { /** @var array of Am_Record_XmlParser_Worker */ protected $nested = array(); /** @var Am_Record */ protected $record; /** @var Am_Table */ protected $table; protected $id; protected $parentKeys = array(); public function __construct(Am_Table $table = null) { $this->table = $table; if (!empty($table->_importNested)) foreach ($table->_importNested as $t) $this->nested[] = new self($t); $this->id = $this->table ? $table->getName(true) : 'NULL'; } function getId() { return $this->id; } public function getWorkers() { return $this->nested; } function start($tag, $attributes) { if (!$this->table) return; switch ($tag) { case 'row': $this->record = $this->table->createRecord(); foreach ($this->record->getTable()->getFields(true) as $k) if (!empty($this->parentKeys[$k])) $this->record->set($k, $this->parentKeys[$k]); $this->record->disableInsertPkCheck(true); break; } } function reset() { $this->record = null; } function end($tag, $attributes, $cdata) { if (!$this->table) return; switch ($tag) { case 'row': if ($this->record && !$this->record->isLoaded()) { $exist = false; if ($fields = $this->record->getTable()->_checkUnique) { $where = array(); foreach ($fields as $f) { $where[$f] = isset($this->record->{$f}) ? $this->record->{$f} : null; } $records = $this->record->getTable()->findBy($where); $exist = (bool)count($records); } if (!$exist) $this->record->insert(); } $this->record = null; break; case 'field': if (!empty($this->record)) $this->record->set($attributes['name'], $cdata); break; } } function endTableData() { if (!$this->table) return; } /** * Insert any collected fields before starting with child records */ function startNestedTables() { if ($this->record) $this->record->insert(); } function getRecord() { return $this->record; } function setParentKeys(array $keys) { $this->parentKeys = $keys; } }PK\Dt m m WithData.phpnu[ $v) $this->$k = $v; $this->type = $this->getType(); $this->name = $name; $this->title = $title; $this->description = $description; $this->validateFunc = $validateFunc; } function getType() { return strtolower(str_replace('Am_CustomField', '', get_class($this))); } function getName() { return $this->name; } /** * Set description * @return Am_CustomField */ function setDescription($description) { $this->description = $description; return $this; } /** * Set validation function * @return Am_CustomField */ function setValidateFunc($callback) { $this->validateFunc = $callback; return $this; } /** * Set any parameter * @param string $name * @param mixed $value * @return Am_CustomField */ function setParam($name, $value) { $this->$name = $value; return $this; } function toArray() { return (array) $this; } /** * Create and return a field * @param $name * @param $title * @param $type * @param $description * @param $validateFunc * @param $moreParams * @return Am_CustomField custom field of given $type */ static function create($name, $title, $type='text', $description=null, $validateFunc=null, $moreParams=array()) { if ($type == '') throw new Am_Exception_InternalError("Empty field [type] passed to Am_CustomField::create"); $className = 'Am_CustomField' . ucfirst($type); if (!class_exists($className, false)) throw new Am_Exception_InternalError("Wrong custom type passed [$type] to Am_CustomField::create"); $o = new $className($name, $title, $description, $validateFunc, $moreParams); return $o; } /** * Add the current field to QF2 container * @param HTML_QuickForm2_Container * @return HTML_QuickForm2_Node */ public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = $container->addElement($this->qfType, $this->name, $attr, $data) ->setLabel(!empty($this->description) ? ___($this->title) . "\n" . ___($this->description) : ___($this->title)); if (!empty($this->size)) $el->setAttribute('size', $this->size); if (!empty($this->default)) $el->setValue($this->default); if (!(defined('AM_ADMIN') && AM_ADMIN)) $this->addValidateFunction($el, $runAt); return $el; } function addValidateFunction(HTML_QuickForm2_Node $el, $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { foreach ((array) $this->validateFunc as $f) { switch ($f) { case 'required' : $r = $el->addRule('required', ___('This field is required'), null, $runAt); break; case 'integer': $r = $el->addRule('regex', ___("Integer value required"), '/^\d+$/', $runAt); break; case 'number': $r = $el->addRule('regex', ___("Numeric value required"), '/^\d+(|\.\d+)$/', $runAt); break; case 'email': $r = $el->addRule('callback', ___("Please enter a valid e-mail address"), array('Am_Validate', 'empty_or_email'), $runAt); break; case 'emails': $r = $el->addRule('callback', ___("Please enter a valid e-mail address"), array('Am_Validate', 'emails'), $runAt); break; case 'url': $r = $el->addRule('callback', ___("Please enter a valid URL"), array('Am_Validate', 'url'), $runAt); break; case 'ip': $r = $el->addRule('callback', ___("Please enter a valid IP address"), array('Am_Validate', 'ip'), $runAt); break; default: if (is_callable($f)) $r = $el->addRule('callback2', '--error--', $f, $runAt); break; }; } } function valueFromTable($val) { if (!$this->isArray) return $val; return is_array($val) ? $val : (array) unserialize($val ? $val : 'a:0:{}'); } function valueToTable($val) { return $this->isArray ? serialize($val) : $val; } public function isArray() { return $this->isArray; } } class Am_CustomFieldHidden extends Am_CustomField { public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { return null; } } class Am_CustomFieldUnknown extends Am_CustomFieldHidden { } class Am_CustomFieldText extends Am_CustomField { public $qfType = 'text'; } class Am_CustomFieldReadonly extends Am_CustomFieldText { public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = parent::addToQF2($container, $attr, $data, $runAt); $el->toggleFrozen(true); //@todo fix return $el; } } class Am_CustomFieldSelect extends Am_CustomField { public $qfType = 'select'; public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = parent::addToQF2($container, $attr, $data, $runAt); $o = $this->isArray ? array() : array('' => ''); foreach ($this->options as $k => $v) $o[htmlentities($k, ENT_QUOTES, 'UTF-8')] = htmlentities(___($v), ENT_QUOTES, 'UTF-8'); $el->loadOptions($o); $el->setAttribute('size', !empty($this->size) ? $this->size : 1); return $el; } } class Am_CustomFieldRadio extends Am_CustomField { public $qfType = 'advradio'; public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = parent::addToQF2($container, $attr, $data, $runAt); $el->loadOptions(array_map('___', $this->options)); return $el; } } class Am_CustomFieldMulti_Select extends Am_CustomFieldSelect { public $qfType = 'multi_select'; protected $isArray = true; public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = parent::addToQF2($container, $attr, $data, $runAt); $el->setAttribute('multiple', 'multiple'); $el->setAttribute('class', 'magicselect'); return $el; } } class Am_CustomFieldUpload extends Am_CustomField { const UPLOAD_PREFIX = 'custom-field'; public $qfType = 'upload'; public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $urlUpload = '/upload/upload'; $urlGet = '/upload/get'; $el = parent::addToQF2($container, $attr, array_merge($data, array( 'secure' => true, 'prefix' => self::UPLOAD_PREFIX)), $runAt); if (defined('AM_ADMIN') && AM_ADMIN) { $urlUpload = '/admin-upload/upload'; $urlGet = '/admin-upload/get'; //$el->toggleFrozen(true); } $el->setJsOptions(<< 1)), $data, $runAt); } } class Am_CustomFieldPeriod extends Am_CustomFieldText { } class Am_CustomFieldMoney extends Am_CustomFieldText { } class Am_CustomFieldSingle_Checkbox extends Am_CustomField { public $qfType = 'advcheckbox'; } class Am_CustomFieldCheckbox extends Am_CustomField { var $qfType = 'advcheckbox'; protected $isArray = true; public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { if (empty($this->options)) $el = parent::addToQF2($container, array('value' => 1), $data, $runAt); else { $this->qfType = 'group'; $el = parent::addToQF2($container, $attr, $data, $runAt); $el->setSeparator("
"); foreach ($this->options as $k => $v) { $chkbox = $el->addAdvCheckbox(null, array('value' => $k))->setContent(___($v)); if (in_array($k, (array) $this->default)) $chkbox->setAttribute('checked', 'checked'); } $el->addHidden(null, array('value' => '')); $el->addFilter('array_filter'); } return $el; } } class Am_CustomFieldTextarea extends Am_CustomFieldText { var $qfType = 'textarea'; public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { $el = parent::addToQF2($container, $attr, $data, $runAt); $el->setAttribute('rows', !empty($this->rows) ? $this->rows : 2); $el->setAttribute('cols', !empty($this->cols) ? $this->cols : 40); return $el; } } class Am_CustomFieldHeader extends Am_CustomFieldText { public function addToQF2(HTML_QuickForm2_Container $container, $attr = array(), $data = array(), $runAt = HTML_QuickForm2_Rule::CLIENT_SERVER) { throw new Am_Exception_InternalError("Not Implemented"); } } class Am_CustomFieldDate extends Am_CustomFieldText { public $qfType = 'Date'; } /** * Custom fields manager, can be added for example to @see Am_Table * It just contains fields definitions, it does not affect record saving itself * @package Am_Record */ class Am_CustomFieldsManager { protected $ignoreFields = array(); protected $fields = array(); protected $callbacks = array(); /** * If called with one argument, will add field as is, * if called with 2 or more arguments, all of them will be passed * to Am_CustomField constructor, and resulting object will be added * @param Am_CustomField $field * @return Am_CustomField inserted field */ function add($field) { if (!$field instanceof Am_CustomField) { $args = func_get_args(); if (count($args) == 1) $args[1] = $args[0]; // set fieldtitle == fieldname $field = call_user_func_array(array('Am_CustomField', 'create'), $args); } $this->fields[$field->name] = $field; return $field; } /** * Register a callback that will be called before first call of * @see Am_CustomFieldManager::getAll() * @param $callback * @return Am_CustomFieldsManager provides fluent interface */ function addCallback($callback) { if (!is_callable($callback, true)) throw new Am_Exception_InternalError("Wrong callback passed to Am_CustomFieldsManager::addCallback()"); $this->callbacks[] = $callback; return $this; } /** * Delete field by name * @param string $name * @return Am_CustomFieldManager */ function del($name) { $this->runCallbacks(); if (array_key_exists($name, $this->fields)) unset($this->fields[$name]); return $this; } /** * Return array of registered fields. If callbacks * are registered, it runs all of them and cleans up * callbacks list (so every callback is called only once) * @return array of Am_CustomField objects */ function getAll() { $this->runCallbacks(); return $this->fields; } /** * Return list of added fields like deprecated global * $member_additional_fields, $payment_additional_fields, * $product_additional_fields variables * @return array list of field defs * @deprecated will be removed asap */ function compatGetAll() { $res = array(); foreach ($this->getAll() as $f) { $a = $f->toArray(); $a['type'] = $f->getType(); if ($a['type'] == 'unknown') $a['type'] = ''; $a['validate_func'] = $a['validateFunc']; unset($a['validateFunc']); unset($a['qfType']); $res[] = $a; } return $res; } protected function runCallbacks() { if (!$this->callbacks) return; foreach ($this->callbacks as $c) call_user_func($c, $this); $this->callbacks = array(); } /** * Return field by name * @param string $name * @return Am_CustomField|null */ function get($name) { $this->runCallbacks(); if (array_key_exists($name, $this->fields)) return $this->fields[$name]; } /** * To allow calling addText, addMulti_Select and everything * if class Am_CustomField existing, then use add(name, description, ...) */ function __call($method, $arguments) { if (strpos($method, 'add') === 0) { $class = 'Am_CustomField' . substr($method, 3); if (!class_exists($class, false)) throw new Am_Exception_InternalError("Method " . get_class($this) . "->" . $method . " is not implemented, because [$class] is not defined"); $reflectionObj = new ReflectionClass($class); return $this->add($reflectionObj->newInstanceArgs($arguments)); } else throw new Am_Exception_InternalError("Method " . get_class($this) . "->" . $method . " is not implemented"); } /** for testing only! */ function _setDb($db) { $this->_db = $db; } function valuesFromTable(array $vars) { foreach ($this->getAll() as $field) { $fn = $field->name; if (isset($vars[$fn])) $vars[$fn] = $field->valueFromTable($vars[$fn]); } return $vars; } function valuesToTable(array $vars) { foreach ($this->getAll() as $field) { $fn = $field->name; if (isset($vars[$fn])) $vars[$fn] = $field->valueToTable($vars[$fn]); } return $vars; } } /** * Manipulation and storage of custom field values * @package Am_Record */ class Am_DataFieldStorage { const TYPE_SCALAR = 0; const TYPE_SERIALIZED = 1; const TYPE_BLOB = 16; /** * If you have got this value in get or getAll, use getBlob() to retreive value */ const BLOB_VALUE = 'BLOB_VALUE'; /** @var Am_Record */ protected $record; protected $data = array(); protected $blobData = array(); protected $changedFields = array(); protected $deletedFields = array(); protected $blobFields = array(); protected $isLoaded = false; function __construct(Am_Record $record) { $this->record = $record; } function get($fieldName) { $this->load(); if (array_key_exists($fieldName, $this->data)) { if (!empty($this->blobFields[$fieldName])) { return self::BLOB_VALUE; } else return $this->data[$fieldName]; } } /** * @return Am_DataFieldStorage */ function set($fieldName, $value) { $this->load(); $this->data[$fieldName] = $value; if ($value === null) { $this->deletedFields[$fieldName] = true; unset($this->changedFields[$fieldName]); } else { $this->changedFields[$fieldName] = true; unset($this->deletedFields[$fieldName]); } return $this; } /** * Set blob value for later saving * @param string $fieldName * @param string|stream $value * @return Am_DataFieldStorage */ function setBlob($fieldName, $value) { $this->set($fieldName, $value); $this->blobFields[$fieldName] = true; return $this; } /** * @return blob value as stream */ function getBlob($fieldName) { return $this->record->getAdapter()->selectCell("SELECT `blob` FROM ?_data WHERE `table`=? AND `id`=? AND `key`=?", $this->record->getTable()->getName(true), $this->record->pk(), $fieldName); } /** * Return all records, for blobs Am_DataFieldStorage::BLOB_VALUE returned */ function getAll() { $this->load(); return $this->data; } function load() { if ($this->isLoaded || $this->changedFields || !$this->record->pk()) return false; $this->data = $this->record->getAdapter()->select( "SELECT `key` as ARRAY_KEY,`type`, CASE `type` WHEN ? THEN NULL WHEN ? THEN `blob` ELSE `value` END AS `value` FROM ?_data WHERE `table`=? AND `id`=? ", self::TYPE_BLOB, self::TYPE_SERIALIZED, $this->record->getTable()->getName(true), $this->record->pk()); foreach ($this->data as $k => $arr) { switch ($arr['type']) { case self::TYPE_SCALAR: $this->data[$k] = $arr['value']; break; case self::TYPE_SERIALIZED: $this->data[$k] = unserialize($arr['value']); break; case self::TYPE_BLOB: $this->data[$k] = self::BLOB_VALUE; break; default: throw new Am_Exception_InternalError("Unknown record type {$arr['type']} in ?_data"); } } } protected function getInsertRows() { $rows = array(); $db = $this->record->getAdapter(); foreach (array_keys($this->changedFields) as $fieldName) { $blob = null; $val = $this->data[$fieldName]; if (!empty($this->blobFields[$fieldName])) { $type = self::TYPE_BLOB; $blob = $val; $val = null; } elseif (is_scalar($val) || is_null($val)) { $type = self::TYPE_SCALAR; $val = $this->data[$fieldName]; } else { $type = self::TYPE_SERIALIZED; $blob = serialize($val); $val = null; } $rows[] = "(" . implode(',', array( $db->escape($this->record->getTable()->getName(true)), $this->record->pk(), $db->escape($fieldName), $type, $db->escape($val), $db->escape($blob), )) . ")"; } return $rows; } function insert() { if (!$this->record->pk()) throw new Am_Exception_Db("Could not insert() datafields on not saved Am_Record..." . get_class($this->record)); $rows = $this->getInsertRows(); if (!$rows) return; $this->record->getAdapter()->query( "INSERT INTO ?_data (`table`,`id`,`key`,`type`,`value`,`blob`) " . " VALUES " . implode(",", $rows)); $this->changedFields = $this->deletedFields = array(); } function update() { $fields = array_keys($this->deletedFields); if ($fields) $this->record->getAdapter()->query( "DELETE FROM ?_data WHERE `table`=? AND `id`=? AND `key` IN (?a)", $this->record->getTable()->getName(true), $this->record->pk(), $fields); $rows = $this->getInsertRows(); if (!$rows) return; $this->record->getAdapter()->query( "INSERT INTO ?_data (`table`,`id`,`key`,`type`,`value`,`blob`) " . " VALUES " . implode(",", $rows) . " ON DUPLICATE KEY UPDATE `value`=VALUES(`value`), `type`=VALUES(`type`), `blob`=VALUES(`blob`)"); $this->changedFields = $this->deletedFields = array(); } function delete() { $this->record->getAdapter()->query("DELETE FROM ?_data WHERE `table`=? AND `id`=?", $this->record->getTable()->getName(true), $this->record->pk()); $this->data = array(); $this->changedFields = array(); $this->blobData = array(); $this->blobFields = array(); $this->deletedFields = array(); $this->isLoaded = true; } } /** * Class to handle related meta-data from "am_data" table */ class Am_Record_WithData extends Am_Record { /** @var Am_DataFieldStorage objects */ protected $_dataStorage; /** * @return Am_DataFieldStorage */ public function data() { if (empty($this->_dataStorage)) $this->_dataStorage = new Am_DataFieldStorage($this); return $this->_dataStorage; } public function insert($reload = true) { parent::insert($reload); if ($this->_dataStorage) $this->_dataStorage->insert(); return $this; } public function update() { parent::update(); if ($this->_dataStorage) $this->_dataStorage->update(); return $this; } /** * Convert currently set object variables to database record array * @return array */ public function toRow() { return $this->getTable()->customFields()->valuesToTable(parent::toRow()); } /** * Set fields from array retreived from database. * If $this->_fields is a not-empty array, only these fields will be set * * @param array $vars * @return Am_Record provides fluent interface */ public function fromRow(array $vars) { return parent::fromRow($this->getTable()->customFields()->valuesFromTable($vars)); } public function delete() { parent::delete(); $this->data()->delete(); return $this; } public function toArray() { $data = $this->data()->getAll(); $arr = parent::toArray(); return array_merge($data, $arr); } public function setForInsert($vars) { $this->_prepareForSet($vars); return parent::setForInsert($vars); } public function setForUpdate($vars) { $this->_prepareForSet($vars); return parent::setForUpdate($vars); } protected function _prepareForSet(& $vars) { $extra = array(); $fields = $this->getTable()->getFields(true); foreach ($vars as $k => $v) { if (in_array($k, $fields)) { // nop } elseif ($this->getTable()->customFields()->get($k)) { $this->data()->set($k, $v); unset($vars[$k]); } else { unset($vars[$k]); } } } public function __sleep() { $ret = parent::__sleep(); $ret[] = '_dataStorage'; return $ret; } } class Am_Table_WithData extends Am_Table { protected $_recordClass = 'Am_Record_WithData'; /** @var Am_CustomFieldsManager */ protected $_customFields; protected $_customFieldsConfigKey = null; public function init() { $this->customFields()->addCallback(array($this, 'addFieldsFromSavedConfig')); } /** @return Am_CustomFieldsManager */ public function customFields() { if (!$this->_customFields) $this->_customFields = new Am_CustomFieldsManager; return $this->_customFields; } public function getCustomFieldsConfigKey() { return $this->_customFieldsConfigKey ? $this->_customFieldsConfigKey : $this->getName(true) . '_fields'; } /** * Find records by related ?_data record * @param type $key * @param type $value */ function findByData($key, $value, $limit = 100) { return $this->selectObjects("SELECT t.* FROM {$this->_table} t LEFT JOIN ?_data d ON (d.`table` = ? AND d.`id` = t.{$this->_key} AND d.`key`=?) WHERE d.`value`=? LIMIT ?d", $this->getName(true), $key, $value, $limit); } /** * First first record by related ?_data record * @param string $key * @param string $value * @return Am_Record|null */ function findFirstByData($key, $value) { return current($this->findByData($key, $value, 1)); } function addFieldsFromSavedConfig() { $config_key = $this->getCustomFieldsConfigKey(); foreach ((array) $this->getDi()->config->get($config_key) as $f) { $this->customFields()->add($f['name'], $f['title'], $f['type'], $f['description'], $f['validate_func'], (array) $f['additional_fields'] + array('from_config' => 1)); } } function syncSortOrder() { $db = $this->getDi()->db; $fields = array(); foreach ($this->customFields()->getAll() as $f) $fields[] = $f->getName(); // delete records that are not found in config $db->query("DELETE FROM ?_custom_field_sort WHERE custom_field_table=? { and custom_field_name NOT IN (?a) }", $this->getName(true), count($fields) ? $fields : DBSIMPLE_SKIP); if ($fields) { // add records that present in config $x = (int) $db->selectCell("SELECT MAX(sort_order) FROM ?_custom_field_sort WHERE custom_field_table=?", $this->getName(true)); if (!$x) $x = 0; foreach ($fields as $field_name) { $x++; $db->query("INSERT IGNORE INTO ?_custom_field_sort (custom_field_table, custom_field_name, sort_order) VALUES (?, ?, ?)", $this->getName(true), $field_name, $x); } } } function sortCustomFields($a, $b) { static $max, $sort_order; if (!$sort_order) { $sort_order = $this->getDi()->db->selectCol("SELECT custom_field_name as ARRAY_KEY, sort_order FROM ?_custom_field_sort WHERE custom_field_table = ?", $this->getName(true)); $max = $sort_order ? max($sort_order) : 1; } //$max+1 is just enough big integer value to place field to the end //in case order is not defined if (!isset($sort_order[$a])) $sort_order[$a] = $max + 1; if (!isset($sort_order[$b])) $sort_order[$b] = $max + 1; return $sort_order[$a] - $sort_order[$b]; } } PK\'H XmlParser.phpnu[PK\Dt m m &WithData.phpnu[PKl