* @license PHP License * @package WB * @subpackage content */ /** * Load classes */ WBClass::load('WBContent' , 'patI18n' . 'WBCache'); /** * Content component: Tree (Node) Editor * * Edit tree nodes * * @version 1.3.1 * @package WB * @subpackage content */ class WBContent_TreeEditor extends WBContent { /** * my parameter list * * - action tell class what shall be done * - requiredgroup * - table tree table * - tableenv if table env is set, it will be used to load config and templates * - 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', 'tableenv' => '', 'managesortcolumn' => '', 'levels' => 1, // not implemented, yet 'addcurrentuser' => 'new', 'translator' => 'no', 'translatorgroup' => 'nls-translator' ); /** * Tree datasource object * @var WBDatasource_Tree */ private $tree; /** * selected node * @var array */ private $node = array(); /** * relation table * @var WBDatasource_RelationTable */ private $relTable; /** * NLS Saver * @var WBDatasource_NLS_Saver */ private $nlsSaver; /** * User is allowed to edit content and translations * @const edit mode */ const EDIT_MODE_NORMAL = 1; /** * User may edit translations only * @const translation mode */ const EDIT_MODE_TRANSLATE = 2; /** * Current Edit Mode * @var int */ private $editMode = self::EDIT_MODE_NORMAL; /** * List Of Form Element NLS Saver Handles * @var array */ private $nlsSaverFormElement = array(); /** * Run * * Run component * * @return array parameter list */ public function run() { if (!$this->isUserInGroup($this->config['requiredgroup'])) { if (!in_array($this->config['translator'], array('auto', 'on')) || !$this->isUserInGroup($this->config['translatorgroup'])) { $this->loadTemplates('anon'); return $this->config; } $this->editMode = self::EDIT_MODE_TRANSLATE; // limit action if (!in_array($this->config['action'], array('list', 'edit'))) { $this->config['action'] = 'list'; } } // 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']; // use tree table $params = array( 'treetable' => $this->config['table'] ); $this->tree = WBClass::create('WBDatasource_Tree', $params); $this->tree->switchTranslation(false); $this->nlsSaver = WBClass::create('WBDatasource_NLS_Saver'); $this->nlsSaver->setTableName($this->config['table']); $this->nlsSaver->enable(false); if (in_array($this->config['translator'], array('auto', 'on'))) { $this->nlsSaver->enable(true); } $this->addConfigAsGlobalVars(); if (self::EDIT_MODE_NORMAL == $this->editMode) { $this->tmpl->addGlobalVar('treeeditor_mode', 'edit'); } $id = $this->req->get('id', ''); switch ($this->config['action']) { case 'mv': // @todo implement propperly try{ $src = $this->req->get('src'); $des = $this->req->get('id'); $this->tree->moveChild($src, $des); } catch(WBException $e) { // nothing to do } $this->loadNode(); $this->loadTemplates('list'); break; case 'edit': if ($this->loadNode()) { $this->addRelatedData($this->node); if (self::EDIT_MODE_TRANSLATE == $this->editMode) { /** @var WBDatasource_Table */ $table = WBClass::create('WBDatasource_Table'); $list = $table->get('nlslangtranslator', null, $this->user->getId()); $lang = array(); foreach ($list as $l) { $lang[] = $l['lang']; } $this->nlsSaver->setAllowedLang($lang); } $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(null, false)) { $this->moveNode(substr($this->config['action'], 4)); $par = $this->tree->getParent($id); if (empty($par)) { $id = null; } else { $id = $par[0]['id']; } } $this->req->set('id', $id); $this->loadNode(); $this->loadTemplates('list'); break; default: case 'list': $this->loadNode(); $this->loadTemplates('list'); break; } $this->displayList($id); // 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($id) { // selected item 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); $pCount = 0; if (!empty($parents)) { $pCount = count($parents); if ($this->tmpl->exists('parent_entry')) { $this->tmpl->addRows('parent_entry', $parents); } } $this->tmpl->addGlobalVar('parent_count', $pCount); } /** * 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 (empty($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 (self::EDIT_MODE_TRANSLATE == $this->editMode) { if (empty($this->node)) { $this->loadTemplates('list'); return false; } $values = array_merge($this->node, $values); $this->nlsSaver->strip($values); $this->nlsSaver->save($values); $this->loadTemplates('list'); return false; } if ($this->initRelationTable()) { $this->relTable->extract($values); } $this->nlsSaver->strip($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); if ($this->initRelationTable()) { $this->relTable->add($this->node['id']); } $this->nlsSaver->setId($this->node['id']); $this->nlsSaver->save($values); break; case 'edit': if ('always' == $this->config['addcurrentuser']) { $values[$this->tree->getIdentifier('user')] = $this->user->getId(); } if ($this->initRelationTable()) { $this->relTable->delete($this->node['id']); $this->relTable->add($this->node['id']); } $this->nlsSaver->save($values); $this->tree->set($this->node['id'], $values); break; default: break; } $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['tableenv']; } /** * Fetch List of Form Elements * * Use parent's method and add translators form elements * * @use WBDatasource_NLS_Saver::addFormElements() * @param string name of the xml- and template-filename * @return array $elements */ protected function getFormElementList($name) { $elements = parent::getFormElementList($name); $this->nlsSaverFormElement = $this->nlsSaver->addFormElements($elements); if (self::EDIT_MODE_TRANSLATE == $this->editMode) { foreach ($elements as $k => &$e) { if (isset($e['translate']) && 'no' == $e['translate']) { unset($elements[$k]); continue; } if (in_array($k, $this->nlsSaverFormElement)) { continue; } $e['attributes']['required'] = 'no'; $e['attributes']['disabled'] = 'yes'; } } return $elements; } /** * What to Do When Rendering Form Element * * @param string form element name * @param array element data * @return bool true to go on, false to handle yourself */ protected function onFormElementRendered($name, $data) { if (in_array($name, $this->nlsSaverFormElement)) { $name = explode(WBDatasource_NLS_Saver::ELEMENT_TRANSLATION_DELIMITER, $name); if ($this->tmpl->exists('nls_translator_' . $name[0])) { $this->tmpl->addVar('nls_translator_' . $name[0], 'with_translator', 1); $data['lang'] = $name[1]; $this->tmpl->addRow('nls_translator_' . $name[0] . '_nav', $data, 'ELEMENT_'); $this->tmpl->addRow('nls_translator_' . $name[0] . '_element', $data, 'ELEMENT_'); } return false; } return true; } /** * load menu item * * Load node from tree table or simply inject raw data * * @param array raw data * @param bool wheather to add to template * @return string $id if correct null otherwise */ private function loadNode($raw = null, $add2Tmpl = true) { if (!empty($raw)) { $this->node = $raw; $this->req->set('id', $raw['id']); if ($add2Tmpl) { $this->tmpl->addGlobalVars($this->node, 'node_'); } $this->nlsSaver->setId($raw['id']); return $raw['id']; } // selected menu item $id = $this->req->get('id', ''); $this->node = array(); $this->nlsSaver->setId('__new'); if(empty($id)) { return null; } try{ $this->node = $this->tree->get($id); } catch (WBException_Datasource $e) { $this->node = array(); return null; } $this->nlsSaver->setId($id); if ($add2Tmpl) { $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['tableenv'], $conf); return true; } }