* @license PHP License * @package WB * @subpackage content */ /** * Load classes */ WBClass::load('WBContent' , 'patI18n' . 'WBCache'); /** * Content component: Tree (Node) Editor * * Edit tree nodes * * @version 1.0.0 * @package WB * @subpackage content */ class WBContent_TreeEditor extends WBContent { /** * my parameter list * * - action tell class what shall be done * - requiredgroup * - talbe tree table * - alphalength of tree table * - managesortcolumn column name or empty ("") * - levels number of levels to move, if managesortcolumn applies (1) * * @var array */ protected $config = array( 'action' => 'list', 'requiredgroup' => 'contenteditor', 'table' => 'menu', 'managesortcolumn' => '', 'levels' => 1, // not implemented, yet 'addcurrentuser' => 'new' ); /** * Tree datasource object * @var WBDatasource_Tree */ private $tree; /** * selected node * @var array */ private $node = array(); /** * relation table * @var WBDatasource_RelationTable */ private $relTable; /** * run * * run component * * @return array parameter list */ public function run() { if (!$this->isUserInGroup($this->config['requiredgroup'])) { $this->loadTemplates('anon'); return $this->config; } // patch tmplDir with table name $tmplDir = $this->config['tmplDir']; $this->config['tmplDir'] = $tmplDir . '/' . $this->config['table']; // use tree table $params = array( 'treetable' => $this->config['table'] ); $this->tree = WBClass::create('WBDatasource_Tree', $params); // if not in display mode, don't translate anything $this->tree->switchTranslation(false); $this->tmpl->addGlobalVars($this->config); switch ($this->config['action']) { case 'edit': if ($this->loadNode()) { $this->addRelatedData($this->node); $this->processForm('edit', $this->node); } else { $this->loadTemplates('list'); } break; case 'add': $this->loadNode(); $this->processForm('edit', array()); break; case 'rm': if ($this->loadNode()) { if ('yes' == $this->req->get('force', 'no')) { $parent = $this->tree->getParent($this->req->get('id')); $this->tree->delete($this->req->get('id')); $this->loadTemplates('list'); // override parent data if root if (is_null($parent)) { $parent = array(); foreach ($this->node as $k => $v) { $parent[$k] = ''; } $parent['id'] = null; } $this->loadNode($parent); break; } $this->loadTemplates('rm'); } else { $this->loadTemplates('list'); } break; case 'movetop': case 'moveup': case 'movedown': case 'movebottom': case 'moverollup': case 'moverolldown': if ($this->loadNode()) { $this->moveNode(substr($this->config['action'], 4)); } $this->loadTemplates('list'); break; default: case 'list': $this->loadNode(); $this->loadTemplates('list'); break; } $this->displayList(); // undo tmplDir patch $this->config['tmplDir'] = $tmplDir; return $this->config; } /** * receive output * * fetch output of this content component * * @return string */ public function getString() { return $this->tmpl->getParsedTemplate('snippet'); } /** * display selected branch * */ private function displayList() { // selected item $id = $this->req->get('id', ''); if (empty($id)) { $id = 0; } // add list of children $children = $this->tree->getChildren($id); $this->tmpl->addGlobalVar('list_count', count($children)); if ($this->tmpl->exists('list_entry')) { $this->tmpl->addRows('list_entry', $children); } // add list of parent nodes $parents = $this->tree->getParent($id, 0); $this->tmpl->addGlobalVar('parent_count', count($parents)); if ($this->tmpl->exists('parent_entry')) { $this->tmpl->addRows('parent_entry', $parents); } } /** * move node's position * * Calculate new position for each node and save it. This method always * saves all the siblings, regardless whether their position is unchanged. * * Direction can be one of: up, down, top, bottom, rollup and rolldown * * @todo change implementation like at TableEditor to avoid loading all siblings * @param string $dir move direction */ private function moveNode($dir) { if (empty($this->config['managesortcolumn'])) { return; } $sortCol = $this->config['managesortcolumn']; // swap node with parent $node = $this->node; $tmp = $this->tree->getParent($node['id']); if (null === $tmp) { $tmp = array($this->tree->get(null)); } $this->loadNode($tmp[0]); $siblings = $this->tree->getChildren($this->node['id']); // find current position $pos = 0; foreach ($siblings as $sib) { if ($sib['id'] == $node['id']) { break; } ++$pos; } $top = array_slice($siblings, 0, $pos); $bot = array_slice($siblings, $pos + 1); switch ($dir) { case 'up': if (empty($top)) { return; } array_unshift($bot, array_pop($top)); $top[] = $node; break; case 'down': if (empty($bot)) { return; } $top[] = array_shift($bot); $top[] = $node; break; case 'top': if (empty($top)) { return; } array_unshift($top, $node); break; case 'bottom': if (empty($bot)) { return; } $bot[] = $node; break; case 'rollup': $top[] = $node; $bot[] = array_shift($top); break; case 'rolldown': $top[] = $node; array_unshift($top, array_pop($bot)); break; default: return; break; } $pos = 0; foreach ($top as $sib) { $this->tree->set($sib['id'], array($sortCol => $pos++)); } foreach ($bot as $sib) { $this->tree->set($sib['id'], array($sortCol => $pos++)); } } /** * store menu record * * Menu record is valid, save either as new child or override existing one. * * @param patForms $form * @param array $values * @return bool whether to continue with template engine */ protected function onEditValid($form, $values) { if ($this->initRelationTable()) { $this->relTable->extract($values); } switch ($this->config['action']) { case 'add': $parent = 0; if (!empty($this->node)) { $parent = $this->node['id']; } // count siblings to get new position if (!empty($this->config['managesortcolumn'])) { $siblings = $this->tree->getChildren($parent); $values[$this->config['managesortcolumn']] = count($siblings); } if ('always' == $this->config['addcurrentuser'] || 'new' == $this->config['addcurrentuser']) { $values[$this->tree->getIdentifier('user')] = $this->user->getId(); } $this->node['id'] = $this->tree->addChild($parent, $values); break; case 'edit': if ('always' == $this->config['addcurrentuser']) { $values[$this->tree->getIdentifier('user')] = $this->user->getId(); } $this->tree->set($this->node['id'], $values); if ($this->initRelationTable()) { $this->relTable->delete($this->node['id']); } break; default: break; } if ($this->initRelationTable()) { $this->relTable->add($this->node['id']); } $this->loadTemplates('list'); return false; } /** * location of form config * * Return sub directory where form element definitions are located * * @return string folder */ protected function getFormConfigDir() { return 'table/' . $this->config['table']; } /** * load menu item * * Load node from tree table or simply inject raw data * * @param array raw data * @return string $id if correct null otherwise */ private function loadNode($raw = null) { if (!empty($raw)) { $this->node = $raw; $this->req->set('id', $raw['id']); $this->tmpl->addGlobalVars($this->node, 'node_'); return $raw['id']; } // selected menu item $id = $this->req->get('id', ''); $this->node = array(); if(empty($id)) { return null; } try{ $this->node = $this->tree->get($id); } catch (WBException_Datasource $e) { $this->node = array(); return null; } $this->tmpl->addGlobalVars($this->node, 'node_'); return $id; } /** * Load data from related tables * * Resolve foreign keys stored in other tables and merge widata * * @param array &$data */ private function addRelatedData(&$data) { 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->setConfig($this->config['table'], $conf); return true; } }