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} 13 14/** 15 * 16 */ 17class ModLib extends TikiLib 18{ 19 20 public $pref_errors = []; 21 22 // additional module zones added to this array will be exposed to tiki.tpl 23 // TODO change modules user interface to enable additional zones 24 public $module_zones = [ 25 'top' => 'top_modules', 26 'topbar' => 'topbar_modules', 27 'pagetop' => 'pagetop_modules', 28 'left' => 'left_modules', 29 'right' => 'right_modules', 30 'pagebottom' => 'pagebottom_modules', 31 'bottom' => 'bottom_modules', 32 ]; 33 34 public $cssfiles = [ 35 'calendar_new' => [ 36 'csspath' => 'themes/base_files/feature_css/calendar.css', 37 'rank' => 20, 38 ], 39 'action_calendar' => [ 40 'csspath' => 'themes/base_files/feature_css/calendar.css', 41 'rank' => 20, 42 ], 43 ]; 44 45 function __construct() 46 { 47 global $prefs; 48 49 if (! empty($prefs['module_zone_available_extra'])) { 50 foreach (array_filter((array) $prefs['module_zone_available_extra']) as $name) { 51 $this->module_zones[$name] = $name . '_modules'; 52 } 53 } 54 } 55 56 /** 57 * @param $name 58 * @param $title 59 * @param $data 60 * @param null $parse 61 * 62 * @return TikiDb_Pdo_Result 63 * @throws Exception 64 */ 65 function replace_user_module($name, $title, $data, $parse = null) 66 { 67 global $prefs; 68 69 if ((! empty($name)) && (! empty($data))) { 70 $query = "delete from `tiki_user_modules` where `name`=?"; 71 $this->query($query, [$name], -1, -1, false); 72 $query = "insert into `tiki_user_modules`(`name`,`title`,`data`, `parse`) values(?,?,?,?)"; 73 $result = $this->query($query, [$name,$title,$data,$parse]); 74 75 $cachelib = TikiLib::lib('cache'); 76 $cachelib->invalidate("user_modules_$name"); 77 78 $wikilib = TikiLib::lib('wiki'); // used to require lib/wiki/wikilib.php where convertToTiki9 lives 79 $converter = new convertToTiki9(); 80 $converter->saveObjectStatus($name, 'tiki_user_modules', 'new9.0+'); 81 82 return $result; 83 } 84 } 85 86 /** 87 * @param int $moduleId 88 * @param $name 89 * @param $title 90 * @param $position 91 * @param $order 92 * @param int $cache_time 93 * @param int $rows 94 * @param null $groups 95 * @param null $params 96 * @param null $type 97 * 98 * @return bool 99 * @throws Exception 100 */ 101 function assign_module($moduleId = 0, $name, $title, $position, $order, $cache_time = 0, $rows = 10, $groups = null, $params = null, $type = null) 102 { 103 //check for valid values 104 $cache_time = is_numeric($cache_time) ? $cache_time : 0; 105 $rows = is_numeric($rows) ? $rows : 10; 106 107 if (is_array($params)) { 108 $params = $this->serializeParameters($name, $params); 109 } 110 111 if ($moduleId) { 112 $query = "update `tiki_modules` set `name`=?,`title`=?,`position`=?,`ord`=?,`cache_time`=?,`rows`=?,`groups`=?,`params`=?,`type`=? where `moduleId`=?"; 113 $result = $this->query($query, [$name,$title,$position,(int) $order,(int) $cache_time,(int) $rows,$groups,$params,$type, $moduleId]); 114 if (! $result || ! $result->numRows()) { 115 $moduleId = false; 116 } 117 } else { 118 $query = "delete from `tiki_modules` where `name`=? and `position`=? and `ord`=? and `params`=?"; 119 $this->query($query, [$name, $position, (int)$order, $params]); 120 $query = "insert into `tiki_modules`(`name`,`title`,`position`,`ord`,`cache_time`,`rows`,`groups`,`params`,`type`) values(?,?,?,?,?,?,?,?,?)"; 121 $this->query($query, [$name,$title,$position,(int) $order,(int) $cache_time,(int) $rows,$groups,$params,$type]); 122 $moduleId = $this->lastInsertId(); //to return the recently created module 123 if ($type == "D" || $type == "P") { 124 $query = 'select `moduleId` from `tiki_modules` where `name`=? and `title`=? and `position`=? and `ord`=? and `cache_time`=? and `rows`=? and `groups`=? and `params`=? and `type`=?'; 125 $moduleId = $this->getOne($query, [$name,$title,$position,(int) $order,(int) $cache_time,(int) $rows,$groups,$params,$type]); 126 } 127 } 128 if ($type == "D" || $type == "P") { 129 $usermoduleslib = TikiLib::lib('usermodules'); 130 $usermoduleslib->add_module_users($moduleId, $name, $title, $position, $order, $cache_time, $rows, $groups, $params, $type); 131 } 132 return $moduleId; 133 } 134 135 /* Returns the requested module assignation. A module assignation is represented by an array similar to a tiki_modules record. The groups field is unserialized in the module_groups key, a spaces-separated list of groups. */ 136 /** 137 * @param $moduleId 138 * @return mixed 139 */ 140 function get_assigned_module($moduleId) 141 { 142 $query = "select * from `tiki_modules` where `moduleId`=?"; 143 $result = $this->query($query, [$moduleId]); 144 $res = $result->fetchRow(); 145 146 if ($res["groups"]) { 147 $grps = unserialize($res["groups"]); 148 if (! empty($grps)) { 149 $res["module_groups"] = implode(' ', $grps); 150 } 151 } 152 153 return $res; 154 } 155 156 /** 157 * @param $moduleId 158 * @return bool 159 */ 160 function unassign_module($moduleId) 161 { 162 $query = "delete from `tiki_modules` where `moduleId`=?"; 163 $result = $this->query($query, [$moduleId]); 164 $query = "delete from `tiki_user_assigned_modules` where `moduleId`=?"; 165 $this->query($query, [$moduleId]); 166 return $result && $result->numRows(); 167 } 168 169 /** 170 * @param $name 171 * @return int 172 */ 173 function get_rows($name) 174 { 175 $query = "select `rows` from `tiki_modules` where `name`=?"; 176 177 $rows = $this->getOne($query, [$name]); 178 179 if ($rows == 0) { 180 $rows = 10; 181 } 182 183 return $rows; 184 } 185 186 /** 187 * @param $moduleId 188 * 189 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result 190 */ 191 function module_up($moduleId) 192 { 193 $query = "update `tiki_modules` set `ord`=`ord`-1 where `moduleId`=?"; 194 return $this->query($query, [$moduleId]); 195 } 196 197 /** 198 * @param $moduleId 199 * 200 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result 201 */ 202 function module_down($moduleId) 203 { 204 $query = "update `tiki_modules` set `ord`=`ord`+1 where `moduleId`=?"; 205 return $this->query($query, [$moduleId]); 206 } 207 208 /** 209 * Sets position of module to left - this method does not appear to be used 210 * 211 * @param $moduleId 212 * 213 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result 214 */ 215 function module_left($moduleId) 216 { 217 $query = "update `tiki_modules` set `position`='left' where `moduleId`=?"; 218 return $this->query($query, [$moduleId]); 219 } 220 221 /** 222 * Sets position of module as right - this method does not appear to be used 223 * 224 * @param $moduleId 225 * 226 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result 227 */ 228 function module_right($moduleId) 229 { 230 $query = "update `tiki_modules` set `position`='right' where `moduleId`=?"; 231 return $this->query($query, [$moduleId]); 232 } 233 234 /** 235 * Reset all module ord's according to supplied array or by displayed order 236 * 237 * @param array $module_order [zone][moduleId] (optional) 238 * 239 * @return int 240 */ 241 function reorder_modules($module_order = []) 242 { 243 global $user; 244 $all_modules = $this->get_modules_for_user($user, $this->module_zones); 245 if (empty($module_order)) { // rewrite module order as displayed 246 foreach ($all_modules as $zone => $contents) { 247 $module_order[$zone] = []; 248 foreach ($contents as $index => $module) { 249 $module_order[$zone][$index] = (int) $module['moduleId']; 250 } 251 } 252 } 253 $section_map = array_flip($this->module_zones); 254 $bindvars = []; 255 $query = 'UPDATE `tiki_modules` SET `ord`=?, `position`=? WHERE `moduleId`=?;'; 256 $i = 0; 257 foreach ($module_order as $zone => $contents) { 258 $section_initial = $section_map[$zone]; 259 foreach ($contents as $index => $moduleId) { 260 if ($moduleId) { 261 if ($all_modules[$zone][$index]['moduleId'] != $moduleId 262 || ($all_modules[$zone][$index]['ord'] != $index + 1 263 || $all_modules[$zone][$index]['position'] != $section_initial)) 264 { 265 $bindvars = [ 266 $index + 1, 267 $section_initial, 268 $moduleId, 269 ]; 270 $result = $this->query($query, $bindvars); 271 if ($result && $result->numRows()) { 272 $i = $i + $result->numRows(); 273 } 274 } 275 } 276 } 277 } 278 return $i; 279 } 280 281 /** 282 * @return array 283 */ 284 function get_all_modules() 285 { 286 $user_modules = $this->list_user_modules(); 287 288 $all_modules = []; 289 290 foreach ($user_modules["data"] as $um) { 291 $all_modules[] = $um["name"]; 292 } 293 294 // Now add all the system modules 295 $h = opendir("templates/modules"); 296 while (($file = readdir($h)) !== false) { 297 if (substr($file, 0, 4) == 'mod-' && preg_match("/\.tpl$/", $file)) { 298 if (! strstr($file, "nocache")) { 299 $name = substr($file, 4, strlen($file) - 8); 300 301 $all_modules[] = $name; 302 } 303 } 304 } 305 closedir($h); 306 return $all_modules; 307 } 308 309 /** 310 * @param $name 311 * 312 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result 313 * @throws Exception 314 */ 315 function remove_user_module($name) 316 { 317 318 $query = "delete from `tiki_modules` where `name`=?"; 319 $this->query($query, [$name]); 320 321 $query = " delete from `tiki_user_modules` where `name`=?"; 322 $result = $this->query($query, [$name]); 323 324 $cachelib = TikiLib::lib('cache'); 325 $cachelib->invalidate('user_modules'); 326 327 return $result; 328 } 329 330 /** 331 * @param string $sort_mode 332 * @return array 333 */ 334 function list_user_modules($sort_mode = 'name_asc') 335 { 336 $query = "select * from `tiki_user_modules` order by " . $this->convertSortMode($sort_mode); 337 338 $result = $this->query($query, []); 339 $query_cant = "select count(*) from `tiki_user_modules`"; 340 $cant = $this->getOne($query_cant, []); 341 $ret = []; 342 343 while ($res = $result->fetchRow()) { 344 $ret[] = $res; 345 } 346 347 $retval = []; 348 $retval["data"] = $ret; 349 $retval["cant"] = $cant; 350 return $retval; 351 } 352 353 /** 354 * @return int 355 */ 356 function clear_cache() 357 { 358 global $tikidomain; 359 $dircache = "modules/cache"; 360 if ($tikidomain) { 361 $dircache .= "/$tikidomain"; 362 } 363 $h = opendir($dircache); 364 $i = 0; 365 while (($file = readdir($h)) !== false) { 366 if (substr($file, 0, 3) == 'mod') { 367 $file = "$dircache/$file"; 368 $result = unlink($file); 369 if ($result) { 370 $i++; 371 } 372 } 373 } 374 closedir($h); 375 return $i; 376 } 377 /* @param module_info = info of a module 378 * @param user_groups = list of groups of a user 379 * @param user = the user 380 * @return string 'y' = ok, 'n' = not ok 381 */ 382 function check_groups($module_info, $user, $user_groups) 383 { 384 global $prefs, $tiki_p_admin; 385 if (empty($user)) { 386 $user_groups = [ 'Anonymous' ]; 387 } 388 $pass = 'y'; 389 if ($tiki_p_admin == 'y' && $prefs['modhideanonadmin'] == 'y' && $module_info['groups'] == serialize(['Anonymous']) && 390 strpos($_SERVER["SCRIPT_NAME"], 'tiki-admin_modules.php') === false) { 391 $pass = 'n'; 392 } elseif ($tiki_p_admin != 'y' && $prefs['modallgroups'] != 'y') { 393 if ($module_info['groups']) { 394 if (! is_array($module_info['groups'])) { 395 $module_groups = unserialize($module_info['groups']); 396 } else { 397 $module_groups = $module_info['groups']; 398 } 399 } else { 400 $module_groups = []; 401 } 402 if (! empty($module_groups)) { // if no groups are set show to all users (modules revamp [MOD] in Tiki 7) 403 $pass = 'n'; 404 if ($prefs['modseparateanon'] !== 'y') { 405 foreach ($module_groups as $mod_group) { 406 if (in_array($mod_group, $user_groups)) { 407 $pass = 'y'; 408 break; 409 } 410 } 411 } else { 412 if (! $user) { 413 if (in_array('Anonymous', $module_groups)) { 414 $pass = 'y'; 415 } 416 } else { 417 foreach ($module_groups as $mod_group) { 418 if ($mod_group === 'Anonymous') { 419 continue; 420 } 421 if (in_array($mod_group, $user_groups)) { 422 $pass = 'y'; 423 break; 424 } 425 } 426 } 427 } 428 } 429 } 430 return $pass; 431 } 432 433 /** 434 * @param $user 435 * @param array $module_zones 436 * @return array 437 */ 438 function get_modules_for_user($user, array $module_zones = []) 439 { 440 if (empty($module_zones)) { 441 $module_zones = $this->module_zones; 442 } 443 $list = $this->get_raw_module_list_for_user($user, $module_zones); 444 445 foreach ($list as & $partial) { 446 $partial = array_map([ $this, 'augment_module_parameters' ], $partial); 447 if (! $this->is_admin_mode(true)) { 448 $partial = array_values(array_filter($partial, [ $this, 'filter_active_module' ])); 449 } 450 } 451 452 return $list; 453 } 454 455 /** 456 * @param $module 457 * @return mixed 458 */ 459 function augment_module_parameters($module) 460 { 461 global $prefs; 462 463 parse_str($module['params'], $module_params); 464 $default_params = [ 465 'decorations' => 'y', 466 'overflow' => 'n', 467 'nobox' => 'n', 468 'notitle' => 'n', 469 'error' => '', 470 'flip' => ( $prefs['user_flip_modules'] == 'module' ) ? 'n' : $prefs['user_flip_modules'], 471 ]; 472 473 if (! is_array($module_params)) { 474 $module_params = []; 475 } 476 477 $module_params = array_merge($default_params, $module_params); 478 479 $module_params['module_position'] = $module['position']; 480 $module_params['module_ord'] = $module['ord']; 481 482 if ($module['name'] == 'package' && ! empty($module_params['otherparams'])) { 483 parse_str($module_params['otherparams'], $other_params); 484 if (is_array($other_params)) { 485 $module_params = $module_params + $other_params; 486 } 487 } 488 489 if ($prefs['user_flip_modules'] === 'n') { 490 $module_params['flip'] = 'n'; 491 } 492 493 if (isset($module_params['section']) && $module_params['section'] == 'wiki') { 494 $module_params['section'] = 'wiki page'; 495 } 496 497 $module['params'] = $module_params; 498 499 return $module; 500 } 501 502 /** 503 * @param $module 504 * @return bool 505 */ 506 function filter_active_module($module) 507 { 508 global $section, $page, $prefs, $user; 509 $tikilib = TikiLib::lib('tiki'); 510 // Validate preferences 511 $module_info = $this->get_module_info($module['name']); 512 $params = $module['params']; 513 514 if ($prefs['feature_perspective'] == 'y') { 515 $perspectivelib = TikiLib::lib('perspective'); 516 $persp = $perspectivelib->get_current_perspective($prefs); 517 if (empty($persp)) { 518 $persp = 0; 519 } 520 if (isset($params['perspective']) && ! in_array($persp, (array) $params['perspective'])) { 521 return false; 522 } 523 } 524 525 if (isset($params["lang"]) && ! in_array($prefs['language'], (array) $params["lang"])) { 526 return false; 527 } 528 529 if (isset($params['section']) && ( ! isset($section) || ! in_array($section, (array) $params['section']))) { 530 return false; 531 } 532 533 if (isset($params['nopage']) && isset($page) && isset($section) && $section == 'wiki page') { 534 if (in_array($page, (array) $params['nopage'])) { 535 return false; 536 } 537 } 538 539 if (isset($params['page'])) { 540 if (! isset($section) || $section != 'wiki page' || ! isset($page)) { // must be in a page 541 return false; 542 } elseif (! in_array($page, (array) $params['page'])) { 543 return false; 544 } 545 } 546 547 if (isset($params['theme'])) { 548 global $tc_theme; 549 550 $ok = false; 551 foreach ((array) $params['theme'] as $t) { 552 // remove any css extension 553 $t = preg_replace('/\.css$/i', '', $t); 554 if ($t{0} != '!') { // usual behavior 555 if (! empty($tc_theme) && $t === $tc_theme) { 556 $ok = true; 557 } elseif ($t === $prefs['theme'] && empty($tc_theme)) { 558 $ok = true; 559 } 560 } else { // negation behavior 561 $excluded_theme = substr($t, 1); 562 $ok = true; 563 if (! empty($tc_theme) && $excluded_theme === $tc_theme) { 564 return false; 565 } elseif ($excluded_theme === $prefs['theme'] && empty($tc_theme)) { 566 return false; 567 } 568 } 569 } 570 if (! $ok) { 571 return false; 572 } 573 } 574 575 if (! Perms::get()->admin) { 576 $user_groups = Perms::get()->getGroups(); 577 } else { 578 $user_groups = []; 579 } 580 581 if ('y' != $this->check_groups($module, $user, $user_groups)) { 582 return false; 583 } 584 585 if (isset($params['creator']) && $section == 'wiki page' && isset($page)) { 586 if (! $page_info = $tikilib->get_page_info($page)) { 587 return false; 588 } elseif ($params['creator'] == 'y' && $page_info['creator'] != $user) { 589 return false; 590 } elseif ($params['creator'] == 'n' && $page_info['creator'] == $user) { 591 return false; 592 } 593 } 594 595 if (isset($params['contributor']) && $section == 'wiki page' && isset($page)) { 596 if (! $page_info = $tikilib->get_page_info($page)) { 597 return false; 598 } else { 599 $wikilib = TikiLib::lib('wiki'); 600 $contributors = $wikilib->get_contributors($page); 601 $contributors[] = $page_info['creator']; 602 $in = in_array($user, $contributors); 603 604 if ($params['contributor'] == 'y' && ! $in) { 605 return false; 606 } elseif ($params['contributor'] == 'n' && $in) { 607 return false; 608 } 609 } 610 } 611 612 if ($module['name'] == 'login_box' && (basename($_SERVER['SCRIPT_NAME']) == 'tiki-login_scr.php' || basename($_SERVER['SCRIPT_NAME']) == 'tiki-login_openid.php')) { 613 return false; 614 } 615 616 if ($prefs['feature_categories'] == 'y') { 617 if ($this->is_hidden_by_category($params)) { 618 return false; 619 } 620 621 if ($this->is_hidden_by_no_category($params)) { 622 return false; 623 } 624 } 625 626 if ($prefs['cookie_consent_feature'] == 'y' && $prefs['cookie_consent_disable'] !== 'y') { // check if consent required to show 627 if (! empty($params['cookie_consent']) && $params['cookie_consent'] === 'y') { 628 global $feature_no_cookie; 629 if ($feature_no_cookie) { 630 return false; 631 } 632 } 633 } 634 635 foreach ($module_info['prefs'] as $p) { 636 if ($prefs[$p] != 'y') { 637 $this->add_pref_error($module['name'], $p); 638 return false; 639 } 640 } 641 642 return true; 643 } 644 645 /** 646 * @param $params 647 * @return bool 648 */ 649 private function is_hidden_by_category($params) 650 { 651 global $cat_type, $cat_objid; 652 if (empty($params['category'])) { 653 return false; 654 } 655 656 if (empty($cat_type) || empty($cat_objid)) { 657 return true; 658 } 659 660 $catIds = TikiLib::lib('categ')->get_object_categories($cat_type, $cat_objid); 661 662 if (empty($catIds)) { 663 return true; 664 } 665 666 // Multi-value params of custom modules need transformation into an array 667 if (is_array($params['category'])) { 668 $categories = (array) $params['category']; 669 } else { 670 $categories = explode(';', $params['category']); 671 } 672 673 return ! $this->matches_any_in_category_list($categories, $catIds, ! empty($params['subtree'])); 674 } 675 676 /** 677 * @param $params 678 * @return bool 679 */ 680 private function is_hidden_by_no_category($params) 681 { 682 global $cat_type, $cat_objid; 683 if (empty($params['nocategory'])) { 684 return false; 685 } 686 687 if (empty($cat_type) || empty($cat_objid)) { 688 return false; 689 } 690 691 $catIds = TikiLib::lib('categ')->get_object_categories($cat_type, $cat_objid); 692 693 if (empty($catIds)) { 694 return false; 695 } 696 697 // Multi-value params of custom modules need transformation into an array 698 if (is_array($params['nocategory'])) { 699 $categories = (array) $params['nocategory']; 700 } else { 701 $categories = explode(';', $params['nocategory']); 702 } 703 704 return $this->matches_any_in_category_list($categories, $catIds, ! empty($params['subtree'])); 705 } 706 707 /** 708 * @param $desiredList 709 * @param $categoryList 710 * @param bool $deep 711 * @return bool 712 */ 713 private function matches_any_in_category_list($desiredList, $categoryList, $deep = false) 714 { 715 if (empty($categoryList)) { 716 return false; 717 } 718 719 $allcats = null; // only needed if category names are used 720 721 foreach ($desiredList as $category) { 722 if (is_numeric($category)) { 723 if (in_array($category, $categoryList)) { 724 return true; 725 } 726 } else { 727 if (! $allcats) { 728 // gets all categories (cached but perms are checked so only load all if necessary) 729 $allcats = TikiLib::lib('categ')->getCategories(); 730 } 731 foreach ($categoryList as $id) { 732 if (isset($allcats[$id]) && $allcats[$id]['name'] == $category) { 733 return true; 734 } 735 } 736 } 737 } 738 739 if ($deep) { 740 $nextList = []; 741 foreach ($categoryList as $id) { 742 if (isset($allcats[$id]) && $allcats[$id]['parentId']) { 743 $nextList[] = $allcats[$id]['parentId']; 744 } 745 } 746 747 return $this->matches_any_in_category_list($desiredList, $nextList, $deep); 748 } 749 750 return false; 751 } 752 753 /** 754 * @param $user 755 * @param array $module_zones 756 * 757 * @return array 758 * @throws Exception 759 */ 760 private function get_raw_module_list_for_user($user, array $module_zones) 761 { 762 global $prefs; 763 $usermoduleslib = TikiLib::lib('usermodules'); 764 765 $out = array_fill_keys(array_values($module_zones), []); 766 767 if (! empty($prefs['module_file'])) { 768 $out = array_merge($out, $this->read_module_file($prefs['module_file'])); 769 } elseif ($prefs['user_assigned_modules'] == 'y' 770 //need to use Perms class instead of $tiki_p_configure_modules global as the global is null 771 //for some reason when feature_modulecontrols is not set 772 && Perms::get()->configure_modules 773 && $user 774 && $usermoduleslib->user_has_assigned_modules($user) ) { 775 foreach ($module_zones as $zone => $zone_name) { 776 $out[$zone_name] = $usermoduleslib->get_assigned_modules_user($user, $zone); 777 } 778 } else { 779 $modules_by_position = $this->get_assigned_modules(null, 'y'); 780 foreach ($module_zones as $zone => $zone_name) { 781 if (isset($modules_by_position[$zone])) { 782 $out[$zone_name] = $modules_by_position[$zone]; 783 } 784 } 785 } 786 787 return $out; 788 } 789 790 /** 791 * @return array 792 */ 793 function list_module_files() 794 { 795 $files = []; 796 if (is_dir('modules')) { 797 if ($dh = opendir('modules')) { 798 while (($file = readdir($dh)) !== false) { 799 if (preg_match("/^mod-func-.*\.php$/", $file)) { 800 array_push($files, $file); 801 } 802 } 803 closedir($dh); 804 } 805 } 806 sort($files); 807 return $files; 808 } 809 810 /** 811 * @param $module 812 * @return array|mixed 813 */ 814 function get_module_info($module) 815 { 816 if (is_array($module)) { 817 $moduleName = $module['name']; 818 } else { 819 $moduleName = $module; 820 } 821 822 global $prefs; 823 824 $cachelib = TikiLib::lib('cache'); 825 $cacheKey = 'module.' . $moduleName . $prefs['language']; 826 $info = $cachelib->getSerialized($cacheKey, 'module'); 827 828 if ($info) { 829 if (! isset($info['cachekeygen'])) { 830 $info['cachekeygen'] = [ $this, 'createDefaultCacheKey' ]; 831 } 832 return $info; 833 } 834 835 $phpfuncfile = 'modules/mod-func-' . $moduleName . '.php'; 836 $info_func = "module_{$moduleName}_info"; 837 $info = []; 838 839 if (file_exists($phpfuncfile)) { 840 include_once $phpfuncfile; 841 842 if (function_exists($info_func)) { 843 $info = $info_func(); 844 if (! empty($info['params'])) { 845 foreach ($info['params'] as &$p) { 846 $p['section'] = 'module'; 847 } 848 } 849 } 850 851 $info['type'] = 'function'; 852 } 853 854 $defaults = [ 855 'name' => $moduleName, 856 'description' => tra('Description not available'), 857 'type' => 'include', 858 'prefs' => [], 859 'params' => [], 860 ]; 861 862 $info = array_merge($defaults, $info); 863 864 $info['params'] = array_merge( 865 $info['params'], 866 [ 867 'title' => [ 868 'name' => tra('Module Title'), 869 'description' => tra('Title to display at the top of the box.'), 870 'filter' => 'striptags', 871 'section' => 'appearance', 872 ], 873 'nobox' => [ 874 'name' => tra('No Box'), 875 'description' => 'y|n ' . tra('Show only the content'), 876 'section' => 'appearance', 877 ], 878 'decorations' => [ 879 'name' => tra('Title, background, etcs'), 880 'description' => 'y|n ' . tra('Show module decorations'), 881 'section' => 'appearance', 882 ], 883 'notitle' => [ 884 'name' => tra('No Title'), 885 'description' => 'y|n ' . tra('Hide module title'), 886 'filter' => 'alpha', 887 'section' => 'appearance', 888 ], 889 'category' => [ 890 'name' => tra('Category'), 891 'description' => tra('Module displayed depending on category. Separate multiple category IDs or names by semi-colons.'), 892 'section' => 'visibility', 893 'separator' => ';', 894 'filter' => 'alnum', 895 'profile_reference' => 'category', 896 ], 897 'nocategory' => [ 898 'name' => tra('No Category'), 899 'description' => tra('Module is hidden depending on category. Separate multiple category IDs or names by semi-colons. This takes precedence over the category parameter above.'), 900 'section' => 'visibility', 901 'separator' => ';', 902 'filter' => 'alnum', 903 'profile_reference' => 'category', 904 ], 905 'subtree' => [ 906 'name' => tra('Category subtrees'), 907 'description' => tra('Consider child categories of the categories listed in "category" and "no category" to be part of those categories. (0 or 1)'), 908 'section' => 'visibility', 909 'filter' => 'int', 910 ], 911 'perspective' => [ 912 'name' => tra('Perspective'), 913 'description' => tra('Module is displayed only in the listed perspective ID(s). Separate multiple perspective IDs by semi-colons.'), 914 'separator' => ';', 915 'filter' => 'digits', 916 'section' => 'visibility', 917 'profile_reference' => 'perspective', 918 ], 919 'lang' => [ 920 'name' => tra('Language'), 921 'description' => tra('Module is displayed only when the specified language(s) in use. Designate languages by two-character language codes. Separate multiple languages by semi-colons.'), 922 'separator' => ';', 923 'filter' => 'lang', 924 'section' => 'visibility', 925 ], 926 'section' => [ 927 'name' => tra('Section'), 928 'description' => tra('Module is displayed only in the specified sections. Separate multiple sections by semi-colons. Choose from: blogs; calendar; categories; cms (for "articles"); contacts; directory; faqs; featured_links; file_galleries; forums; galleries (for "image galleries"); gmaps; html_pages; maps; mytiki; newsletters; poll; quizzes; surveys; trackers; user_messages; webmail; wiki page'), 929 'separator' => ';', 930 'filter' => 'striptags', 931 'section' => 'visibility', 932 ], 933 'page' => [ 934 'name' => tra('Page Filter'), 935 'description' => tra('Module is displayed only on the specified page(s). Separate multiple page names by semi-colons.'), 936 'separator' => ';', 937 'filter' => 'pagename', 938 'section' => 'visibility', 939 'profile_reference' => 'wiki_page', 940 ], 941 'nopage' => [ 942 'name' => tra('No Page'), 943 'description' => tra('Module is not displayed on the specified page(s). Separate multiple page names by semi-colons.'), 944 'separator' => ';', 945 'filter' => 'pagename', 946 'section' => 'visibility', 947 'profile_reference' => 'wiki_page', 948 ], 949 'theme' => [ 950 'name' => tra('Theme'), 951 'description' => tra('Module is displayed or not displayed depending on the theme. (Enter the theme\'s file name, for example, "thenews.css".) Prefix the theme name with "!" for the module to not display. Separate multiple theme names by semi-colons.'), 952 'separator' => ';', 953 'filter' => 'themename', 954 'section' => 'visibility', 955 ], 956 'creator' => [ 957 'name' => tra('Creator'), 958 'description' => tra('Module only available based on the relationship of the user with the wiki page. Either only creators (y) or only non-creators (n) will see the module.'), 959 'filter' => 'alpha', 960 'section' => 'visibility', 961 ], 962 'contributor' => [ 963 'name' => tra('Contributor'), 964 'description' => tra('Module only available based on the relationship of the user with the wiki page. Either only contributors (y) or only non-contributors (n) will see the module.'), 965 'filter' => 'alpha', 966 'section' => 'visibility', 967 ], 968 'flip' => [ 969 'name' => tra('Flip'), 970 'description' => tra('Users can open and close the module.'), 971 'filter' => 'alpha', 972 'section' => 'appearance', 973 ], 974 'style' => [ 975 'name' => tra('Style'), 976 'description' => tra('CSS style attribute (for example, to position the module)'), 977 'section' => 'appearance', 978 ], 979 'class' => [ 980 'name' => tra('Class'), 981 'description' => tra('Extra class (for CSS or JavaScript)'), 982 'section' => 'appearance', 983 ], 984 'topclass' => [ 985 'name' => tra('Containing Class'), 986 'description' => tra('Custom CSS class of div around the module.'), 987 'section' => 'appearance', 988 ], 989 ] 990 ); 991 992 if ($prefs['cookie_consent_feature'] === 'y' && $prefs['cookie_consent_disable'] !== 'y') { 993 $info['params']['cookie_consent'] = [ 994 'name' => tra('Cookie Consent'), 995 'description' => 'n|y ' . tra('Show only if consent to accept cookies has been granted.'), 996 'filter' => 'alpha', 997 'section' => 'visibility', 998 ]; 999 } 1000 1001 // Parameters common to several modules, but not all 1002 $common_params = [ 1003 'nonums' => [ 1004 'name' => tra('No Numbers'), 1005 'description' => tra('If set to "y", the module will not number list items.'), 1006 'section' => 'appearance', 1007 ], 1008 'rows' => [ 1009 'name' => tra('Rows'), 1010 'description' => tra('Number of rows, or items, to display.') . ' ' . tra('Default: 10.'), 1011 'section' => 'appearance', 1012 ] 1013 ]; 1014 1015 if ($info['type'] == 'function') { 1016 foreach ($common_params as $key => $common_param) { 1017 $info['params'][$key] = $common_param; 1018 } 1019 } 1020 1021 // Parameters are not required, unless specified. 1022 if (! empty($info['params'])) { 1023 foreach ($info['params'] as &$param) { 1024 if (! isset($param['required'])) { 1025 $param['required'] = false; 1026 } 1027 } 1028 } 1029 1030 $cachelib->cacheItem($cacheKey, serialize($info), 'module'); 1031 1032 if (! isset($info['cachekeygen'])) { 1033 $info['cachekeygen'] = [ $this, 'createDefaultCacheKey' ]; 1034 } 1035 1036 return $info; 1037 } 1038 1039 /** 1040 * @param $mod_reference 1041 * @return string 1042 */ 1043 function createDefaultCacheKey($mod_reference) 1044 { 1045 global $prefs; 1046 return $mod_reference['moduleId'] . '-' . $mod_reference['name'] . '-' . $prefs['language'] . '-' . 1047 serialize($mod_reference['params']) . (isset($_SESSION['current_perspective']) ? '-p' . $_SESSION['current_perspective'] : ''); 1048 } 1049 1050 /** 1051 * @param $mod_reference 1052 * 1053 * @return bool|mixed|null|string|string[] 1054 * @throws SmartyException 1055 */ 1056 1057 function execute_module($mod_reference) 1058 { 1059 global $prefs, $tiki_p_admin; 1060 $smarty = TikiLib::lib('smarty'); 1061 1062 try { 1063 $defaults = [ 1064 'style' => '', 1065 'nonums' => 'n', 1066 ]; 1067 $module_params = isset($mod_reference['params']) ? (array) $mod_reference['params'] : []; 1068 $module_params = array_merge($defaults, $module_params); // not sure why style doesn't get set sometime but is used in the tpl 1069 1070 $mod_reference = array_merge(['moduleId' => null, 'ord' => 0, 'position' => 0, 'rows' => 10], $mod_reference); 1071 1072 $info = $this->get_module_info($mod_reference); 1073 $cachefile = $this->get_cache_file($mod_reference, $info); 1074 1075 foreach ((array) $info['prefs'] as $preference) { 1076 if ($prefs[$preference] != 'y') { 1077 $smarty->loadPlugin('smarty_block_remarksbox'); 1078 1079 return smarty_block_remarksbox( 1080 [ 1081 'type' => 'warning', 1082 'title' => tr('Failed to execute "%0" module', $mod_reference['name']), 1083 ], 1084 tr('Missing dependencies'), 1085 $smarty, 1086 $repeat 1087 ); 1088 } 1089 } 1090 1091 if (! $cachefile || $this->require_cache_build($mod_reference, $cachefile) || $this->is_admin_mode()) { 1092 if ($this->is_admin_mode()) { 1093 require_once('lib/setup/timer.class.php'); 1094 $timer = new timer('module'); 1095 $timer->start('module'); 1096 } 1097 if ($info['type'] == "function") { // Use the module name as default module title. This can be overriden later. A module can opt-out of this in favor of a dynamic default title set in the TPL using clear_assign in the main module function. It can also be overwritten in the main module function. 1098 $smarty->assign('tpl_module_title', tra($info['name'])); 1099 } 1100 1101 $smarty->assign('nonums', $module_params['nonums']); 1102 1103 if ($info['type'] == 'include') { 1104 $phpfile = 'modules/mod-' . $mod_reference['name'] . '.php'; 1105 1106 if (file_exists($phpfile)) { 1107 include $phpfile; 1108 } 1109 } elseif ($info['type'] == 'function') { 1110 $function = 'module_' . $mod_reference['name']; 1111 $phpfuncfile = 'modules/mod-func-' . $mod_reference['name'] . '.php'; 1112 1113 if (file_exists($phpfuncfile)) { 1114 include_once $phpfuncfile; 1115 } 1116 1117 if (function_exists($function)) { 1118 $function($mod_reference, $module_params); 1119 } 1120 } 1121 1122 $ck = getCookie('mod-' . $mod_reference['name'] . $mod_reference['position'] . $mod_reference['ord'], 'menu', 'o'); 1123 $smarty->assign('module_display', ($prefs['javascript_enabled'] == 'n' || $ck == 'o')); 1124 1125 $smarty->assign_by_ref('module_rows', $mod_reference['rows']); 1126 $smarty->assign_by_ref('module_params', $module_params); // module code can unassign this if it wants to hide params 1127 $smarty->assign('module_ord', $mod_reference['ord']); 1128 $smarty->assign('module_position', $mod_reference['position']); 1129 $smarty->assign('moduleId', $mod_reference['moduleId']); 1130 if (isset($module_params['title'])) { 1131 $smarty->assign('tpl_module_title', tra($module_params['title'])); 1132 } 1133 $smarty->assign('tpl_module_name', $mod_reference['name']); 1134 1135 $tpl_module_style = empty($mod_reference['module_style']) ? '' : $mod_reference['module_style']; 1136 1137 if ($tiki_p_admin == 'y' && $this->is_admin_mode() && (! $this->filter_active_module($mod_reference) || 1138 $prefs['modhideanonadmin'] == 'y' && (empty($mod_reference['groups']) || $mod_reference['groups'] == serialize(['Anonymous'])))) { 1139 $tpl_module_style .= 'opacity: 0.5;'; 1140 } 1141 if (isset($module_params['overflow']) && $module_params['overflow'] === 'y') { 1142 $tpl_module_style .= 'overflow:visible !important;'; 1143 } 1144 $smarty->assign('tpl_module_style', $tpl_module_style); 1145 1146 $template = 'modules/mod-' . $mod_reference['name'] . '.tpl'; 1147 1148 if (file_exists('templates/' . $template)) { 1149 $data = $smarty->fetch($template); 1150 } else { 1151 $data = $this->get_user_module_content($mod_reference['name'], $module_params); 1152 } 1153 $smarty->clear_assign('module_params'); // ensure params not available outside current module 1154 $smarty->clear_assign('tpl_module_title'); 1155 $smarty->clear_assign('tpl_module_name'); 1156 $smarty->clear_assign('tpl_module_style'); 1157 1158 if ($this->is_admin_mode() && $timer) { 1159 $elapsed = round($timer->stop('module'), 3); 1160 $data = preg_replace('/<div /', '<div title="Module Execution Time ' . $elapsed . 's" ', $data, 1); 1161 } 1162 1163 if (! empty($cachefile) && ! $this->is_admin_mode()) { 1164 file_put_contents($cachefile, $data); 1165 } 1166 } else { 1167 $data = file_get_contents($cachefile); 1168 } 1169 1170 return $data; 1171 } catch (Exception $e) { 1172 $smarty->loadPlugin('smarty_block_remarksbox'); 1173 if ($tiki_p_admin == 'y') { 1174 $message = $e->getMessage(); 1175 } else { 1176 $message = tr('Contact the system administrator'); 1177 } 1178 $repeat = false; 1179 return smarty_block_remarksbox( 1180 [ 1181 'type' => 'warning', 1182 'title' => tr('Failed to execute "%0" module', $mod_reference['name']), 1183 ], 1184 html_entity_decode($message), 1185 $smarty, 1186 $repeat 1187 ); 1188 } 1189 } 1190 1191 /** 1192 * Returns true if on the admin modules page 1193 * 1194 * @param bool $ifShowingHiddenModules - check for $_REQUEST['show_hidden_modules'] as well 1195 * 1196 * @return bool 1197 */ 1198 function is_admin_mode($ifShowingHiddenModules = false) 1199 { 1200 global $tiki_p_admin_modules; 1201 1202 $ok = true; 1203 if ($ifShowingHiddenModules && empty($_REQUEST['show_hidden_modules'])) { 1204 $ok = false; 1205 } 1206 return $ok && $tiki_p_admin_modules === 'y' && 1207 strpos($_SERVER["SCRIPT_NAME"], 'tiki-admin_modules.php') !== false; 1208 } 1209 1210 /** 1211 * @param $name 1212 * @param $module_params 1213 * @return mixed 1214 */ 1215 function get_user_module_content($name, $module_params) 1216 { 1217 $smarty = TikiLib::lib('smarty'); 1218 $tikilib = TikiLib::lib('tiki'); 1219 $smarty->assign('module_type', 'module'); 1220 $info = $this->get_user_module($name); 1221 if (! empty($info)) { 1222 // test if we have a menu 1223 if (strpos($info['data'], '{menu ') === 0 and strpos($info['data'], "css=n") === false) { 1224 $smarty->assign('module_type', 'cssmenu'); 1225 } 1226 1227 $info = $this->parse($info); 1228 1229 // re-assign module_params for the custom module in case a module plugin is used inside it 1230 $smarty->assign_by_ref('module_params', $module_params); 1231 $smarty->assign('user_title', tra($info['title'])); 1232 $smarty->assign_by_ref('user_data', $info['data']); 1233 $smarty->assign_by_ref('user_module_name', $info['name']); 1234 1235 return $smarty->fetch('modules/user_module.tpl'); 1236 } 1237 } 1238 1239 /** 1240 * Parses custom module content if the module requires 1241 * 1242 * @param $info 1243 * @return mixed 1244 */ 1245 function parse($info) 1246 { 1247 if (isset($info['parse']) && $info['parse'] == 'y') { 1248 $parserlib = TikiLib::lib('parser'); 1249 $info['data'] = $parserlib->parse_data($info['data'], [ 1250 'is_html' => true, 1251 'suppress_icons' => true, 1252 'typography' => false, // typography feature breaks quotes and causes smarty compiler errors, so disable it for custom modules 1253 ]); 1254 $info['title'] = $parserlib->parse_data($info['title'], [ 1255 'noparseplugins' => true, 1256 'is_html' => true, 1257 ]); 1258 } 1259 1260 return $info; 1261 } 1262 1263 /** 1264 * @param $mod_reference 1265 * @param $info 1266 * @return null|string 1267 */ 1268 function get_cache_file($mod_reference, $info) 1269 { 1270 global $tikidomain, $user; 1271 $nocache = 'templates/modules/mod-' . $mod_reference["name"] . '.tpl.nocache'; 1272 1273 // Uncacheable 1274 if (! empty($user) || $mod_reference['cache_time'] <= 0 || file_exists($nocache)) { 1275 return null; 1276 } 1277 1278 $cb = $info['cachekeygen']; 1279 1280 $cachefile = 'modules/cache/'; 1281 if ($tikidomain) { 1282 $cachefile .= "$tikidomain/"; 1283 } 1284 1285 $cachefile .= 'mod-' . md5(call_user_func($cb, $mod_reference)); 1286 1287 return $cachefile; 1288 } 1289 1290 // Returns whether $cachefile needs to be [re]built 1291 /** 1292 * @param $mod_reference 1293 * @param $cachefile 1294 * @return bool 1295 */ 1296 function require_cache_build($mod_reference, $cachefile) 1297 { 1298 $tikilib = TikiLib::lib('tiki'); 1299 return ! file_exists($cachefile) 1300 || ( $tikilib->now - filemtime($cachefile) ) >= $mod_reference['cache_time']; 1301 } 1302 1303 /** 1304 * @param $input 1305 * @param $params 1306 */ 1307 function dispatchValues($input, & $params) 1308 { 1309 if (is_string($input)) { 1310 parse_str($input, $module_params); 1311 } else { 1312 $module_params = $input; 1313 } 1314 1315 foreach ($params as $name => & $inner) { 1316 if (isset($module_params[$name])) { 1317 if (isset($inner['separator'])) { 1318 $inner['value'] = implode($inner['separator'], (array) $module_params[$name]); 1319 } else { 1320 $inner['value'] = $module_params[$name]; 1321 } 1322 } else { 1323 $inner['value'] = null; 1324 } 1325 } 1326 // resort params into sections 1327 $reorderedparams = []; 1328 foreach ($params as $k => $p) { 1329 if (! isset($reorderedparams[$p['section']])) { 1330 $reorderedparams[$p['section']] = []; 1331 } 1332 $reorderedparams[$p['section']][$k] = $p; 1333 } 1334 $params = $reorderedparams; 1335 } 1336 1337 /** 1338 * @param $name 1339 * @param $params 1340 * @return string 1341 */ 1342 function serializeParameters($name, $params) 1343 { 1344 $info = $this->get_module_info($name); 1345 $expanded = []; 1346 1347 foreach ($info['params'] as $name => $def) { 1348 if (isset($def['filter'])) { 1349 $filter = TikiFilter::get($def['filter']); 1350 } else { 1351 $filter = null; 1352 } 1353 1354 if (isset($params[$name]) && $params[$name] !== '') { 1355 if (isset($def['separator']) && strpos($params[$name], $def['separator']) !== false) { 1356 $parts = explode($def['separator'], $params[$name]); 1357 1358 if ($filter) { 1359 foreach ($parts as & $single) { 1360 $single = $filter->filter($single); 1361 $single = trim($single); 1362 } 1363 } 1364 } else { 1365 $parts = $params[$name]; 1366 if ($filter) { 1367 $parts = $filter->filter($parts); 1368 } 1369 } 1370 1371 $expanded[$name] = $parts; 1372 } 1373 } 1374 if (empty($expanded)) { 1375 return '';// http_build_query return NULL or '' depending on system 1376 } 1377 1378 return http_build_query($expanded, '', '&'); 1379 } 1380 1381 /** 1382 * @param $module_name 1383 * @param $preference_name 1384 */ 1385 function add_pref_error($module_name, $preference_name) 1386 { 1387 $this->pref_errors[] = ['mod_name' => $module_name, 'pref_name' => $preference_name]; 1388 } 1389 1390 1391 /* Returns all module assignations for a certain position, or all positions (by default). A module assignation 1392 is represented by an array similar to a tiki_modules record. The groups field is unserialized in the module_groups key, a spaces-separated list of groups. 1393 If asking for a specific position, returns an array of module assignations. If not, returns an array of arrays of modules assignations indexed by positions. For example: array("l" -> array("module assignation")) 1394 TODO: Document $displayed's effect */ 1395 /** 1396 * @param null $position 1397 * @param string $displayed 1398 * @return array 1399 */ 1400 function get_assigned_modules($position = null, $displayed = "n") 1401 { 1402 1403 $filter = ''; 1404 $bindvars = []; 1405 1406 if ($position !== null) { 1407 $filter .= 'where `position`=?'; 1408 $bindvars[] = $position; 1409 } 1410 1411 if ($displayed != 'n') { 1412 $filter .= ( $filter == '' ? 'where' : 'and' ) . " (`type` is null or `type` != ?)"; 1413 $bindvars[] = 'y'; 1414 } 1415 1416 $query = "select * from `tiki_modules` $filter order by " . $this->convertSortMode("ord_asc"); 1417 1418 $result = $this->fetchAll($query, $bindvars); 1419 1420 $ret = []; 1421 foreach ($result as $res) { 1422 if ($res["groups"] && strlen($res["groups"]) > 1) { 1423 $grps = @unserialize($res["groups"]); 1424 1425 $res["module_groups"] = ''; 1426 if (is_array($grps)) { 1427 foreach ($grps as $grp) { 1428 $res["module_groups"] .= " $grp "; 1429 } 1430 } 1431 } else { 1432 $res["module_groups"] = ' '; 1433 } 1434 if ($position === null) { 1435 if (! isset($ret[$res['position']])) { 1436 $ret[$res['position']] = []; 1437 } 1438 $ret[$res['position']][] = $res; 1439 } else { 1440 $ret[] = $res; 1441 } 1442 } 1443 return $ret; 1444 } 1445 1446 /** 1447 * @param $name 1448 * @return mixed 1449 */ 1450 function is_user_module($name) 1451 { 1452 return $this->table('tiki_user_modules')->fetchCount(['name' => $name]); 1453 } 1454 1455 /** 1456 * @param $name 1457 * @return mixed 1458 */ 1459 function get_user_module($name) 1460 { 1461 return $this->table('tiki_user_modules')->fetchFullRow(['name' => $name]); 1462 } 1463 1464 /** 1465 * @global TikiLib $tikilib 1466 * @param bool $added shows current prefs not in defaults 1467 * @return array (prefname => array( 'current' => current value, 'default' => default value )) 1468 */ 1469 function getModulesForExport() 1470 { 1471 $export = []; 1472 $assigned_modules = $this->get_assigned_modules(); 1473 1474 foreach ($assigned_modules as $zone => $modules) { 1475 foreach ($modules as $pos => $module) { 1476 $modtogo['type'] = 'module'; 1477 $modtogo['data'] = []; 1478 1479 $modtogo['data']['name'] = $module['name']; 1480 parse_str($module['params'], $modtogo['data']['params']); 1481 $modtogo['data']['groups'] = unserialize($module['groups']); 1482 $modtogo['data']['order'] = $module['ord']; 1483 1484 $modtogo['data']['position'] = str_replace('_modules', '', $this->module_zones[$module['position']]); 1485 1486 if ($this->is_user_module($module['name'])) { 1487 $um = $this->get_user_module($module['name']); 1488 if (preg_match("/^\!*\{.*\}$/", trim($um['data']), $matches)) { // start and end with { and } makes yaml parser think it's a serialized value 1489 $um['data'] = $um['data'] . "\n"; // so force it to be a literal block 1490 } 1491 $modtogo['data']['custom'] = $um['data']; // the yaml dumper copes with linefeeds etc as a literal block 1492 $modtogo['data']['parse'] = empty($um['parse']) ? 'n' : $um['parse']; 1493 } 1494 1495 $export[] = $modtogo; 1496 } 1497 } 1498 return $export; 1499 } 1500 1501 /** 1502 * @param $filename 1503 * @return array|mixed 1504 */ 1505 private function read_module_file($filename) 1506 { 1507 $cachelib = TikiLib::lib('cache'); 1508 1509 $expiry = filemtime($filename); 1510 if ($modules = $cachelib->getSerialized($filename, 'modules', $expiry)) { 1511 return $modules; 1512 } 1513 1514 $content = file_get_contents($filename); 1515 if (! $content) { 1516 Feedback::error(tr('Module file "%0" not found.', $filename)); 1517 return ''; 1518 } 1519 1520 $profile = Tiki_Profile::fromString("{CODE(caption=>YAML)}$content{CODE}"); 1521 1522 $out = array_fill_keys(array_values($this->module_zones), []); 1523 foreach ($profile->getObjects() as $object) { 1524 if ($object->getType() == 'module') { 1525 $handler = new Tiki_Profile_InstallHandler_Module($object, []); 1526 1527 $data = $handler->getData(); 1528 $object->replaceReferences($data); 1529 $data = $handler->formatData($data); 1530 1531 $data['groups'] = unserialize($data['groups']); 1532 $position = $data['position']; 1533 $zone = $this->module_zones[$position]; 1534 $out[$zone][] = $data; 1535 } 1536 } 1537 1538 $cachelib->cacheItem($filename, serialize($out), 'modules'); 1539 return $out; 1540 } 1541} 1542 1543/** 1544 * Function made available in the template files to behave differently depending on if a zone is empty or not. 1545 */ 1546function zone_is_empty($zoneName) 1547{ 1548 $smarty = TikiLib::lib('smarty'); 1549 $moduleZones = $smarty->getTemplateVars('module_zones'); 1550 1551 $key = $zoneName . '_modules'; 1552 if (empty($moduleZones[$key])) { 1553 return true; 1554 } 1555 1556 foreach ($moduleZones[$key] as $module) { 1557 $data = (string) (isset($module['data']) ? $module['data'] : ''); 1558 if (! empty($data)) { 1559 return false; 1560 } 1561 } 1562 1563 return true; 1564} 1565