1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8//this script may only be included - so its better to die if called directly. 9if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) !== false) { 10 header('location: index.php'); 11 exit; 12} 13class StructLib extends TikiLib 14{ 15 public $displayLanguageOrder; 16 17 public function __construct() 18 { 19 global $prefs; 20 parent::__construct(); 21 22 $this->displayLanguageOrder = []; 23 } 24 public function s_export_structure($structure_id) 25 { 26 global $exportlib, $tikidomain; 27 global $dbTiki; 28 include_once('lib/wiki/exportlib.php'); 29 include_once('lib/tar.class.php'); 30 $page_info = $this->s_get_structure_info($structure_id); 31 $page_name = $page_info['pageName']; 32 $zipname = $page_name . '.zip'; 33 $tar = new tar(); 34 $pages = $this->s_get_structure_pages($page_info['page_ref_id']); 35 foreach ($pages as $page) { 36 $data = $exportlib->export_wiki_page($page['pageName'], 0); 37 $tar->addData($page['pageName'], $data, $this->now); 38 } 39 $dump = 'dump'; 40 if ($tikidomain) { 41 $dump .= "/$tikidomain"; 42 } 43 $tar->toTar("$dump/$page_name.tar", false); 44 header("location: $dump/$page_name.tar"); 45 return ''; 46 } 47 public function s_export_structure_tree($structure_id, $level = 0) 48 { 49 $structure_tree = $this->get_subtree($structure_id); 50 $level = 0; 51 $first = true; 52 header('Content-type: text/plain; charset=utf-8'); 53 foreach ($structure_tree as $node) { 54 //This special case indicates head of structure 55 if ($node['first'] and $node['last']) { 56 print (tra('Use this tree to copy the structure') . ': ' . $node['pageName'] . "\n\n"); 57 } elseif ($node['first'] or ! $node['last']) { 58 if ($node['first'] and ! $first) { 59 $level++; 60 } 61 $first = false; 62 for ($i = 0; $i < $level; $i++) { 63 print (' '); 64 } 65 print ($node['pageName']); 66 if (! empty($node['page_alias'])) { 67 print("->" . $node['page_alias']); 68 } 69 print("\n"); 70 } else { 71 //node is a place holder for last in level 72 $level--; 73 } 74 } 75 } 76 public function s_remove_page($page_ref_id, $delete, $name = '') 77 { 78 // Now recursively remove 79 global $user, $prefs, $tiki_p_remove; 80 if ($prefs['feature_user_watches'] == 'y') { 81 include_once('lib/notifications/notificationemaillib.php'); 82 sendStructureEmailNotification(['action' => 'remove', 'page_ref_id' => $page_ref_id, 'name' => $name]); 83 } 84 $query = 'select `page_ref_id`, ts.`page_id`, `pageName` '; 85 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 86 $query .= 'where tp.`page_id`=ts.`page_id` and `parent_id`=?'; 87 $result = $this->query($query, [(int) $page_ref_id]); 88 //Iterate down through the child nodes 89 while ($res = $result->fetchRow()) { 90 $this->s_remove_page($res['page_ref_id'], $delete); 91 } 92 //Only delete a page if other structures arent referencing it 93 if ($delete && $tiki_p_remove == 'y') { 94 $page_info = $this->s_get_page_info($page_ref_id); 95 $query = 'select count(*) from `tiki_structures` where `page_id`=?'; 96 $count = $this->getOne($query, [(int) $page_info['page_id']]); 97 $wikilib = TikiLib::lib('wiki'); 98 if ($count == 1 && $wikilib->is_editable($page_info['pageName'], $user)) { 99 $this->remove_all_versions($page_info['pageName']); 100 } 101 } 102 // Remove the space created by the removal 103 $page_info = $this->s_get_page_info($page_ref_id); 104 if (isset($page_info["parent_id"])) { 105 $query = "update `tiki_structures` set `pos`=`pos`-1 where `pos`>? and `parent_id`=?"; 106 $this->query($query, [(int) $page_info["pos"], (int) $page_info["parent_id"]]); 107 } 108 //Remove the structure node 109 $query = 'delete from `tiki_structures` where `page_ref_id`=?'; 110 $result = $this->query($query, [(int) $page_ref_id]); 111 return true; 112 } 113 public function promote_node($page_ref_id) 114 { 115 global $prefs; 116 $page_info = $this->s_get_page_info($page_ref_id); 117 $parent_info = $this->s_get_parent_info($page_ref_id); 118 //If there is a parent and the parent isnt the structure root node. 119 if (isset($parent_info) && $parent_info["parent_id"]) { 120 //Make a space for the node after its parent 121 $query = 'update `tiki_structures` set `pos`=`pos`+1 where `pos`>? and `parent_id`=?'; 122 $this->query($query, [(int) $parent_info['pos'], (int) $parent_info['parent_id']]); 123 //Move the node up one level 124 $query = 'update `tiki_structures` set `parent_id`=?, `pos`=(? + 1) where `page_ref_id`=?'; 125 $this->query($query, [(int) $parent_info['parent_id'], (int) $parent_info['pos'], (int) $page_ref_id]); 126 //Remove the space that was created by the promotion 127 $query = "update `tiki_structures` set `pos`=`pos`-1 where `pos`>? and `parent_id`=?"; 128 $this->query($query, [(int) $page_info["pos"], (int) $page_info["parent_id"]]); 129 if ($prefs['feature_user_watches'] == 'y') { 130 include_once('lib/notifications/notificationemaillib.php'); 131 sendStructureEmailNotification(['action' => 'move_up', 'page_ref_id' => $page_ref_id, 'parent_id' => $page_info['parent_id']]); 132 } 133 } 134 } 135 public function demote_node($page_ref_id) 136 { 137 $page_info = $this->s_get_page_info($page_ref_id); 138 $parent_info = $this->s_get_parent_info($page_ref_id); 139 $query = 'select `page_ref_id`, `pos` from `tiki_structures` where `pos`<? and `parent_id`=? order by `pos` desc'; 140 $result = $this->query($query, [(int) $page_info['pos'], (int) $page_info['parent_id']]); 141 if ($previous = $result->fetchRow()) { 142 //Get last child nodes for previous sibling 143 $query = 'select `pos` from `tiki_structures` where `parent_id`=? order by `pos` desc'; 144 $result = $this->query($query, [(int) $previous['page_ref_id']]); 145 if ($res = $result->fetchRow()) { 146 $pos = $res['pos']; 147 } else { 148 $pos = 0; 149 } 150 $query = 'update `tiki_structures` set `parent_id`=?, `pos`=(? + 1) where `page_ref_id`=?'; 151 $this->query($query, [(int) $previous['page_ref_id'], (int) $pos, (int) $page_ref_id]); 152 //Remove the space created by the demotion 153 $query = "update `tiki_structures` set `pos`=`pos`-1 where `pos`>? and `parent_id`=?"; 154 $this->query($query, [(int) $page_info["pos"], (int) $page_info["parent_id"]]); 155 global $prefs; 156 if ($prefs['feature_user_watches'] == 'y') { 157 include_once('lib/notifications/notificationemaillib.php'); 158 sendStructureEmailNotification(['action' => 'move_down', 'page_ref_id' => $page_ref_id, 'parent_id' => $previous['page_ref_id']]); 159 } 160 } 161 } 162 public function move_after_next_node($page_ref_id) 163 { 164 $page_info = $this->s_get_page_info($page_ref_id); 165 $query = 'select `page_ref_id`, `pos` from `tiki_structures` where `pos`>? and `parent_id`=? order by `pos` asc'; 166 $result = $this->query($query, [(int) $page_info['pos'], (int) $page_info['parent_id']]); 167 $res = $result->fetchRow(); 168 if ($res) { 169 //Swap position values 170 $query = 'update `tiki_structures` set `pos`=? where `page_ref_id`=?'; 171 $this->query($query, [(int) $page_info['pos'], (int) $res['page_ref_id']]); 172 $this->query($query, [(int) $res['pos'], (int) $page_info['page_ref_id']]); 173 } 174 } 175 public function move_before_previous_node($page_ref_id) 176 { 177 $page_info = $this->s_get_page_info($page_ref_id); 178 $query = 'select `page_ref_id`, `pos` from `tiki_structures` where `pos`<? and `parent_id`=? order by `pos` desc'; 179 $result = $this->query($query, [(int) $page_info['pos'], (int) $page_info['parent_id']]); 180 $res = $result->fetchRow(); 181 if ($res) { 182 //Swap position values 183 $query = 'update `tiki_structures` set `pos`=? where `page_ref_id`=?'; 184 $this->query($query, [(int) $res['pos'], (int) $page_info['page_ref_id']]); 185 $this->query($query, [(int) $page_info['pos'], (int) $res['page_ref_id']]); 186 } elseif ($page_info['pos'] > 1) { // a bug occurred - try to fix 187 $query = 'update `tiki_structures` set `pos`=? where `page_ref_id`=?'; 188 $this->query($query, [$page_info['pos'] - 1, (int) $page_info['page_ref_id']]); 189 } 190 } 191 192 /** 193 * @param $data array - from from nestedSortable('toHierarchy') 194 */ 195 196 public function reorder_structure($data) 197 { 198 global $user; 199 200 if (! empty($data)) { 201 $parent_ref_id = $data[0]->structure_id; 202 $structure_info = $this->s_get_structure_info($parent_ref_id); // "root" 203 204 if (TikiLib::lib('tiki')->user_has_perm_on_object($user, $structure_info['pageName'], 'wiki page', 'tiki_p_edit_structures')) { 205 $structure_id = $structure_info['structure_id']; 206 $tiki_structures = TikiDb::get()->table('tiki_structures'); 207 $orders = []; 208 $conditions = ['structure_id' => (int) $structure_id]; 209 210 foreach ($data as $node) { 211 if ($node->item_id != 'root') { 212 if (! isset($orders[$node->depth])) { 213 $orders[$node->depth] = 1; 214 } else { 215 $orders[$node->depth]++; 216 } 217 $node->parent_id = $node->parent_id == 'root' || empty($node->parent_id) ? $parent_ref_id : $node->parent_id; 218 $fields = [ 219 'parent_id' => $node->parent_id, 220 'pos' => $orders[$node->depth], 221 'page_alias' => $node->page_alias, 222 ]; 223 if ($node->item_id < 1000000) { 224 $conditions['page_ref_id'] = (int) $node->item_id; 225 $tiki_structures->update( 226 $fields, 227 $conditions 228 ); 229 } else { 230 // new nodes with id > 1000000 231 $fields['page_id'] = TikiLib::lib('tiki')->get_page_id_from_name($node->page_name); 232 $fields['structure_id'] = $structure_id; 233 $tiki_structures->insert($fields); 234 } 235 } 236 } 237 238 return $structure_info; 239 } 240 } 241 return false; 242 } 243 244 /** \brief Create a structure entry with the given name 245 \param parent_id The parent entry to add this to. 246 If NULL, create new structure. 247 \param after_ref_id The entry to add this one after. 248 If NULL, put it in position 0. 249 \param name The wiki page to reference 250 \param alias An alias for the wiki page name. 251 \return the new entries page_ref_id or null if not created. 252 */ 253 public function s_create_page($parent_id, $after_ref_id, $name, $alias = '', $structure_id = null, $options = []) 254 { 255 global $prefs; 256 $ret = null; 257 258 $hide_toc = isset($options['hide_toc']) ? $options['hide_toc'] : 'n'; 259 $creator = isset($options['creator']) ? $options['creator'] : tra('system'); 260 $creator_msg = isset($options['creator_msg']) ? $options['creator_msg'] : tra('created from structure'); 261 $ip_source = isset($options['ip_source']) ? $options['ip_source'] : '0.0.0.0'; 262 263 // If the page doesn't exist then create a new wiki page! 264 $newpagebody = ''; 265 if ($hide_toc !== 'y') { 266 $newpagebody = tra("Table of contents") . ":" . "{toc}"; 267 } 268 $created = $this->create_page($name, 0, $newpagebody, $this->now, $creator_msg, $creator, $ip_source, '', false, '', ['parent_id' => $parent_id]); 269 270 if (! empty($parent_id) || $created || ! $this->page_is_in_structure($name)) { 271 // if were not trying to add a duplicate structure head 272 $query = 'select `page_id` from `tiki_pages` where `pageName`=?'; 273 $page_id = $this->getOne($query, [$name]); 274 if (! empty($after_ref_id)) { 275 $max = $this->getOne('select `pos` from `tiki_structures` where `page_ref_id`=?', [(int) $after_ref_id]); 276 } else { 277 $max = 0; 278 } 279 if (! isset($after_ref_id)) { 280 // after_ref_id The entry to add this one after. If NULL, put it in position 0. 281 $max = 0; 282 $query = 'update `tiki_structures` set `pos`=`pos`+1 where `pos`>? and `parent_id`=?'; 283 $this->query($query, [(int) $max, (int) $parent_id]); 284 } elseif ($after_ref_id != 0) { 285 if ($max > 0) { 286 //If max is 5 then we are inserting after position 5 so we'll insert 5 and move all 287 // the others 288 $query = 'update `tiki_structures` set `pos`=`pos`+1 where `pos`>? and `parent_id`=?'; 289 $result = $this->query($query, [(int) $max, (int) $parent_id]); 290 } 291 } elseif (! $created) { 292 $max = $this->getOne('select max(`pos`) from `tiki_structures` where `parent_id`=?', [(int) $parent_id]); 293 } 294 // 295 //Create a new structure entry 296 $max++; 297 $query = 'insert into `tiki_structures`(`parent_id`,`page_id`,`page_alias`,`pos`, `structure_id`) values(?,?,?,?,?)'; 298 $result = $this->query($query, [(int) $parent_id, (int) $page_id, $alias, (int) $max, (int) $structure_id]); 299 //Get the page_ref_id just created 300 if (isset($parent_id)) { 301 $parent_check = ' and `parent_id`=?'; 302 $attributes = [(int) $page_id,$alias,(int) $max, (int) $parent_id]; 303 } else { 304 $parent_check = ' and (`parent_id` is null or `parent_id`=0)'; 305 $attributes = [(int) $page_id,$alias,(int) $max]; 306 } 307 $query = 'select `page_ref_id` from `tiki_structures` '; 308 $query .= 'where `page_id`=? and `page_alias`=? and `pos`=?'; 309 $query .= $parent_check; 310 $ret = $this->getOne($query, $attributes); 311 if (empty($parent_id)) { 312 $query = 'update `tiki_structures` set `structure_id`=? where `page_ref_id`=?'; 313 $this->query($query, [$ret, $ret]); 314 } 315 316 if ($prefs['feature_wiki_categorize_structure'] == 'y') { 317 $this->categorizeNewStructurePage($name, $this->s_get_structure_info($parent_id)); 318 } 319 320 if ($prefs['feature_user_watches'] == 'y') { 321 include_once('lib/notifications/notificationemaillib.php'); 322 sendStructureEmailNotification(['action' => 'add', 'page_ref_id' => $ret, 'name' => $name]); 323 } 324 } 325 return $ret; 326 } 327 328 /** 329 * Categorizes a (new) page the same as the parent structure 330 * Called from s_create_page if feature_wiki_categorize_structure = y 331 * 332 * @param string $page name of new page 333 * @param array $structure_info structure info 334 */ 335 public function categorizeNewStructurePage($page, $structure_info) 336 { 337 $categlib = TikiLib::lib('categ'); 338 339 $cat_type = 'wiki page'; 340 $cat_href = "tiki-index.php?page=" . urlencode($page); 341 342 $structObjectId = $categlib->is_categorized($cat_type, $structure_info["pageName"]); 343 if ($structObjectId) { 344 // structure is categorized 345 $pageObjectId = $categlib->is_categorized($cat_type, $page); 346 $structure_cats = $categlib->get_object_categories($cat_type, $structure_info["pageName"]); 347 if (! $pageObjectId) { 348 // added page is not categorized 349 $pageObjectId = $categlib->add_categorized_object($cat_type, $page, '', $page, $cat_href); 350 foreach ($structure_cats as $cat_acat) { 351 $categlib->categorize($pageObjectId, $cat_acat); 352 } 353 } else { 354 // added page is already categorized (somehow?) 355 $cats = $categlib->get_object_categories($cat_type, $page); 356 foreach ($structure_cats as $cat_acat) { 357 if (! in_array($cat_acat, $cats, true)) { 358 $categlib->categorize($pageObjectId, $cat_acat); 359 } 360 } 361 } 362 } 363 } 364 365 public function get_subtree($page_ref_id, $level = 0, $parent_pos = '') 366 { 367 $tikilib = TikiLib::lib('tiki'); 368 $ret = []; 369 $pos = 1; 370 //The structure page is used as a title 371 if ($level == 0) { 372 $struct_info = $this->s_get_page_info($page_ref_id); 373 $aux['first'] = true; 374 $aux['last'] = true; 375 $aux['pos'] = ''; 376 $aux['page_ref_id'] = $struct_info['page_ref_id']; 377 $aux['pageName'] = $struct_info['pageName']; 378 $aux['page_alias'] = $struct_info['page_alias']; 379 $wikilib = TikiLib::lib('wiki'); 380 $is_locked = $wikilib->is_locked($struct_info['pageName']); 381 if ($is_locked) { 382 $aux['flag'] = 'L'; 383 $aux['user'] = $is_locked; 384 } 385 $perms = $tikilib->get_perm_object($struct_info['pageName'], 'wiki page', '', false); 386 $aux['editable'] = $perms['tiki_p_edit']; 387 $aux['viewable'] = $perms['tiki_p_view']; 388 $ret[] = $aux; 389 $level++; 390 } 391 //Get all child nodes for this page_ref_id 392 $query = 'select `page_ref_id`, `page_alias`, `pageName`, `flag`, `user`, `pos` as db_pos '; 393 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 394 $query .= 'where ts.`page_id` = tp.`page_id` and `parent_id`=? order by `pos` asc'; 395 $result = $this->query($query, [(int) $page_ref_id]); 396 $subs = []; 397 $row_max = $result->numRows(); 398 while ($res = $result->fetchRow()) { 399 //Add 400 $aux['first'] = ($pos == 1); 401 $aux['db_pos'] = $res['db_pos']; 402 $aux['last'] = false; 403 $aux['page_ref_id'] = $res['page_ref_id']; 404 $aux['pageName'] = $res['pageName']; 405 $aux['page_alias'] = $res['page_alias']; 406 $aux["flag"] = $res["flag"]; 407 $aux["user"] = $res["user"]; 408 global $user; 409 if ($this->user_has_perm_on_object($user, $res['pageName'], 'wiki page', 'tiki_p_edit')) { 410 $aux['editable'] = 'y'; 411 $aux['viewable'] = 'y'; 412 } else { 413 $aux['editable'] = 'n'; 414 if ($this->user_has_perm_on_object($user, $res['pageName'], 'wiki page', 'tiki_p_view')) { 415 $aux['viewable'] = 'y'; 416 } else { 417 $aux['viewable'] = 'n'; 418 } 419 } 420 if (strlen($parent_pos) == 0) { 421 $aux['pos'] = "$pos"; 422 } else { 423 $aux['pos'] = $parent_pos . '.' . "$pos"; 424 } 425 $ret[] = $aux; 426 //Recursively add any child nodes 427 $subs = $this->get_subtree($res['page_ref_id'], ($level + 1), $aux['pos']); 428 if (isset($subs)) { 429 $ret = array_merge($ret, $subs); 430 } 431 // Insert a dummy entry to close table/list 432 if ($pos == $row_max) { 433 $aux['first'] = false; 434 $aux['last'] = true; 435 $ret[] = $aux; 436 } 437 $pos++; 438 } 439 return $ret; 440 } 441 /**Returns an array of page_info arrays 442 This can be used to construct a path from the 443 structure head to the requested page. 444 */ 445 public function get_structure_path($page_ref_id) 446 { 447 global $prefs; 448 $structure_path = []; 449 $page_info = $this->s_get_page_info($page_ref_id); 450 if ($page_info['parent_id']) { 451 $structure_path = $this->get_structure_path($page_info['parent_id']); 452 } 453 $structure_path[] = $page_info; 454 foreach ($structure_path as $key => $value) { 455 if ($prefs['namespace_indicator_in_structure'] === 'y' && ! empty($prefs['namespace_separator']) 456 && strpos($value['pageName'], $prefs['namespace_separator']) !== false) { 457 $arr = explode($prefs['namespace_separator'], $value['pageName']); 458 $structure_path[$key]['stripped_pageName'] = end($arr); 459 } else { 460 $structure_path[$key]['stripped_pageName'] = $value['pageName']; 461 } 462 } 463 return $structure_path; 464 } 465 /* get all the users that watches a page or a page above */ 466 public function get_watches($pageName = '', $page_ref_id = 0, $recurs = true) 467 { 468 global $tiki_p_watch_structure; 469 if ($tiki_p_watch_structure != 'y') { 470 return []; 471 } 472 $query = "SELECT ts.`parent_id`,tuw.`email`,tuw.`user`, tuw.`event`"; 473 $query .= " FROM `tiki_structures` ts"; 474 $query .= " LEFT JOIN ( 475 SELECT watchId, user, event, object, title, type, url, email FROM `tiki_user_watches` 476 UNION DISTINCT 477 SELECT watchId, uu.login as user, event, object, title, type, url, uu.email 478 FROM 479 `tiki_group_watches` tgw 480 INNER JOIN users_usergroups ug ON tgw.`group` = ug.groupName 481 INNER JOIN users_users uu ON ug.userId = uu.userId AND uu.email IS NOT NULL AND uu.email <> '' 482 ) tuw ON (tuw.`object`=ts.`page_ref_id` AND tuw.`event`=?)"; 483 if (empty($page_ref_id)) { 484 $query .= " LEFT JOIN `tiki_pages` tp ON ( tp.`page_id`=ts.`page_id`)"; 485 $query .= " WHERE tp.`pageName`=?"; 486 $result = $this->query($query, ['structure_changed', $pageName]); 487 } else { 488 $query .= " WHERE ts.`page_ref_id`=?"; 489 $result = $this->query($query, ['structure_changed', $page_ref_id]); 490 } 491 $ret = []; 492 while ($res = $result->fetchRow()) { 493 $parent_id = $res['parent_id']; 494 unset($res['parent_id']); 495 if (! empty($res['email']) || ! empty($res['user'])) { 496 $ret[] = $res; 497 } 498 } 499 if (! empty($parent_id) && $recurs) { 500 $ret2 = $this->get_watches('', $parent_id); 501 if (! empty($ret2)) { 502 $ret = array_merge($ret2, $ret); 503 } 504 } 505 return $ret; 506 } 507 /**Returns a structure_info array 508 See get_page_info for details of array 509 */ 510 public function s_get_structure_info($page_ref_id) 511 { 512 $parent_id = $this->getOne('select `parent_id` from `tiki_structures` where `page_ref_id`=?', [(int) $page_ref_id]); 513 if (! $parent_id) { 514 return $this->s_get_page_info($page_ref_id); 515 } 516 return $this->s_get_structure_info($parent_id); 517 } 518 /**Returns an array of info about the parent 519 page_ref_id 520 See get_page_info for details of array 521 */ 522 public function s_get_parent_info($page_ref_id) 523 { 524 // Try to get the parent of this page 525 $parent_id = $this->getOne('select `parent_id` from `tiki_structures` where `page_ref_id`=?', [(int) $page_ref_id]); 526 if (! $parent_id) { 527 return null; 528 } 529 return ($this->s_get_page_info($parent_id)); 530 } 531 532 public function use_user_language_preferences($langContext = null) 533 { 534 global $prefs; 535 if ($prefs['feature_multilingual'] != 'y') { 536 return; 537 } 538 if ($prefs['feature_multilingual_structures'] != 'y') { 539 return; 540 } 541 542 $multilinguallib = TikiLib::lib('multilingual'); 543 544 $this->displayLanguageOrder = $multilinguallib->preferredLangs($langContext); 545 } 546 547 public function build_language_order_clause(&$args, $pageTable = 'tp', $structTable = 'ts') 548 { 549 $query = " CASE\n"; 550 551 552 // Languages in preferences go first 553 foreach ($this->displayLanguageOrder as $key => $lang) { 554 $query .= "\tWHEN $pageTable.lang = ? THEN ?\n"; 555 $args[] = $lang; 556 $args[] = $key; 557 } 558 559 // If nothing in preferences, use structure default 560 $query .= "\tWHEN $structTable.page_id = $pageTable.page_id THEN ?\n"; 561 $args[] = count($this->displayLanguageOrder); 562 563 // Else should never be required 564 $query .= "\tELSE ?\nEND\n"; 565 $args[] = count($this->displayLanguageOrder) + 1; 566 567 return $query; 568 } 569 570 /** Return an array of page info 571 */ 572 public function s_get_page_info($page_ref_id) 573 { 574 if (empty($this->displayLanguageOrder)) { 575 $query = 'select `pos`, `page_ref_id`, `parent_id`, ts.`page_id`, `pageName`, `page_alias`, `structure_id` '; 576 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 577 $query .= 'where ts.`page_id`=tp.`page_id` and `page_ref_id`=?'; 578 $result = $this->query($query, [(int) $page_ref_id]); 579 } else { 580 $args = [ (int) $page_ref_id ]; 581 582 $query = " 583 SELECT 584 `pos`, 585 `page_ref_id`, 586 `parent_id`, 587 ts.`page_id`, 588 `pageName`, 589 `page_alias`, 590 `structure_id` 591 FROM 592 `tiki_structures` ts 593 LEFT JOIN tiki_translated_objects a ON a.type = 'wiki page' AND a.objId = ts.page_id 594 LEFT JOIN tiki_translated_objects b ON b.type = 'wiki page' AND a.traId = b.traId 595 LEFT JOIN `tiki_pages` tp ON b.`objId` = tp.`page_id` OR ts.page_id = tp.page_id 596 WHERE 597 `page_ref_id` = ? 598 ORDER BY " . $this->build_language_order_clause($args) 599 . " LIMIT 1"; 600 601 $result = $this->query($query, $args); 602 } 603 604 if ($res = $result->fetchRow()) { 605 return $res; 606 } else { 607 return null; 608 } 609 } 610 // that is intended to replace the get_subtree_toc and get_subtree_toc_slide 611 // it's used only in {toc} thing hardcoded in parse tikilib->parse -- (mose) 612 // the $tocPrefix can be used to Prefix a subtree as it would start from a given number (e.g. 2.1.3) 613 public function build_subtree_toc($id, $slide = false, $order = 'asc', $tocPrefix = '') 614 { 615 global $user, $tikilib, $prefs; 616 $ret = []; 617 $cant = $this->getOne('select count(*) from `tiki_structures` where `parent_id`=?', [(int) $id]); 618 if ($cant) { 619 // TODO : FIX 620 $args = []; 621 if (! $this->displayLanguageOrder) { 622 $query = 'select `page_ref_id`, `pageName`, `page_alias`, tp.`description` from `tiki_structures` ts, `tiki_pages` tp '; 623 $query .= 'where ts.`page_id`=tp.`page_id` and `parent_id`=? order by ' . $this->convertSortMode('pos_' . $order); 624 $args[] = (int) $id; 625 } else { 626 $query = " 627 SELECT 628 `page_ref_id`, 629 `pageName`, 630 `page_alias`, 631 tp.`description` 632 FROM 633 `tiki_structures` ts 634 INNER JOIN tiki_pages tp ON tp.page_id = ( 635 SELECT tp.page_id 636 FROM 637 `tiki_pages` tr 638 LEFT JOIN tiki_translated_objects a ON tr.page_id = a.objId AND a.type = 'wiki page' 639 LEFT JOIN tiki_translated_objects b ON b.type = 'wiki page' AND a.traId = b.traId 640 LEFT JOIN tiki_pages tp ON b.objId = tp.page_id OR tr.page_id = tp.page_id 641 WHERE 642 tr.page_id = ts.page_id 643 ORDER BY " . $this->build_language_order_clause($args) . " 644 LIMIT 1 645 ) 646 WHERE 647 parent_id = ? 648 order by " . $this->convertSortMode('pos_' . $order); 649 $args[] = (int) $id; 650 } 651 $result = $this->query($query, $args); 652 $prefix = 1; 653 while ($res = $result->fetchRow()) { 654 if (! $tikilib->user_has_perm_on_object($user, $res['pageName'], 'wiki page', 'tiki_p_view')) { 655 continue; 656 } 657 658 if ($prefs['namespace_indicator_in_structure'] === 'y' 659 && ! empty($prefs['namespace_separator']) 660 && ! empty($res['pageName']) 661 && strpos($res['pageName'], $prefs['namespace_separator']) !== false) { 662 $arr = explode($prefs['namespace_separator'], $res['pageName']); 663 $res['short_pageName'] = end($arr); 664 } else { 665 $res['short_pageName'] = $res['pageName']; 666 } 667 $res['prefix'] = ($tocPrefix == '') ? '' : "$tocPrefix."; 668 $res['prefix'] .= $prefix; 669 $prefix++; 670 if ($res['page_ref_id'] != $id) { 671 $sub = $this->build_subtree_toc($res['page_ref_id'], $slide, $order, $res['prefix']); 672 if (is_array($sub)) { 673 $res['sub'] = $sub; 674 } 675 } 676 //if ($res['page_alias']<>'') $res['pageName']=$res['page_alias']; 677 $back[] = $res; 678 } 679 } else { 680 return false; 681 } 682 return $back; 683 } 684 public function get_toc($page_ref_id, $order = 'asc', $showdesc = false, $numbering = true, $numberPrefix = '', $type = 'plain', $page = '', $maxdepth = 0, $mindepth = 0, $sortalpha = 0, $structurePageName = '') 685 { 686 global $user, $prefs; 687 688 $structure_tree = $this->build_subtree_toc($page_ref_id, false, $order, $numberPrefix); 689 690 if ($type === 'admin') { 691 // check perms here as we still have $page_ref_id 692 $structure_info = $this->s_get_structure_info($page_ref_id); 693 694 $perms = Perms::get('wiki page', $structure_info["pageName"]); 695 696 if ($prefs['lock_wiki_structures'] === 'y') { 697 $lockedby = TikiLib::lib('attribute')->get_attribute('wiki structure', $structure_info['pageName'], 'tiki.object.lock'); 698 if ($lockedby && $lockedby === $user && $perms->lock_structures || ! $lockedby || $perms->admin_structures) { 699 $editable = $perms->edit_structures; 700 } else { 701 $editable = false; 702 } 703 } else { 704 $editable = $perms->edit_structures; 705 } 706 707 if (! $editable) { 708 $type = 'plain'; 709 } else { 710 TikiLib::lib('smarty')->assign('structure_name', $structure_info["pageName"]); 711 $json_params = json_encode( 712 [ 713 'page_ref_id' => $page_ref_id, 714 'order' => $order, 715 'showdesc' => $showdesc, 716 'numbering' => $numbering, 717 'numberPrefix' => $numberPrefix, 718 'type' => $type, 719 'page' => $page, 720 'maxdepth' => $maxdepth, 721 'mindepth' => $mindepth, 722 'sortalpha' => $sortalpha, 723 'structurePageName' => $structurePageName 724 ] 725 ); 726 TikiLib::lib('smarty')->assign('json_params', $json_params); 727 } 728 } 729 730 if ($structure_tree != '') { 731 if ($mindepth > 0) { 732 $currentLevel = $structure_tree; 733 for ($i = 0; $i < $mindepth; $i++) { 734 $deeperLevel = []; 735 if ($currentLevel != '') { 736 foreach ($currentLevel as $leaf) { 737 if (isset($leaf['sub']) && is_array($leaf['sub'])) { 738 foreach ($leaf['sub'] as $sub) { 739 $deeperLevel[] = $sub; 740 } 741 } 742 } 743 } 744 $currentLevel = $deeperLevel; 745 } 746 if ($maxdepth > 0) { 747 $maxdepth = $maxdepth - $mindepth; 748 if ($maxdepth <= 0) { 749 $maxdepth = 1; 750 } 751 } 752 $structure_tree = $currentLevel; 753 } 754 if ($sortalpha == 'alpha') { 755 if ($order == 'asc') { 756 usort($structure_tree, [$this, 'compareByPageName']); 757 } else { 758 usort($structure_tree, [$this, 'compareByPageNameDesc']); 759 } 760 } 761 } 762 763 $nodelist = $this->fetch_toc($structure_tree, $showdesc, $numbering, $type, $page, $maxdepth, 0, $structurePageName); 764 if ($type === 'admin' && empty($nodelist)) { 765 $nodelist = "<ol class='admintoc' style='min-height: 4em;' data-params='$json_params'></ol>"; 766 } 767 return $nodelist . "\n"; 768 } 769 770 public function compareByPageName($a, $b) 771 { 772 return strcasecmp($a['pageName'], $b['pageName']); 773 } 774 775 public function compareByPageNameDesc($a, $b) 776 { 777 return strcasecmp($b['pageName'], $a['pageName']); 778 } 779 780 public function fetch_toc($structure_tree, $showdesc, $numbering, $type = 'plain', $page = '', $maxdepth = 0, $cur_depth = 0, $structurePageName = '') 781 { 782 $smarty = TikiLib::lib('smarty'); 783 global $user; 784 $ret = ''; 785 if ($structure_tree != '') { 786 if (($maxdepth <= 0) || ($cur_depth < $maxdepth)) { 787 $smarty->assign('toc_type', $type); 788 $ret .= $smarty->fetch('structures_toc-startul.tpl') . "\n"; 789 790 foreach ($structure_tree as $leaf) { 791 if (is_numeric($page)) { 792 $smarty->assign('hilite', $leaf["page_ref_id"] == $page); 793 } else { 794 $smarty->assign('hilite', $leaf["pageName"] == $page); 795 } 796 797 if ($type === 'admin') { 798 if ($this->user_has_perm_on_object($user, $leaf["pageName"], 'wiki page', 'tiki_p_edit')) { 799 $leaf['editable'] = true; 800 } else { 801 $leaf['editable'] = false; 802 } 803 if (TikiLib::lib('tiki')->user_watches($user, 'structure_changed', $leaf['page_ref_id'], 'structure')) { 804 $leaf['event'] = true; 805 } else { 806 $leaf['event'] = false; 807 } 808 } 809 810 $smarty->assign('structurePageName', $structurePageName); 811 $smarty->assign_by_ref('structure_tree', $leaf); 812 $smarty->assign('showdesc', $showdesc); 813 $smarty->assign('numbering', $numbering); 814 $ret .= $smarty->fetch('structures_toc-leaf.tpl'); 815 if (isset($leaf['sub']) && is_array($leaf['sub'])) { 816 $ret .= $this->fetch_toc($leaf['sub'], $showdesc, $numbering, $type, $page, $maxdepth, $cur_depth + 1, $structurePageName) . "</li>\n"; 817 } else { 818 $ret .= str_repeat("\t", ($cur_depth * 2) + 1) . "</li>\n"; 819 } 820 } 821 $ret .= $smarty->fetch('structures_toc-endul.tpl') . "\n"; 822 } 823 } 824 return $ret; 825 } 826 // end of replacement 827 public function page_is_in_structure($pageName) 828 { 829 $query = 'select count(*) '; 830 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 831 $query .= 'where ts.`page_id`=tp.`page_id` and `pageName`=?'; 832 $cant = $this->getOne($query, [$pageName]); 833 return $cant; 834 } 835 public function page_id_is_in_structure($pageId) 836 { 837 $query = 'select count(*) '; 838 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 839 $query .= 'where ts.`page_id`=tp.`page_id` and `page_id`=?'; 840 $cant = $this->getOne($query, [$pageId]); 841 return $cant; 842 } 843 //Is this page the head page for a structure? 844 public function get_struct_ref_if_head($pageName) 845 { 846 $query = 'select `page_ref_id` '; 847 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 848 $query .= 'where ts.`page_id`=tp.`page_id` and (`parent_id` is null or `parent_id`=0) and `pageName`=?'; 849 $page_ref_id = $this->getOne($query, [$pageName]); 850 if ($page_ref_id) { 851 return $page_ref_id; 852 } 853 854 if (! $this->displayLanguageOrder) { 855 return null; 856 } 857 858 $query = " 859 SELECT 860 page_ref_id 861 FROM 862 tiki_structures ts 863 INNER JOIN tiki_translated_objects a ON ts.page_id = a.objId AND a.type = 'wiki page' 864 INNER JOIN tiki_translated_objects b ON a.traId = b.traId AND b.type = 'wiki page' 865 INNER JOIN tiki_pages tp ON b.objId = tp.page_id 866 WHERE 867 (parent_id IS NULL or parent_id = 0) 868 AND pageName = ?"; 869 870 $page_ref_id = $this->getOne($query, [$pageName]); 871 return $page_ref_id; 872 } 873 //Get reference id for a page 874 public function get_struct_ref_id($pageName) 875 { 876 $query = 'select `page_ref_id` '; 877 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 878 $query .= 'where ts.`page_id`=tp.`page_id` and `pageName`=?'; 879 $page_ref_id = $this->getOne($query, [$pageName]); 880 return $page_ref_id; 881 } 882 public function get_next_page($page_ref_id, $deep = true) 883 { 884 // If we have children then get the first child 885 if ($deep) { 886 $query = 'select `page_ref_id` '; 887 $query .= 'from `tiki_structures` ts '; 888 $query .= 'where `parent_id`=? '; 889 $query .= 'order by ' . $this->convertSortMode('pos_asc'); 890 $result1 = $this->query($query, [(int) $page_ref_id]); 891 if ($result1->numRows()) { 892 $res = $result1->fetchRow(); 893 return $res['page_ref_id']; 894 } 895 } 896 // Try to get the next page with the same parent as this 897 $page_info = $this->s_get_page_info($page_ref_id); 898 $parent_id = $page_info['parent_id']; 899 $page_pos = $page_info['pos']; 900 if (! $parent_id) { 901 return null; 902 } 903 $query = 'select `page_ref_id` '; 904 $query .= 'from `tiki_structures` ts '; 905 $query .= 'where `parent_id`=? and `pos`>? '; 906 $query .= 'order by ' . $this->convertSortMode('pos_asc'); 907 $result2 = $this->query($query, [(int) $parent_id, (int) $page_pos]); 908 if ($result2->numRows()) { 909 $res = $result2->fetchRow(); 910 return $res['page_ref_id']; 911 } else { 912 return $this->get_next_page($parent_id, false); 913 } 914 } 915 public function get_prev_page($page_ref_id, $deep = false) 916 { 917 //Drill down to last child for this tree node 918 if ($deep) { 919 $query = 'select `page_ref_id` '; 920 $query .= 'from `tiki_structures` ts '; 921 $query .= 'where `parent_id`=? '; 922 $query .= 'order by ' . $this->convertSortMode('pos_desc'); 923 $result = $this->query($query, [$page_ref_id]); 924 if ($result->numRows()) { 925 //There are more children 926 $res = $result->fetchRow(); 927 $page_ref_id = $this->get_prev_page($res['page_ref_id'], true); 928 } 929 return $page_ref_id; 930 } 931 // Try to get the previous page with the same parent as this 932 $page_info = $this->s_get_page_info($page_ref_id); 933 $parent_id = $page_info['parent_id']; 934 $pos = $page_info['pos']; 935 //At the top of the tree 936 if (empty($parent_id)) { 937 return null; 938 } 939 $query = 'select `page_ref_id` '; 940 $query .= 'from `tiki_structures` ts '; 941 $query .= 'where `parent_id`=? and `pos`<? '; 942 $query .= 'order by ' . $this->convertSortMode('pos_desc'); 943 $result = $this->query($query, [(int) $parent_id, (int) $pos]); 944 if ($result->numRows()) { 945 //There is a previous sibling 946 $res = $result->fetchRow(); 947 $page_ref_id = $this->get_prev_page($res['page_ref_id'], true); 948 } else { 949 //No previous siblings, just the parent 950 $page_ref_id = $parent_id; 951 } 952 return $page_ref_id; 953 } 954 public function get_navigation_info($page_ref_id) 955 { 956 $struct_nav_pages = [ 957 'prev' => $this->get_neighbor_info($page_ref_id, 'get_prev_page'), 958 'next' => $this->get_neighbor_info($page_ref_id, 'get_next_page'), 959 'parent' => $this->get_neighbor_info($page_ref_id, 's_get_parent_info'), 960 'home' => $this->s_get_structure_info($page_ref_id), 961 ]; 962 963 return $struct_nav_pages; 964 } 965 966 /** 967 * Get structure info for a page's neighbour respecting view perms 968 * @param int $page_ref_id 969 * @param string $fn function to find neighbour (get_prev_page|get_next_page|s_get_parent_info) 970 * @return null | array neighbour page info 971 */ 972 private function get_neighbor_info($page_ref_id, $fn) 973 { 974 if (method_exists($this, $fn)) { 975 $neighbor = $this->$fn($page_ref_id); 976 if ($neighbor) { 977 if (is_array($neighbor)) { // s_get_parent_info() returns the info array 978 $info = $neighbor; 979 } else { 980 $info = $this->s_get_page_info($neighbor); 981 } 982 if ($info && Perms::get([ 'type' => 'wiki page', 'object' => $info['pageName'] ])->view) { 983 return $info; 984 } else { 985 return $this->get_neighbor_info($neighbor, $fn); 986 } 987 } else { 988 return null; 989 } 990 } else { 991 trigger_error('No structlib method found: ' . $fn); 992 return null; 993 } 994 } 995 /** Return an array of subpages 996 Used by the 'After Page' select box 997 */ 998 public function s_get_pages($parent_id) 999 { 1000 $ret = []; 1001 $query = 'select `pos`, `page_ref_id`, `parent_id`, ts.`page_id`, `pageName`, `page_alias` '; 1002 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 1003 $query .= 'where ts.`page_id`=tp.`page_id` and `parent_id`=? '; 1004 $query .= 'order by ' . $this->convertSortMode('pos_asc'); 1005 $result = $this->query($query, [(int) $parent_id]); 1006 while ($res = $result->fetchRow()) { 1007 //$ret[] = $this->populate_page_info($res); 1008 $ret[] = $res; 1009 } 1010 return $ret; 1011 } 1012 /** Get a list of all structures this page is a member of 1013 */ 1014 public function get_page_structures($pageName, $structure = '') 1015 { 1016 $ret = []; 1017 $structures_added = []; 1018 if (empty($this->displayLanguageOrder)) { 1019 $query = 'select `page_ref_id` '; 1020 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 1021 $query .= 'where ts.`page_id`=tp.`page_id` and (tp.`pageName`=? OR tp.`pageSlug`=?)'; 1022 } else { 1023 $query = " 1024 SELECT DISTINCT 1025 `page_ref_id` 1026 FROM 1027 tiki_structures ts 1028 LEFT JOIN tiki_translated_objects a ON a.objId = ts.page_id AND a.type = 'wiki page' 1029 LEFT JOIN tiki_translated_objects b ON a.traId = b.traId AND b.type = 'wiki page' 1030 LEFT JOIN tiki_pages tp ON ts.page_id = tp.page_id OR b.objId = tp.page_id 1031 WHERE 1032 tp.`pageName`=? OR tp.`pageSlug`=?"; 1033 } 1034 1035 $result = $this->query($query, [$pageName,$pageName]); 1036 while ($res = $result->fetchRow()) { 1037 $next_page = $this->s_get_structure_info($res['page_ref_id']); 1038 //Add each structure head only once 1039 if (! in_array($next_page['page_ref_id'], $structures_added)) { 1040 if (empty($structure) || $structure == $next_page['pageName']) { 1041 $structures_added[] = $next_page['page_ref_id']; 1042 $next_page['req_page_ref_id'] = $res['page_ref_id']; 1043 $ret[] = $next_page; 1044 } 1045 } 1046 } 1047 return $ret; 1048 } 1049 public function get_max_children($page_ref_id) 1050 { 1051 $query = 'select `page_ref_id` from `tiki_structures` where `parent_id`=?'; 1052 $result = $this->query($query, [(int) $page_ref_id]); 1053 if (! $result->numRows()) { 1054 return ''; 1055 } 1056 $res = $result->fetchRow(); 1057 return $res; 1058 } 1059 /** Return a unique list of pages belonging to the structure 1060 \return An array of page_info arrays 1061 */ 1062 public function s_get_structure_pages_unique($page_ref_id) 1063 { 1064 $ret = []; 1065 // Add the structure page as well 1066 $ret[] = $this->s_get_page_info($page_ref_id); 1067 $ret2 = $this->s_get_structure_pages($page_ref_id); 1068 return array_unique(array_merge($ret, $ret2)); 1069 } 1070 /** Return all the pages belonging to a structure 1071 \param Page reference ID in struct table 1072 \return An array of page_info arrays 1073 */ 1074 public function s_get_structure_pages($page_ref_id) 1075 { 1076 $ret = []; 1077 if ($page_ref_id) { 1078 $ret[0] = $this->s_get_page_info($page_ref_id); 1079 $query = 'select `pos`, `page_ref_id`, `parent_id`, ts.`page_id`, `pageName`, `page_alias` '; 1080 $query .= 'from `tiki_structures` ts, `tiki_pages` tp '; 1081 $query .= 'where ts.`page_id`=tp.`page_id` and `parent_id`=? '; 1082 $query .= 'order by ' . $this->convertSortMode('pos_asc'); 1083 $result = $this->query($query, [(int) $page_ref_id]); 1084 while ($res = $result->fetchRow()) { 1085 $ret = array_merge($ret, $this->s_get_structure_pages($res['page_ref_id'])); 1086 } 1087 } 1088 return $ret; 1089 } 1090 public function list_structures($offset, $maxRecords, $sort_mode, $find = '', $exact_match = true, $filter = []) 1091 { 1092 global $prefs; 1093 1094 if ($find) { 1095 if (! $exact_match && $find) { 1096 $find = preg_replace("/(\w+)/", "%\\1%", $find); 1097 $find = preg_split("/[\s]+/", $find, -1, PREG_SPLIT_NO_EMPTY); 1098 $mid = " where (`parent_id` is null or `parent_id`=0) and (tp.`pageName` like " . implode(' or tp.`pageName` like ', array_fill(0, count($find), '?')) . ")"; 1099 $bindvars = $find; 1100 } else { 1101 $mid = ' where (`parent_id` is null or `parent_id`=0) and (tp.`pageName` like ?)'; 1102 $findesc = '%' . $find . '%'; 1103 $bindvars = [$findesc]; 1104 } 1105 } else { 1106 $mid = ' where (`parent_id` is null or `parent_id`=0) '; 1107 $bindvars = []; 1108 } 1109 1110 // If language is set to '', assume that no language filtering should be done. 1111 if (isset($filter['lang']) && $filter['lang'] == '') { 1112 unset($filter['lang']); 1113 } 1114 1115 if ($prefs['feature_wiki_categorize_structure'] == 'y') { 1116 $category_jails = TikiLib::lib('categ')->get_jail(); 1117 if (! isset($filter['andCategId']) && ! isset($filter['categId']) && empty($filter['noCateg']) && ! empty($category_jails)) { 1118 $filter['categId'] = $category_jails; 1119 } 1120 } 1121 1122 $join_tables = ' inner join `tiki_pages` tp on (tp.`page_id`= ts.`page_id`)'; 1123 $join_bindvars = []; 1124 $distinct = ''; 1125 if (! empty($filter)) { 1126 foreach ($filter as $type => $val) { 1127 if ($type == 'categId') { 1128 $categories = TikiLib::lib('categ')->get_jailed((array) $val); 1129 $categories[] = -1; 1130 1131 $cat_count = count($categories); 1132 $join_tables .= " inner join `tiki_objects` as tob on (tob.`itemId`= tp.`pageName` and tob.`type`= ?) inner join `tiki_category_objects` as tc on (tc.`catObjectId`=tob.`objectId` and tc.`categId` IN(" . implode(', ', array_fill(0, $cat_count, '?')) . ")) "; 1133 1134 if ($cat_count > 1) { 1135 $distinct = ' DISTINCT '; 1136 } 1137 1138 $join_bindvars = array_merge(['wiki page'], $categories); 1139 } elseif ($type == 'lang') { 1140 $mid .= empty($mid) ? ' where ' : ' and '; 1141 $mid .= '`lang`=? '; 1142 $bindvars[] = $val; 1143 } 1144 } 1145 } 1146 1147 if (! empty($join_bindvars)) { 1148 $bindvars = empty($bindvars) ? $join_bindvars : array_merge($join_bindvars, $bindvars); 1149 } 1150 1151 $query = "select $distinct `page_ref_id`,`structure_id`,`parent_id`,ts.`page_id`,`page_alias`,`pos`, 1152 `pageName`,tp.`hits`,`data`,tp.`description`,`lastModif`,`comment`,`version`, 1153 `user`,`ip`,`flag`,`points`,`votes`,`cache`,`wiki_cache`,`cache_timestamp`, 1154 `pageRank`,`creator`,`page_size` from `tiki_structures` as ts $join_tables $mid order by " . $this->convertSortMode($sort_mode); 1155 $query_cant = "select count(*) from `tiki_structures` ts $join_tables $mid"; 1156 $result = $this->query($query, $bindvars, $maxRecords, $offset); 1157 $cant = $this->getOne($query_cant, $bindvars); 1158 $ret = []; 1159 while ($res = $result->fetchRow()) { 1160 global $user; 1161 if ($this->user_has_perm_on_object($user, $res['pageName'], 'wiki page', 'tiki_p_view')) { 1162 if (file_exists('whelp/' . $res['pageName'] . '/index.html')) { 1163 $res['webhelp'] = 'y'; 1164 } else { 1165 $res['webhelp'] = 'n'; 1166 } 1167 if ($this->user_has_perm_on_object($user, $res['pageName'], 'wiki page', 'tiki_p_edit')) { 1168 $res['editable'] = 'y'; 1169 } else { 1170 $res['editable'] = 'n'; 1171 } 1172 if ($this->user_has_perm_on_object($user, $res['pageName'], 'wiki page', 'tiki_p_edit_structures')) { 1173 $res['edit_structure'] = 'y'; 1174 } else { 1175 $res['edit_structure'] = 'n'; 1176 } 1177 if ($this->user_has_perm_on_object($user, $res['pageName'], 'wiki page', 'tiki_p_admin_structures')) { 1178 $res['admin_structure'] = 'y'; 1179 } else { 1180 $res['admin_structure'] = 'n'; 1181 } 1182 $ret[] = $res; 1183 } // end check for perm if 1184 } 1185 $retval = []; 1186 $retval['data'] = $ret; 1187 $retval['cant'] = $cant; 1188 return $retval; 1189 } 1190 public function get_page_alias($page_ref_id) 1191 { 1192 $query = 'select `page_alias` from `tiki_structures` where `page_ref_id`=?'; 1193 $res = $this->getOne($query, [(int) $page_ref_id]); 1194 return $res; 1195 } 1196 public function set_page_alias($page_ref_id, $pageAlias) 1197 { 1198 $query = 'update `tiki_structures` set `page_alias`=? where `page_ref_id`=?'; 1199 $this->query($query, [$pageAlias, (int) $page_ref_id]); 1200 } 1201 //This nifty function creates a static WebHelp version using a TikiStructure as 1202 //the base. 1203 public function structure_to_webhelp($page_ref_id, $dir, $top) 1204 { 1205 global $style_base; 1206 global $prefs; 1207 //The first task is to convert the structure into an array with the 1208 //proper format to produce a WebHelp project. 1209 //We have to create something in the form 1210 //$pages=Array('root'=>Array('pag1'=>'','pag2'=>'','page3'=>Array(...))); 1211 //Where the name is the pageName|description and the other side is either '' 1212 //when the page is a leaf or an Array of pages when the page is a folder 1213 //Folders that are not TikiPages are known for having only a name instead 1214 //of name|description 1215 $tree = '$tree=Array(' . $this->structure_to_tree($page_ref_id) . ');'; 1216 eval($tree); 1217 //Now we have the tree in $tree! 1218 $menucode = "foldersTree = gFld(\"Contents\", \"content.html\")\n"; 1219 $menucode .= $this->traverse($tree); 1220 $base = "whelp/$dir"; 1221 copy("$base/menu/options.cfg", "$base/menu/menuNodes.js"); 1222 $fw = fopen("$base/menu/menuNodes.js", 'a+'); 1223 fwrite($fw, $menucode); 1224 fclose($fw); 1225 $docs = []; 1226 $words = []; 1227 $index = []; 1228 $first = true; 1229 $pages = $this->traverse2($tree); 1230 // Now loop the pages 1231 foreach ($pages as $page) { 1232 $query = 'select * from `tiki_pages` where `pageName`=?'; 1233 $result = $this->query($query, [$page]); 1234 $res = $result->fetchRow(); 1235 $docs[] = $res['pageName']; 1236 if (empty($res['description'])) { 1237 $res['description'] = $res['pageName']; 1238 } 1239 $pageName = $res['pageName'] . '|' . $res['description']; 1240 $dat = TikiLib::lib('parser')->parse_data($res['data']); 1241 //Now dump the page 1242 $dat = preg_replace("/tiki-index.php\?page=([^\'\" ]+)/", "$1.html", $dat); 1243 $dat = str_replace('?nocache=1', '', $dat); 1244 $cs = ''; 1245 $data = "<html><head><script src=\"../js/highlight.js\"></script><link rel=\"StyleSheet\" href=\"../../../styles/$style_base.css\" type=\"text/css\" /><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> <title>" . $res["pageName"] . "</title></head><body style=\"padding:10px\" onload=\"doProc();\">$cs<div id='tiki-center'><div class='wikitext'>" . $dat . '</div></div></body></html>'; 1246 $fw = fopen("$base/pages/" . $res['pageName'] . '.html', 'wb+'); 1247 fwrite($fw, $data); 1248 fclose($fw); 1249 unset($dat); 1250 $page_words = preg_split("/[^A-Za-z0-9\-_]/", $res['data']); 1251 foreach ($page_words as $word) { 1252 $word = strtolower($word); 1253 if (strlen($word) > 3 && preg_match("/^[A-Za-z][A-Za-z0-9\_\-]*[A-Za-z0-9]$/", $word)) { 1254 if (! in_array($word, $words)) { 1255 $words[] = $word; 1256 $index[$word] = []; 1257 } 1258 if (! in_array($res['pageName'] . '|' . $res['description'], $index[$word])) { 1259 $index[$word][] = $res['pageName'] . '|' . $res['description']; 1260 } 1261 } 1262 } 1263 } 1264 sort($words); 1265 $i = 0; 1266 $fw = fopen("$base/js/searchdata.js", 'w'); 1267 fwrite($fw, "keywords = new Array();\n"); 1268 foreach ($words as $word) { 1269 fwrite($fw, "keywords[$i] = Array(\"$word\",Array("); 1270 $first = true; 1271 foreach ($index[$word] as $doc) { 1272 if (! $first) { 1273 fwrite($fw, ','); 1274 } else { 1275 $first = false; 1276 } 1277 fwrite($fw, '"' . $doc . '"'); 1278 } 1279 fwrite($fw, "));\n"); 1280 $i++; 1281 } 1282 fclose($fw); 1283 1284 // write the title page, using: 1285 // Browser Title, Logo, Site title, Site subtitle 1286 $fw = fopen("$base/content.html", 'w+'); 1287 $titlepage = "<h1>" . $prefs['browsertitle'] . "</h1><p><img src='../../" . $prefs['sitelogo_src'] . "' alt='" . $prefs['sitelogo_alt'] . "' style=\"text-align: center;\" /></p><h2>" . $prefs['sitetitle'] . "</h2><h3>" . $prefs['sitesubtitle'] . "</h3>"; 1288 fwrite($fw, $titlepage); 1289 fclose($fw); 1290 } 1291 1292 public function structure_to_tree($page_ref_id) 1293 { 1294 $query = 'select * from `tiki_structures` ts,`tiki_pages` tp where tp.`page_id`=ts.`page_id` and `page_ref_id`=?'; 1295 $result = $this->query($query, [(int) $page_ref_id]); 1296 $res = $result->fetchRow(); 1297 if (empty($res['description'])) { 1298 $res['description'] = $res['pageName']; 1299 } 1300 $name = str_replace("'", "\'", $res['description'] . '|' . $res['pageName']); 1301 $code = ''; 1302 $code .= "'$name'=>"; 1303 $query = 'select * from `tiki_structures` ts, `tiki_pages` tp where tp.`page_id`=ts.`page_id` and `parent_id`=?'; 1304 $result = $this->query($query, [(int) $page_ref_id]); 1305 if ($result->numRows()) { 1306 $code .= 'Array('; 1307 $first = true; 1308 while ($res = $result->fetchRow()) { 1309 if (! $first) { 1310 $code .= ','; 1311 } else { 1312 $first = false; 1313 } 1314 $code .= $this->structure_to_tree($res['page_ref_id']); 1315 } 1316 $code .= ')'; 1317 } else { 1318 $code .= "''"; 1319 } 1320 return $code; 1321 } 1322 public function traverse($tree, $parent = '') 1323 { 1324 $code = ''; 1325 foreach ($tree as $name => $node) { 1326 list($name,$link) = explode('|', $name); 1327 if (is_array($node)) { 1328 //New folder node is parent++ folder parent is paren 1329 $new = $parent . 'A'; 1330 $code .= 'foldersTree' . $new . "=insFld(foldersTree$parent,gFld(\"$name\",\"pages/$link.html\"));\n"; 1331 $code .= $this->traverse($node, $new); 1332 } else { 1333 $code .= "insDoc(foldersTree$parent,gLnk(\"R\",\"$name\",\"pages/$link.html\"));\n"; 1334 } 1335 } 1336 return $code; 1337 } 1338 public function traverse2($tree) 1339 { 1340 $pages = []; 1341 foreach ($tree as $name => $node) { 1342 list($name,$link) = explode('|', $name); 1343 if (is_array($node)) { 1344 if (isset($name) && isset($link)) { 1345 $pageName = $link; 1346 $pages[] = $pageName; 1347 } 1348 $pages2 = $this->traverse2($node); 1349 foreach ($pages2 as $elem) { 1350 $pages[] = $elem; 1351 } 1352 } else { 1353 $pages[] = $link; 1354 } 1355 } 1356 return $pages; 1357 } 1358 public function move_to_structure($page_ref_id, $structure_id, $begin = true) 1359 { 1360 $page_info = $this->s_get_page_info($page_ref_id); 1361 $query = "update `tiki_structures` set `pos`=`pos`-1 where `pos`>? and `parent_id`=?"; 1362 $this->query($query, [(int) $page_info["pos"], (int) $page_info["parent_id"]]); 1363 if ($begin) { 1364 $query = "update `tiki_structures` set `pos`=`pos`+1 where `parent_id`=?"; 1365 $this->query($query, [$structure_id]); 1366 $pos = 1; 1367 $query = "update `tiki_structures` set `structure_id`=?, `parent_id`=?, `pos`=? where `page_ref_id`=?"; 1368 $this->query($query, [$structure_id, $structure_id, $pos + 1, $page_ref_id]); 1369 } else { 1370 $query = "select max(`pos`) from `tiki_structures` where `parent_id`=?"; 1371 $pos = $this->getOne($query, [$structure_id]); 1372 $query = "update `tiki_structures` set `structure_id`=?, `parent_id`=?, `pos`=? where `page_ref_id`=?"; 1373 $this->query($query, [$structure_id, $structure_id, $pos + 1, $page_ref_id]); 1374 } 1375 } 1376 1377 /* transform a structure into a menu */ 1378 public function to_menu($channels, $structure, $sectionLevel = 0, $cumul = 0, $params = []) 1379 { 1380 $smarty = TikiLib::lib('smarty'); 1381 include_once('lib/smarty_tiki/function.sefurl.php'); 1382 $options = []; 1383 $cant = 0; 1384 if (empty($channels)) { 1385 return ['cant' => 0, 'data' => []]; 1386 } 1387 foreach ($channels as $channel) { 1388 if (empty($channel['sub'])) { 1389 if (isset($options[$cant - 1]['sectionLevel'])) { 1390 $level = $options[$cant - 1]['sectionLevel']; 1391 while ($level-- > $sectionLevel) { 1392 $options[] = ['type' => '-', 'sectionLevel' => $level]; 1393 ++$cant; 1394 } 1395 } 1396 } 1397 $pageName = $channel['pageName']; 1398 if (isset($params['show_namespace']) && $params['show_namespace'] === 'n') { 1399 $pageName = ! empty($channel['short_pageName']) ? $channel['short_pageName'] : $channel['pageName']; 1400 } 1401 $option['name'] = empty($channel['page_alias']) ? $pageName : $channel['page_alias']; 1402 $option['type'] = empty($channel['sub']) ? 'o' : ($sectionLevel ? $sectionLevel : 's'); 1403 $option['url'] = smarty_function_sefurl(['page' => $channel['pageName'], 'structure' => $structure, 'page_ref_id' => $channel['page_ref_id'], 'sefurl' => 'n'], $smarty->getEmptyInternalTemplate()); 1404 $option['canonic'] = '((' . $channel['pageName'] . '))'; 1405 $option['sefurl'] = smarty_function_sefurl(['page' => $channel['pageName'], 'structure' => $structure, 'page_ref_id' => $channel['page_ref_id']], $smarty->getEmptyInternalTemplate()); 1406 $option['position'] = $cant + $cumul; 1407 $option['sectionLevel'] = $sectionLevel; 1408 1409 $option['url'] = str_replace('&', '&', $option['url']); // as of Tiki 7 menu items get encoded later 1410 $option['sefurl'] = str_replace('&', '&', $option['sefurl']); 1411 $option['optionId'] = $channel['page_ref_id']; 1412 1413 ++$cant; 1414 $options[] = $option; 1415 if (! empty($channel['sub'])) { 1416 $oSub = $this->to_menu($channel['sub'], $structure, $sectionLevel + 1, $cant + $cumul, $params); 1417 $cant += $oSub['cant']; 1418 $options = array_merge($options, $oSub['data']); 1419 } 1420 } 1421 return ['data' => $options, 'cant' => $cant]; 1422 } 1423} 1424