0) { $cachelib->empty_type_cache('menu_' . $menuId . '_'); } else { $menus = $this->list_menus(); foreach ($menus['data'] as $menu_info) { $cachelib->empty_type_cache('menu_' . $menu_info['menuId'] . '_'); } } } public function list_menus($offset = 0, $maxRecords = -1, $sort_mode = 'menuId_asc', $find = '') { if ($find) { $findesc = '%' . $find . '%'; $mid = " where (`name` like ? or `description` like ?)"; $bindvars = [$findesc,$findesc]; } else { $mid = ""; $bindvars = []; } $query = "select * from `tiki_menus` $mid order by " . $this->convertSortMode($sort_mode); $result = $this->query($query, $bindvars, $maxRecords, $offset); $query_cant = "select count(*) from `tiki_menus` $mid"; $cant = $this->getOne($query_cant, $bindvars); $ret = []; while ($res = $result->fetchRow()) { $query = "select count(*) from `tiki_menu_options` where `menuId`=?"; $res["options"] = $this->getOne($query, [(int) $res["menuId"]]); $ret[] = $res; } $retval = []; $retval["data"] = $ret; $retval["cant"] = $cant; return $retval; } public function replace_menu($menuId, $name, $description = '', $type = 'd', $icon = null, $use_items_icons = 'n', $parse = 'n') { // Check the name if (isset($menuId) and $menuId > 0) { $query = "update `tiki_menus` set `name`=?,`description`=?,`type`=?, `icon`=?, `use_items_icons`=?, `parse`=? where `menuId`=?"; $bindvars = [$name,$description,$type,$icon,$use_items_icons,$parse,(int) $menuId]; $this->empty_menu_cache($menuId); } else { // was: replace into. probably we need a delete here $query = "insert into `tiki_menus` (`name`,`description`,`type`,`icon`,`use_items_icons`,`parse`) values(?,?,?,?,?,?)"; $bindvars = [$name,$description,$type,$icon,$use_items_icons,$parse]; } $result = $this->query($query, $bindvars); return true; } public function clone_menu($menuId, $name, $description = '') { $menus = $this->table('tiki_menus'); $row = $menus->fetchFullRow([ 'menuId' => $menuId ]); $row['menuId'] = null; $row['name'] = $name; $row['description'] = $description; $newId = $menus->insert($row); $menuoptions = $this->table('tiki_menu_options'); $oldoptions = $menuoptions->fetchAll($menuoptions->all(), [ 'menuId' => $menuId ]); $row = null; foreach ($oldoptions as $row) { $row['optionId'] = null; $row['menuId'] = $newId; $menuoptions->insert($row); } } /* * Replace the current menu options for id 42 with what's in tiki.sql */ public function reset_app_menu() { $tiki_sql = file_get_contents('db/tiki.sql'); preg_match_all('/^(?:INSERT|UPDATE) (?:INTO )?`?tiki_menu_options`? .*$/mi', $tiki_sql, $matches); if ($matches && count($matches[0])) { $menuoptions = $this->table('tiki_menu_options'); $menuoptions->deleteMultiple([ 'menuId' => 42 ]); foreach ($matches[0] as $query) { $this->query($query); } $this->empty_menu_cache(42); } } public function get_max_option($menuId) { $query = "select max(`position`) from `tiki_menu_options` where `menuId`=?"; $max = $this->getOne($query, [(int) $menuId]); return $max; } public function replace_menu_option($menuId, $optionId, $name, $url, $type = 'o', $position = 1, $section = '', $perm = '', $groupname = '', $level = 0, $icon = '', $class = '') { if ($optionId) { $query = "update `tiki_menu_options` set `name`=?,`url`=?,`type`=?,`position`=?,`section`=?,`perm`=?,`groupname`=?,`userlevel`=?,`icon`=?,`class`=? where `optionId`=?"; $bindvars = [$name,$url,$type,(int) $position,$section,$perm,$groupname,$level,$icon,$class,$optionId]; } else { $query = "insert ignore into `tiki_menu_options`(`menuId`,`name`,`url`,`type`,`position`,`section`,`perm`,`groupname`,`userlevel`,`icon`,`class`) values(?,?,?,?,?,?,?,?,?,?,?)"; $bindvars = [(int) $menuId,$name,$url,$type,(int) $position,$section,$perm,$groupname,$level,$icon,$class]; } $this->empty_menu_cache($menuId); $result = $this->query($query, $bindvars, -1, -1, TikiDb::ERR_EXCEPTION); if (! $optionId) { $optionId = $this->lastInsertId(); } return $optionId; } public function remove_menu($menuId) { $query = "delete from `tiki_menus` where `menuId`=?"; $result = $this->query($query, [(int) $menuId]); $options = $this->list_menu_options($menuId); foreach ($options["data"] as $option) { TikiLib::lib('attribute')->set_attribute('menu', $option['optionId'], 'tiki.menu.templatedgroupid', null); } $query = "delete from `tiki_menu_options` where `menuId`=?"; $result = $this->query($query, [(int) $menuId]); $this->empty_menu_cache($menuId); return true; } public function remove_menu_option($optionId) { $query = "select `menuId` from `tiki_menu_options` where `optionId`=?"; $menuId = $this->getOne($query, [(int) $optionId]); $query = "delete from `tiki_menu_options` where `optionId`=?"; $result = $this->query($query, [(int) $optionId]); TikiLib::lib('attribute')->set_attribute('menu', $optionId, 'tiki.menu.templatedgroupid', null); $this->empty_menu_cache($menuId); return true; } public function get_menu_option($optionId) { $query = "select * from `tiki_menu_options` where `optionId`=?"; $result = $this->query($query, [(int) $optionId]); if (! $result->numRows()) { return false; } $res = $result->fetchRow(); return $res; } public function prev_pos($optionId) { $query = "select `position`, `menuId` from `tiki_menu_options` where `optionId` =?"; $result = $this->query($query, [$optionId]); if (! ($res = $result->fetchRow())) { return; } $position1 = $res['position']; $menuId = $res['menuId']; $query = "select `position` from `tiki_menu_options` where `menuId` =? and `position` < ? order by `position` desc"; if (! ($position = $this->getOne($query, [$menuId, $position1]))) { return; } $query = "update `tiki_menu_options` set `position`=? where `position`=? and `menuId`=? "; $result = $this->query($query, [$position1, $position, $menuId]); $query = "update `tiki_menu_options` set `position`=? where `optionId`=?"; $result = $this->query($query, [$position, $optionId,]); $this->empty_menu_cache($menuId); } public function next_pos($optionId) { $query = "select `position`, `menuId` from `tiki_menu_options` where `optionId` =?"; $result = $this->query($query, [$optionId]); if (! ($res = $result->fetchRow())) { return; } $position1 = $res['position']; $menuId = $res['menuId']; $query = "select `position` from `tiki_menu_options` where `menuId` =? and `position` > ? order by `position` asc"; if (! $position = $this->getOne($query, [$menuId, $position1])) { return; } $query = "update `tiki_menu_options` set `position`=? where `position`=? and `menuId`=? "; $result = $this->query($query, [$position1, $position, $menuId]); $query = "update `tiki_menu_options` set `position`=? where `optionId`=?"; $result = $this->query($query, [$position, $optionId]); $this->empty_menu_cache($menuId); } /** * Add parent option to each of the result of list_menu_options * * Formerly created the field "type_description" with description of the now deprecated type * * @param array $options * @return array */ public function prepare_options_for_editing($options) { if (isset($options['data'])) { $cant = $options['cant']; $options = $options['data']; } $treeOut = []; $types = [ 's' => tra('section level 0'), "r" => tra('sorted section level 0'), '1' => tra('section level 1'), '2' => tra('section level 2'), '3' => tra('section level 3'), 'o' => tra('option'), '-' => tra('separator'), ]; $current_parents_branch = []; $treeOut = array_map(function ($option) use (&$current_parents_branch, &$types) { if ($option['type'] === 's' || $option['type'] === 'r') { array_splice($current_parents_branch, 0); $option['parent'] = 0; array_push($current_parents_branch, $option['optionId']); } elseif (is_numeric($option['type'])) { array_splice($current_parents_branch, (int) $option['type']); $option['parent'] = end(array_values($current_parents_branch)); array_push($current_parents_branch, $option['optionId']); } elseif ($option['type'] === 'o') { $option['parent'] = end(array_values($current_parents_branch)); } elseif ($option['type'] === '-') { array_pop($current_parents_branch); } $option['type_description'] = $types[$option['type']]; return $option; }, $options); $treeOut = array_filter($treeOut, function ($option) { return $option['type'] !== '-'; }); if (isset($cant)) { $options = [ 'data' => $treeOut, 'cant' => $cant ]; } return $options; } // rename all the url of the form ((pageName)) public function rename_wiki_page($oldName, $newName) { $query = "update `tiki_menu_options` set `url`=? where `url`=?"; $result = $this->query($query, ['((' . $newName . '))', '((' . $oldName . '))']); $query = "select `menuId` from `tiki_menu_options` where `url`=?"; $result = $this->fetchAll($query, ['((' . $newName . '))']); foreach ($result as $p) { $this->empty_menu_cache($p['menuId']); } // try to change some tiki-index.php?page - very limitted: for another http://anothersite/tiki-index.php?page= must not be changed $query = "select * from `tiki_menu_options` where `url` like ?"; $result = $this->query($query, ["%tiki-index.php?page=$oldName%"]); $query = "update `tiki_menu_options` set `url`=? where `optionId`=?"; $menu_cache_removed = []; while ($res = $result->fetchRow()) { $p = parse_url($res['url']); if ($p['path'] == 'tiki-index.php') { parse_str($p['query'], $p); if ($p['page'] == $oldName) { $url = str_replace($oldName, $newName, $res['url']); $this->query($query, [$url, $res['optionId']]); if (! isset($menu_cache_removed[$p['menuId']])) { $menu_cache_removed[$p['menuId']] = 1; $this->empty_menu_cache($p['menuId']); } } } } } // look if the current url matches the menu option - to be improved a lot public function menuOptionMatchesUrl($option) { global $prefs; if (empty($option['url'])) { return false; } $url = str_replace('+', ' ', str_replace('&', '&', urldecode($_SERVER['REQUEST_URI']))); $option['url'] = str_replace('+', ' ', str_replace('&', '&', urldecode($option['url']))); if (strstr($option['url'], 'structure=') && ! strstr($url, 'structure=')) { // try to find al the occurence of the page in structures $option['url'] = preg_replace('/&structure=.*/', '', $option['url']); } if (preg_match('/.*tiki.index.php$/', $url)) { $wikilib = TikiLib::lib('wiki'); $homePage = $wikilib->get_default_wiki_page(); $url .= "?page=$homePage"; } if (preg_match('/.*tiki.index.php$/', $option['url'])) { $wikilib = TikiLib::lib('wiki'); $homePage = $wikilib->get_default_wiki_page(); $option['url'] .= "?page=$homePage"; } $pos = false; if ($prefs['feature_sefurl'] == 'y' && ! empty($option['sefurl'])) { $pos = strpos($url, '/' . str_replace('&', '&', urldecode($option['sefurl']))); // position in $url $lg = 1 + strlen($option['sefurl']); } if ($pos === false) { $pos = strpos(strtolower($url), strtolower($option['url'])); $lg = strlen($option['url']); } if ($pos !== false) { $last = $pos + $lg; if ($last >= strlen($url) || $url[$last] == '#' || $url[$last] == '?' || $url[$last] == '&') { return true; } } return false; } // assign selected and selectedAscendant to a menu // sectionLevel ->shows only the list of submenus where the url is find in this level // toLevel -> do not show more than this level // also sets setion open/close according to javascript and cookies public function setSelected($channels, $sectionLevel = '', $toLevel = '', $params = '') { if (! empty($params['subMenu'])) { $subMenu = []; $cant = 0; $in = false; $optionLevel = $level = 0; foreach ($channels['data'] as $position => $option) { if (is_numeric($option['type'])) { $optionLevel = $option['type']; } elseif ($option['type'] == '-') { $optionLevel = $optionLevel - 1; } elseif ($option['type'] == 'r' || $option['type'] == 's') { $optionLevel = 0; } if ($in && $optionLevel <= $level) { break; } elseif ($in) { $subMenu[] = $option; $cant++; } elseif (! $in && $option['optionId'] == $params['subMenu']) { $level = $optionLevel; $in = true; } if ($option['type'] != '-' && $option['type'] != 'o') { ++$optionLevel; } } $channels = ['data' => $this->lower($subMenu), 'cant' => $cant]; } $selecteds = []; $optionLevel = 0; if (is_numeric($sectionLevel)) { // must extract only the submenu level sectionLevel where the current url is $findUrl = false; $cant = 0; foreach ($channels['data'] as $position => $option) { if (is_numeric($option['type'])) { $optionLevel = $option['type']; } elseif ($option['type'] == '-') { $optionLevel = $optionLevel - 1; } elseif ($option['type'] == 'r' || $option['type'] == 's') { $optionLevel = 0; } if ($optionLevel < $sectionLevel) { //close the submenu if ($findUrl) { break; } if (! empty($subMenu)) { unset($subMenu); } $cant = 0; } if ($optionLevel >= $sectionLevel - 1 && ! empty($option['url']) && $this->menuOptionMatchesUrl($option)) { $findUrl = true; } if ($optionLevel >= $sectionLevel) { $subMenu[] = $option; ++$cant; if (empty($selectedPosition) && $option['type'] != 'o' && $option['type'] != '-') { // not pretty but works - optionLevel will get "shifted up" by $sectionLevel later in lower() $selecteds[$optionLevel - $sectionLevel] = $cant - 1; } if (! empty($option['url']) && $this->menuOptionMatchesUrl($option)) { $findUrl = true; $selectedPosition = $cant - 1; } } if ($option['type'] != '-' && $option['type'] != 'o') { ++$optionLevel; } } if (! empty($subMenu) && $findUrl && $cant) { $subMenu = $this->lower($subMenu); $channels['data'] = $subMenu; $channels['cant'] = $cant; } else { $channels['data'] = []; $channels['cant'] = 0; } } else { foreach ($channels['data'] as $position => $option) { if (is_numeric($option['type'])) { $optionLevel = $option['type']; } elseif ($option['type'] == '-') { $optionLevel = $optionLevel - 1; } elseif ($option['type'] == 'r' || $option['type'] == 's') { $optionLevel = 0; } if ($option['type'] != 'o' && $option['type'] != '-') { $selecteds[$optionLevel] = $position; } if ($this->menuOptionMatchesUrl($option)) { $selectedPosition = $position; break; } if ($option['type'] != '-' && $option['type'] != 'o') { ++$optionLevel; } } } if (isset($selectedPosition)) { $channels['data'][$selectedPosition]['selected'] = true; for ($o = 0; $o < $optionLevel; ++$o) { if ($o !== $selectedPosition) { $channels['data'][$selecteds[$o]]['selectedAscendant'] = true; } } } if (is_numeric($toLevel)) { $subMenu = []; $cant = 0; foreach ($channels['data'] as $position => $option) { if (is_numeric($option['type'])) { $optionLevel = $option['type']; } elseif ($option['type'] == '-') { $optionLevel = $optionLevel - 1; } elseif ($option['type'] == 'r' || $option['type'] == 's') { $optionLevel = 0; } if ($optionLevel <= $toLevel) { $subMenu[] = $option; $cant++; } if ($option['type'] != '-' && $option['type'] != 'o') { ++$optionLevel; } } $channels = ['data' => $subMenu, 'cant' => $cant]; } // set sections open/close according to cookie global $prefs; foreach ($channels['data'] as $position => &$option) { if (! empty($params['menu_cookie']) && $params['menu_cookie'] == 'n') { if (! empty($option['selected']) || ! empty($option['selectedAscendant'])) { $option['open'] = true; } } else { if (empty($params['id']) && ! empty($params['structureId'])) { $params['id'] = $params['structureId']; } $ck = isset($option['position']) ? getCookie('menu' . $params['id'] . '__' . $option['position'], 'menu') : 'o'; if ($prefs['javascript_enabled'] === 'n') { $option['open'] = true; } elseif ($ck === 'o') { $option['open'] = true; } elseif ($ck === 'c') { $option['open'] = false; } } } return $channels; } public function lower($subMenu) { $lower = false; foreach ($subMenu as $i => $option) { // begin all the secrtion at 0 to have a nice display if (is_numeric($option['type'])) { if ($lower === false) { $lower = $option['type']; } $subMenu[$i]['type'] -= $lower; if ($subMenu[$i]['type'] == 0) { $subMenu[$i]['type'] = 's'; // section levels go: s, 1, 2, 3 etc } } } return $subMenu; } // check if a option belongs to a menu public function check_menu_option($menuId, $optionId) { $query = 'SELECT `menuId` FROM `tiki_menu_options` WHERE `optionId` = ?'; $dbMenuId = $this->getOne($query, [$optionId]); if ($dbMenuId == $menuId) { return true; } else { return false; } } public function import_menu_options($menuId) { $smarty = TikiLib::lib('smarty'); $options = []; $fname = $_FILES['csvfile']['tmp_name']; $fhandle = fopen($fname, "r"); $fields = fgetcsv($fhandle, 1000); if (! $fields[0]) { $smarty->assign('msg', tra('The file has incorrect syntax or is not a CSV file')); $smarty->display("error.tpl"); die; } while (! feof($fhandle)) { $res = ['optionId' => '', 'type' => '', 'name' => '', 'url' => '', 'position' => 0, 'section' => '', 'perm' => '', 'groupname' => '', 'userlevel' => '', 'class' => '', 'icon' => '', 'remove' => '']; $data = fgetcsv($fhandle, 1000); if (empty($data)) { continue; } for ($i = 0, $icount_fields = count($fields); $i < $icount_fields; $i++) { $res[$fields[$i]] = $data[$i]; } if (empty($res['optionId']) || $this->check_menu_option($menuId, $res['optionId'])) { $options[] = $res; } else { $smarty->assign('msg', tra('You can only use optionId = 0 to create a new option; or, to update a menu, use an optionId that is the same as an optionId that is already used in the menu.')); $smarty->display('error.tpl'); die; } } fclose($fhandle); foreach ($options as $option) { if ($option['remove'] == 'y') { $this->remove_menu_option($option['optionId']); } else { $this->replace_menu_option($menuId, $option['optionId'], $option['name'], $option['url'], $option['type'], $option['position'], $option['section'], $option['perm'], $option['groupname'], $option['userlevel'], $option['icon'], $option['class']); } } } public function export_menu_options($menuId, $encoding) { $data = '"optionId","type","name","url","position","section","perm","groupname","userlevel","class","icon","remove"' . "\r\n"; $options = $this->list_menu_options($menuId, 0, -1, 'position_asc', '', true, 0, true); foreach ($options['data'] as $option) { $data .= $option['optionId'] . ',"' . $option['type'] . '","' . str_replace('"', '""', $option['name']) . '","' . str_replace('"', '""', $option['canonic']) . '",' . $option['position'] . ',"' . $option['section'] . '","' . $option['perm'] . '","' . $option['groupname'] . '",' . $option['userlevel'] . ',' . $option['class'] . ',' . $option['icon'] . ',"n"' . "\r\n" ; } if (empty($encoding)) { $encoding = 'UTF-8'; } elseif ($encoding == 'ISO-8859-1') { $data = utf8_decode($data); } header("Content-type: text/comma-separated-values; charset:" . $encoding); header("Content-Disposition: attachment; filename=" . tra('menu') . "_" . $menuId . ".csv"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0,pre-check=0"); header("Pragma: public"); echo $data; die; } public function get_option($menuId, $url) { $query = 'select `optionId` from `tiki_menu_options` where `menuId`=? and `url`=?'; return $this->getOne($query, [$menuId, $url]); } public function get_menu($menuId) { $res = $this->table('tiki_menus')->fetchFullRow(['menuId' => (int) $menuId]); if (empty($res['icon'])) { $res['oicon'] = null; } else { $res['oicon'] = dirname($res['icon']) . '/o' . basename($res['icon']); } return $res; } public function list_menu_options($menuId, $offset = 0, $maxRecords = -1, $sort_mode = 'position_asc', $find = '', $full = false, $level = 0, $do_not_parse = false) { global $user, $tiki_p_admin, $prefs; $wikilib = TikiLib::lib('wiki'); include_once('tiki-sefurl.php'); $options = $this->table('tiki_menu_options'); $conditions = [ 'menuId' => $menuId, ]; if ($find) { $conditions['search'] = $options->expr('(`name` like ? or `url` like ?)', ["%$find%", "%$find%"]); } if ($level && $prefs['feature_userlevels'] == 'y') { $conditions['userlevel'] = $options->lesserThan($level + 1); } $menu = $this->get_menu($menuId); $sort = $options->expr($this->convertSortMode($sort_mode)); $result = $options->fetchAll($options->all(), $conditions, $maxRecords, $offset, $sort); $cant = $options->fetchCount($conditions); $ret = []; foreach ($result as $res) { $res['canonic'] = $res['url']; $resourceGroups = array_filter(explode(',', $res['groupname'] ?: '')); if (! $do_not_parse) { if (isset($menu['parse']) && $menu['parse'] === 'y') { $res['name'] = TikiLib::lib('parser')->parse_data($res['name'], ['suppress_icons' => true]); } else { $res['name'] = htmlspecialchars($res['name']); } } if (preg_match('|^\(\((.+?)\)\)$|', $res['url'], $matches)) { $res['url'] = 'tiki-index.php?page=' . rawurlencode($matches[1]); $res['sefurl'] = $wikilib->sefurl($matches[1]); $perms = Perms::get(['type' => 'wiki page', 'object' => $matches[1]]); if (! $perms->view && ! $perms->wiki_view_ref) { continue; } } else { $res['sefurl'] = filter_out_sefurl($res['url']); } if (! $full) { $display = true; if (isset($res['section']) and $res['section']) { if (strstr($res['section'], '|')) { $display = false; $sections = preg_split('/\s*\|\s*/', $res['section']); foreach ($sections as $sec) { if (! isset($prefs[$sec]) or $prefs[$sec] != 'y') { $display = true; break; } } } else { $display = true; $sections = preg_split('/\s*,\s*/', $res['section']); foreach ($sections as $sec) { if (! isset($prefs[$sec]) or $prefs[$sec] != 'y') { $display = false; break; } } } } if ($display && $tiki_p_admin != 'y') { if (isset($res['perm']) and $res['perm']) { if (strstr($res['perm'], '|')) { $display = false; $sections = preg_split('/\s*\|\s*/', $res['perm']); foreach ($sections as $sec) { if (isset($GLOBALS[$sec]) && $GLOBALS[$sec] == 'y') { $display = true; break; } } } else { $sections = preg_split('/\s*,\s*/', $res['perm']); $display = true; foreach ($sections as $sec) { if (! isset($GLOBALS[$sec]) or $GLOBALS[$sec] != 'y') { $display = false; break; } } } } $userGroups = $this->get_user_groups($user); if (count($resourceGroups) > 0) { $intersect = array_intersect($resourceGroups, $userGroups); if (count($intersect) < 1) { $display = false; } } } if ($display) { $pos = $res['position']; if (empty($ret[$pos]) || empty($ret[$pos]['url'])) { $ret[$pos] = $res; } } } else { $ret[] = $res; } } return [ 'data' => array_values($ret), 'cant' => $cant, ]; } /* *gets result from list_menu_options and sorts "sorted section" sections. */ public function sort_menu_options($channels) { $sorted_channels = []; if (! isset($channels['data']) || $channels['cant'] == 0) { return $channels; } $cant = $channels['cant']; $channels = $channels['data']; $temp_max = count($channels); for ($i = 0; $i < $temp_max; $i++) { $sorted_channels[$i] = $channels[$i]; if ($sorted_channels[$i]['type'] == 'r') { // sorted section $sorted_channels[$i]['type'] = 's'; // common section, let's make it transparent $i++; $section = []; while ($i < count($channels) && $channels[$i]['type'] == 'o') { $section[] = $channels[$i]; $i++; } $i--; //include_once('lib/smarty_tiki/function.menu.php'); usort($section, "compare_menu_options"); $sorted_channels = array_merge($sorted_channels, $section); } } if (isset($cant)) { $sorted_channels = ['data' => $sorted_channels, 'cant' => $cant]; } return $sorted_channels; } public static function clean_menu_html($data) { $data = preg_replace('/