* @license PHP License * @package WB * @subpackage content */ /** * Load required class */ WBClass::load('WBContent'); /** * Content component: TableEditor * * @version 1.0.0 * @package WB * @subpackage content */ class WBContent_TableEditor extends WBContent { /** * my parameter list * * - action either "moveup", "movedown", "list", "add", "edit", "rm", "display" * - requiredgroup (tableeditor) * - requiredgroupmanager (tableadmin) * - showsomeoneelses (1) display other users records, too * - table (blog) * - tableenv if table env is set, it will be used to load config and templates * - limit (20) * - goto pager's page (0) * - id (__new) * - managesortcolumn column name or empty ("") * - levels number of levels to move, if managesortcolumn applies (1) * - addcurrentuser either "new", "always" or "never" ("new") * - searchfields (array()) list of columns that are allowed to search in using LIKE * - usefilter (0) filter lists, display filter form, use form definition * in filter.xml and filter.tmpl to render form. * * @var array */ protected $config = array( 'action' => 'list', 'requiredgroup' => 'tableeditor', 'requiredgroupmanager' => 'tableadmin', 'showsomeoneelses' => 1, 'table' => 'blog', 'tableenv' => '', 'limit' => '20', 'goto' => '0', 'id' => '__new', 'managesortcolumn' => '', 'levels' => 1, 'addcurrentuser' => 'new', 'searchfields' => array(), 'usefilter' => 0 ); /** * table * @var WBDatasource_Table */ protected $table; /** * id of current record of table * @var string */ protected $id = '__new'; /** * relation table * @var WBDatasource_RelationTable */ private $relTable; /** * search and filter clause * @var array */ private $clause = array(); /** * 2nd constructor */ protected function init() { $this->table = WBClass::create( 'WBDatasource_Table' ); } /** * run * * run component * * @return array parameter list */ public function run() { $this->addConfigAsGlobalVars(); if (!$this->isUserInGroup($this->config['requiredgroup']) && !in_array($this->config['action'], array('display', 'list'))) { $this->loadTemplates('anon'); return $this->config; } // by default, table env is the same as table if (empty($this->config['tableenv'])) { $this->config['tableenv'] = $this->config['table']; } // patch tmplDir with table name $tmplDir = $this->config['tmplDir']; $this->config['tmplDir'] = $tmplDir . '/' . $this->config['tableenv']; if ($this->isUserInGroup($this->config['requiredgroup'])) { $this->table->switchTranslation(false); } $p = null; $this->config['action'] = strtolower($this->config['action']); // managesortcolumn is required if (!empty($this->config['managesortcolumn'])) { $this->config['level_' . $this->config['levels'] . '_checked'] = 'selected="selected" checked="checked"'; } else if (in_array($this->config['action'], array('moveup', 'movedown'))) { $this->config['action'] = 'edit'; } // administrators may manage other's entries $foreign = $this->user->getId(); if ($this->isUserInGroup($this->config['requiredgroupmanager']) || WBContent::GROUP_ANON == $this->config['requiredgroupmanager']) { $foreign = null; } if (in_array($this->config['action'], array('display', 'list')) && $this->config['showsomeoneelses']) { $foreign = null; } $search = $this->getSearchQuery(); // reset id $this->id = $this->config['id']; $this->config['id'] = '__new'; // load record $p = array(array()); if($this->id != '__new') { $p = $this->table->get($this->config['table'], $this->id, $foreign); } if (count($p) != 1) { $p = array(array()); $this->id = '__new'; } $p = $p[0]; $this->injectFilter(); $this->tmpl->addGlobalVar('id', $this->id); switch ($this->config['action']) { case 'moveup': $this->moveUp($p); $this->loadTemplates('list'); break; case 'movedown': $this->moveUp($p, false); $this->loadTemplates('list'); break; case 'add': $this->id = '__new'; $this->processForm('edit', $p); break; case 'edit': $this->addRelatedData($p); $tmp = array(); foreach ($p as $k => $v) { if (is_scalar($v)) { $tmp[$k] = $v; } } $this->tmpl->addGlobalVars($tmp); $this->processForm('edit', $p); break; case 'rm': $this->tmpl->addGlobalVars($p); // delete? if ($this->config['action'] == 'rm' && $this->id != '__new') { if ($this->req->get('force', 'no') != 'yes') { $this->loadTemplates('rm'); $this->config['tmplDir'] = $tmplDir; return $this->config; break; } $this->table->delete($this->config['table'], $this->id, $foreign); $this->loadTemplates('rmDeleted'); $this->config['tmplDir'] = $tmplDir; return $this->config; } break; case 'display': $this->tmpl->addGlobalVars($p); $this->loadTemplates('display'); break; case 'list': default: $this->loadTemplates('list'); break; } $this->displayList($foreign, $search); return $this->config; } /** * Set filter clause * * Allow (sub classes) to append or override clause * @param array $clause * @param bool $append */ protected function setClause($clause, $append = false) { if ($append) { $this->clause[] = $clause; return; } $this->clause = $clause; } /** * Inject filter parameter * * In case filters should be used, process filter form and add values to * clause. * * @see onFilterValid() */ protected function injectFilter() { if (!$this->config['usefilter']) { return; } $this->req->set('filter_save', 'always'); $this->processForm('filter', array(), 'filter_save'); $this->req->set('filter_save', null); $html = $this->processForm('filter', array(), 'filter_save'); $this->tmpl->addGlobalVar('filter', $html); } /** * Select filter * * Use filter values to create search clause and build get-parameter string * as well as list of hidden input fields to pass filter options through pages * and forms. * * @param patForms $form * @param array $values */ public function onFilterValid($form, $values) { $get = array(); $input = array(); foreach ($values as $key => $value) { // build search filter parameter if (is_array($value)) { foreach ($value as $v) { $get[] = sprintf('%s[]=%s', $key, urlencode($v)); $input[] = sprintf('', $key, addslashes($v)); } } else { $get[] = sprintf('%s=%s', $key, urlencode($value)); $input[] = sprintf('', $key, addslashes($value)); } // build clause if ('__NULL' == $value) { continue; } if ('__ZERO' == $value) { $value = 0; } $field = explode('_', $key, 2); $field = $field[1]; $clause = array( 'field' => $field, 'value' => $value ); if (is_array($value)) { $clause['relation'] = 'in'; } $this->setClause($clause, true); } $this->tmpl->addGlobalVar('search_filter_url', implode('&', $get)); $this->tmpl->addGlobalVar('search_filter_input_hidden', implode('', $input)); return false; } /** * display list of records in table * * In case there is a template named "list_entry", it will be filled with paged * rows of table * * @param string $foreign key of user table * @param array $search */ protected function displayList($foreign, $search) { if (!$this->tmpl->exists('list_entry')) { return; } $clause = $this->clause; $options = array(); $id = __CLASS__ . '-' . $this->part . '-'. $this->config['table'] . '-' . $this->config['tableenv']; if ($this->config['limit'] && intval($this->config['limit']) > 0) { $options['limit'] = intval($this->config['limit']); } $this->buildSearchClause($search, $clause); $pager = $this->table->getPager($id, $this->config['table'], $foreign, $clause, $options); $this->tmpl->addGlobalVars($pager->browse($this->config['goto']), 'pager_'); $this->tmpl->addRows('list_entry', $pager->get()); } /** * Build search clause from query string * * See for search string and try to transform it to clause. Add clause to given clause (reference). * * 1) Normalize search query * split to words, remove spaces, remove shorts words (less than 3 chars) * 2) Build like statement for all searchable fields and words * 3) Add to clause as complex sub-clause * * CAUTION: make sure, that search query is passed by pager as well. * * @param array $query * @param array $clause */ private function buildSearchClause($query, &$clause) { // search for strings in all searchfields foreach ($query as $q) { $c = array(); foreach ($this->config['searchfields'] as $f) { $c[] = array( 'field' => $f, 'relation' => 'like', 'value' => $q ); } $clause[] = array( 'type' => 'complex', 'bond' => 'or', 'clause' => $c ); } } /** * Get search query array * * Extract relevent search query from request parameter and add as global * template variables * * @return array */ private function getSearchQuery() { if (empty($this->config['searchfields'])) { return array(); } $query = trim($this->req->get('searchquery', '')); if (empty($query)) { return array(); } $query = urldecode($query); // normalize query $query = explode(' ', $query); $query = array_map('trim', $query); foreach ($query as $i => $q) { if (empty($q) || 3 > strlen($q)) { unset($query[$i]); } } $query = array_values($query); $searchquery = implode(' ', $query); $this->tmpl->addGlobalVar('search_query', $searchquery); $this->tmpl->addGlobalVar('search_query_url', urlencode($searchquery)); return $query; } /** * receive output * * fetch output of this content component * * @return string */ public function getString() { return $this->tmpl->getParsedTemplate('snippet'); } /** * Save user's primary data * * Update user's forename and surname * * @param patForms $form * @param array $values */ public function onEditValid($form, $values) { $new = false; if ('__new' == $this->id) { $new = true; } // set user id if (!empty($this->config['addcurrentuser'])) { switch ($this->config['addcurrentuser']) { case 'new': if ($new) { $values[$this->table->getIdentifier('user')] = $this->user->getId(); } break; case 'always': $values[$this->table->getIdentifier('user')] = $this->user->getId(); break; default: case 'never': break; } } if ($this->initRelationTable()) { $this->relTable->extract($values); } $this->tmpl->addGlobalVars($values); // add value for sortcolumn if ($new && !empty($this->config['managesortcolumn'])) { $col = $this->config['managesortcolumn']; $options = array( 'limit' => 1, 'order' => array( 'field' => $col, 'asc' => 0 ), 'column' => array($col) ); $clause = array(); $last = $this->table->get($this->config['table'], null, null, $clause, $options); if (empty($last)) { $values[$col] = 0; } else { $values[$col] = ++$last[0][$col]; } } // delete old relations if (!$new && $this->initRelationTable()) { $this->relTable->delete($this->id); } $id = $this->table->save($this->config['table'], $this->id, $values); if ($this->initRelationTable()) { $this->relTable->add($id); } $eData = array( 'table' => $this->config['table'], 'id' => $id, 'new' => intval($new), 'save' => $values ); WBEvent::trigger('tableeditor:save:' . $this->config['table'], 'Saved record in table', $eData); $this->tmpl->addGlobalVar('id', $id); return true; } /** * Load data from related tables * * Resolve foreign keys stored in other tables and merge widata * * @param array &$data */ private function addRelatedData(&$data) { if ('__new' == $this->id) { return; } if (!$this->initRelationTable()) { return; } $this->relTable->inject($data); } /** * Start relation table * * If relations are configured, start relation table object */ private function initRelationTable() { if ($this->relTable) { return true; } // load relation config $config = WBClass::create('WBConfig'); $config->load($this->getFormConfigDir(). '/edit'); $conf = $config->get('relation', array()); if (empty($conf)) { return false; } $this->relTable = WBClass::create('WBDatasource_RelationTable'); $this->relTable->setTable($this->table); $this->relTable->setConfig($this->config['table'], $conf); return true; } /** * move record up in list * * Aktually swap position with next record to move current record * up or down. * * @param array $data record to move * @param bool $up move up (true) or down (false) */ private function moveUp($data, $up = true) { if (empty($this->config['managesortcolumn'])) { return; } $col = $this->config['managesortcolumn']; // shortcut applies for moving up first row (which is on top already) if ($up && 1 > $data[$col]) { return; } $levels = intval($this->config['levels']); if ($levels < 1) { $levels = 1; } // find other record to swap with $options = array( 'limit' => $levels, 'order' => array( 'field' => $col, 'asc' => 0 ), 'column' => array($col) ); $clause = array(); $clause[] = array( 'field' => $col, 'relation' => 'lt', 'value' => $data[$col] ); if (!$up) { $clause[0]['relation'] = 'gt'; $options['order']['asc'] = 1; } $other = $this->table->get($this->config['table'], null, null, $clause, $options); // if there is not upper entry, skip the rest if (empty($other)) { return; } // selected too many if (count($other) > $levels) { return; } // save current records in new position $last = array_pop($other); $save = array( $col => $last[$col] ); $this->table->save($this->config['table'], $this->id, $save); $primary = $this->table->getIdentifier($this->config['table']); // move all other items to the next position while (0 < count($other)) { $id = $last[$primary]; $last = array_pop($other); $save = array( $col => $last[$col] ); $this->table->save($this->config['table'], $id, $save); } // save last item in current item's previous position $id = $last[$primary]; $save = array( $col => $data[$col] ); $this->table->save($this->config['table'], $id, $save); } /** * location of form config * * Return sub directory where form element definitions are located * * @return string folder */ protected function getFormConfigDir() { return 'table/' . $this->config['tableenv']; } /** * Add form rules to just created form * * * * @param patForms $form object * @param string name of the xml- and template-filename * @param array $elements list of elements current form * @return bool true on success */ protected function insertFormRules(&$form, $name, $elements) { return true; } }