| [ Index ] |
PHP Cross Reference of PivotX trunk SVN |
[Summary view] [Print] [Text view]
1 <?php 2 3 // --------------------------------------------------------------------------- 4 // 5 // PIVOTX - LICENSE: 6 // 7 // This file is part of PivotX. PivotX and all its parts are licensed under 8 // the GPL version 2. see: http://docs.pivotx.net/doku.php?id=help_about_gpl 9 // for more information. 10 // 11 // $Id: objects.php 4165 2012-05-08 10:24:08Z hansfn $ 12 // 13 // --------------------------------------------------------------------------- 14 15 16 17 /** 18 * Base Configuration Class 19 * 20 * Handle the loading and saving configuration data. 21 * It contains defaults flows of reading, verifying/fixing and saving of configuration data. 22 * 23 * Description of the load call: 24 * - __construct 25 * - loadConfig 26 * - verifyConfig 27 * - fixConfig 28 * - saveConfig 29 * - organizeConfig 30 * - organizeConfig 31 * - initConfig 32 * 33 * Save call: 34 * - saveConfig 35 * - organizeConfig 36 */ 37 class BaseConfig { 38 39 var $configfile = ''; 40 var $data = array(); 41 var $changed = false; 42 var $upgraded = false; 43 44 /** 45 * Constructor 46 * 47 * @param filename configuration filename 48 * @param db_path path to pivotx db directory, when false we assume config has been loaded 49 */ 50 function __construct($filename, $db_path = false) { 51 if ($db_path === false) { 52 global $PIVOTX; 53 54 $db_path = $PIVOTX['paths']['db_path']; 55 } 56 57 $this->configfile = $db_path . $filename; 58 59 $this->loadConfig(); 60 61 $this->organizeConfig(); 62 63 $this->initConfig(); 64 } 65 66 /** 67 * Set upgraded 68 */ 69 protected function setUpgraded($upgraded=true) { 70 $this->upgraded = $upgraded; 71 } 72 73 /** 74 * Set changed flag 75 */ 76 protected function setChanged($changed=true) { 77 $this->changed = $changed; 78 } 79 80 /** 81 * Load and verify config 82 */ 83 protected function loadConfig() { 84 $this->data = loadSerialize($this->configfile, true); 85 86 if (!$this->verifyConfig()) { 87 $this->fixConfig(); 88 89 $this->saveConfig(true); 90 } 91 } 92 93 /** 94 * Verify configuration (this should be overwritten in subclass) 95 * 96 * @return boolean true if configuration is ok 97 */ 98 protected function verifyConfig() { 99 return true; 100 } 101 102 /** 103 * Fix configuration (this should be overwritten in subclass) 104 * 105 * This is called when verifyConfig() fails. 106 */ 107 protected function fixConfig() { 108 } 109 110 /** 111 * Organize configuration 112 * 113 * @param boolean true, if configuration can be saved 114 * 115 * This is called when just loaded and before configuration is saved. 116 */ 117 protected function organizeConfig() { 118 if (is_array($this->data)) { 119 ksort($this->data); 120 } 121 122 return true; 123 } 124 125 /** 126 * Initialise configuration after has been read, fixed and organized. 127 */ 128 protected function initConfig() { 129 } 130 131 132 /** 133 * Save configuration if 'safe' to do so 134 */ 135 protected function saveConfig($force_changed=false) { 136 if ($force_changed) { 137 $this->setChanged(); 138 } 139 140 if ($this->changed) { 141 $writable = $this->organizeConfig(); 142 143 if ((defined('PIVOTX_INADMIN') || defined('PIVOTX_INAJAXHELPER')) && (is_array($this->data)) && ($writable)) { 144 saveSerialize($this->configfile, $this->data); 145 } 146 } 147 } 148 149 /** 150 * Return configuration-array size 151 */ 152 public function count() { 153 if (!is_array($this->data)) { 154 return 0; 155 } 156 return count($this->data); 157 } 158 159 /** 160 * Print a comprehensible representation of the users 161 * 162 */ 163 function print_r() { 164 echo "<pre>\n"; 165 print_r($this->data); 166 echo "</pre>\n"; 167 } 168 169 /** 170 * Old save version 171 * 172 * This function force a save anyway. 173 */ 174 public function save() 175 { 176 return $this->saveConfig(true); 177 } 178 } 179 180 /** 181 * Takes care of all configuration settings. The configuration is stored in 182 * pivotx/db/ser_config.php, but is completely accessible through this object. 183 * Saving is automagical and only when something has changed. 184 * 185 */ 186 class Config extends BaseConfig { 187 public function __construct($sites_path = '') { 188 $db_path = dirname(__FILE__) . '/' . $sites_path . 'db/'; 189 190 parent::__construct('ser_config.php',$db_path); 191 } 192 193 public function Config($sites_path = '') { 194 $this->__construct($sites_path); 195 } 196 197 protected function verifyConfig() { 198 if ($this->count() < 5) { 199 return false; 200 } 201 202 $default = getDefaultConfig(); 203 foreach($default as $key=>$value) { 204 if (!isset($this->data[$key])) { 205 return false; 206 } 207 } 208 209 if (!isset($this->data['server_spam_key']) || empty($this->data['server_spam_key'])) { 210 return false; 211 } 212 213 return true; 214 } 215 216 protected function fixConfig() { 217 if ($this->count() < 5) { 218 $this->readOld(); 219 220 $this->setChanged(); 221 } 222 223 $default = getDefaultConfig(); 224 foreach($default as $key=>$value) { 225 226 if (!isset($this->data[$key])) { 227 $this->data[$key] = $value; 228 229 $this->setChanged(); 230 } 231 } 232 233 // Seperate check for 'server_spam_key' since it is different for all PivotX install 234 if (!isset($this->data['server_spam_key']) || empty($this->data['server_spam_key'])) { 235 $server_spam_key = ''; 236 $possible_server_keys = array('SERVER_SIGNATURE','SERVER_ADDR','PHP_SELF','DOCUMENT_ROOT'); 237 foreach ($possible_server_keys as $key) { 238 if (isset($_SERVER[$key])) { 239 $server_spam_key .= $_SERVER[$key]; 240 } 241 } 242 $server_spam_key .= time(); 243 $this->data['server_spam_key'] = md5($server_spam_key); 244 245 $this->setChanged(); 246 } 247 248 // If there's a file called 'pivotxdebugmode.txt', we'll enable debugging 249 if (file_exists(dirname(__FILE__)."/pivotxdebugmode.txt")) { 250 $this->data['debug'] = 1; 251 } 252 } 253 254 /** 255 * If the config file is missing, we check if there's a pivot 1.x config 256 * file that we can use. This function does some comversions to get it up 257 * to date, and sets it in $this->data 258 * 259 */ 260 protected function readOld() { 261 global $pivotx_path; 262 263 // If the old config file doesn't exist or it isn't readable, we return false.. 264 if (!file_exists($pivotx_path.'pv_cfg_settings.php') || (!is_readable($pivotx_path.'pv_cfg_settings.php'))) { 265 return false; 266 } 267 268 // Mark this configuration as updated from old PivotX 269 $this->setUpgraded(); 270 271 // get the config file 272 $fh = file($pivotx_path.'pv_cfg_settings.php'); 273 274 foreach ($fh as $fh_this) { 275 @list($name, $val) = explode("!", $fh_this); 276 $Cfg[trim($name)] = trim($val); 277 } 278 //GetUserInfo(); 279 //ExpandSessions(); 280 281 @$Cfg['ping_urls']=str_replace("|", "\n", $Cfg['ping_urls']); 282 @$Cfg['default_introduction']=str_replace("|", "\n", $Cfg['default_introduction']); 283 284 if (!isset($Cfg['selfreg'])) { $Cfg['selfreg']= 0; } 285 if (!isset($Cfg['xmlrpc'])) { $Cfg['xmlrpc']= 0; } 286 if (!isset($Cfg['hashcash'])) { $Cfg['hashcash']= 0; } 287 if (!isset($Cfg['spamquiz'])) { $Cfg['spamquiz']= 0; } 288 if (!isset($Cfg['hardened_trackback'])) { $Cfg['hardened_trackback']= 0; } 289 if (!isset($Cfg['moderate_comments'])) { $Cfg['moderate_comments']= 0; } 290 if (!isset($Cfg['lastcomm_amount_max'])) { $Cfg['lastcomm_amount_max'] = 60; } 291 292 if (!isset($Cfg['tag_cache_timeout'])) { $Cfg['tag_cache_timeout'] = 60; } 293 if (!isset($Cfg['tag_flickr_enabled'])) { $Cfg['tag_flickr_enabled'] = 1; } 294 if (!isset($Cfg['tag_flickr_amount'])) { $Cfg['tag_flickr_amount'] = 6; } 295 if (!isset($Cfg['tag_fetcher_enabled'])) { $Cfg['tag_fetcher_enabled'] = 1; } 296 if (!isset($Cfg['tag_fetcher_amount'])) { $Cfg['tag_fetcher_amount'] = 10; } 297 if (!isset($Cfg['tag_min_font'])) { $Cfg['tag_min_font'] = 9; } 298 if (!isset($Cfg['tag_max_font'])) { $Cfg['tag_max_font'] = 42; } 299 300 if(!isset($Cfg['server_spam_key'])) { 301 $key = $_SERVER['SERVER_SIGNATURE'].$_SERVER['SERVER_ADDR'].$_SERVER['SCRIPT_URI'].$_SERVER['DOCUMENT_ROOT'].time(); 302 $Cfg['server_spam_key'] = md5($key); 303 } 304 305 // Remove stuff we don't need: 306 unset($Cfg['session_length']); 307 unset($Cfg['sessions']); 308 unset($Cfg['users']); 309 unset($Cfg['userfields']); 310 unset($Cfg['<?php']); 311 unset($Cfg['?>']); 312 313 314 foreach ($Cfg as $key => $val) { 315 if ( (strpos($key,'uf-')===0) || (strpos($key,'user-')===0) ) { 316 unset($Cfg[$key]); 317 } 318 } 319 320 $this->data = $Cfg; 321 } 322 323 /** 324 * Return the entire config as a big array.. It's probable better to use 325 * $PIVOTX['config']->get() if you only need one or few items. 326 * 327 * @see $this->get 328 * @return array 329 */ 330 function getConfigArray() { 331 332 return $this->data; 333 334 } 335 336 /** 337 * Sets a configuration value, and then saves it. 338 * 339 * @param string $key 340 * @param unknown_type $value 341 */ 342 function set($key, $value) { 343 344 // Empty checkboxes are passed by jQuery as string 'undefined', but we want to store them as integer '0' 345 if ($value==="undefined") { $value=0; } 346 347 // Offline configuration is not saved in the normal configuration file 348 if (substr($key,0,8) == 'offline_') { 349 PivotxOffline::setConfig(substr($key,8),$value); 350 return; 351 } 352 353 // Only set (and save) if the value has actually changed. 354 if (empty($this->data[safeString($key)]) || $value !== $this->data[safeString($key)] ) { 355 356 $this->data[safeString($key)] = $value; 357 358 $this->saveConfig(true); 359 } 360 } 361 362 /** 363 * Delete a configuration value. Use with extreme caution. Saves the 364 * configuration afterwards 365 * 366 * @param string $key 367 */ 368 function del($key) { 369 // Old pre PivotX 2.0 configuration didn't use safe_string 370 // on the key - we are handling it here. 371 if (isset($this->data[safeString($key)])) { 372 unset($this->data[safeString($key)]); 373 } else { 374 unset($this->data[$key]); 375 } 376 377 $this->saveConfig(true); 378 } 379 380 /** 381 * Gets a single value from the configuration. 382 * 383 * @param string $key 384 * @return string 385 */ 386 function get($key) { 387 if (isset($this->data[$key])) { 388 return $this->data[$key]; 389 } else { 390 return false; 391 } 392 } 393 } 394 395 /** 396 * Since PHP4 doesn't allow class constants, we define the userlevels as 397 * global constants. 398 */ 399 define("PIVOTX_UL_NOBODY", -1); 400 define("PIVOTX_UL_MOBLOGGER", 0); 401 define("PIVOTX_UL_NORMAL", 1); 402 define("PIVOTX_UL_ADVANCED", 2); 403 define("PIVOTX_UL_ADMIN", 3); 404 define("PIVOTX_UL_SUPERADMIN", 4); 405 406 /** 407 * Portable PHP password hashing framework (phpass) for PivotX: 408 * 409 * The framework can be completely disabled by setting "disable_phpass" to 1 410 * in the advanced configuration. This is not recommended. If it is disabled, 411 * a salted md5 sum is used for password hashing. 412 * 413 * 1) The standard log2 number of iterations for password stretching. This 414 * should be increased from time to time to counteract increases in the speed 415 * and power of computers available to crack the hashes. However, since the 416 * current hashing algorithms aren't capable of running in parallell in PHP, 417 * it shouldn't be increased too often. (It should never exceed 31.) 418 */ 419 define('PIVOTX_PASSWORD_HASH_COUNT', 9); 420 /** 421 * 2) By default, portable hashes are used for maximum portability. Portable 422 * hashes can be disabled by setting "password_non_portable_hashes" 423 * to 1 in the advanced configuration. Non-portable hashes are more secure, 424 * but can be a problem on shared hosting or if you need to move your site 425 * between different servers. 426 */ 427 define('PIVOTX_PASSWORD_PORTABLE_HASHES', true); 428 429 /** 430 * This Class handles all operations with regard to users: adding, deleting, 431 * getting info, etc. 432 * 433 */ 434 class Users extends BaseConfig { 435 436 public function __construct() { 437 parent::__construct('ser_users.php'); 438 } 439 440 public function Users() { 441 $this->__construct(); 442 } 443 444 protected function verifyConfig() { 445 if ($this->count() < 1) { 446 return false; 447 } 448 449 return true; 450 } 451 452 protected function fixConfig() { 453 if (count($this->data) < 5) { 454 $this->readOld(); 455 456 $this->setChanged(); 457 } 458 } 459 460 protected function organizeConfig() { 461 // Make sure the users are sorted as intended. 462 uasort($this->data, array($this, 'sort')); 463 464 if ($this->count() < 1) { 465 return false; 466 } 467 468 return true; 469 } 470 471 protected function readOld() { 472 global $pivotx_path; 473 474 // If the old config file doesn't exist or it isn't readable, we return false.. 475 if (!file_exists($pivotx_path.'pv_cfg_settings.php') || (!is_readable($pivotx_path.'pv_cfg_settings.php'))) { 476 return false; 477 } 478 479 // Mark this configuration as updated from old PivotX 480 $this->setUpgraded(); 481 482 // get the config file 483 $fh = file($pivotx_path.'pv_cfg_settings.php'); 484 485 foreach ($fh as $fh_this) { 486 @list($name, $val) = explode("!", $fh_this); 487 $Cfg[trim($name)] = trim($val); 488 } 489 490 if(isset($Cfg['users'])) { 491 foreach(explode('|', trim($Cfg['users'])) as $inc => $user){ 492 $userdata = array(); 493 $userdata['username'] = $user; 494 foreach(explode('|-|' , $Cfg['user-' . $user]) as $var => $val){ 495 list($Nvar, $Nval) = explode('|', $val); 496 if ($Nvar == 'nick') { 497 $userdata['nickname'] = $Nval; 498 } elseif ($Nvar == 'pass') { 499 $userdata['md5_pass'] = $Nval; 500 } else { 501 $userdata[$Nvar] = $Nval; 502 } 503 } 504 list($userdata['language']) = explode("_",$userdata['language']); 505 $this->addUser($userdata); 506 } 507 } 508 } 509 510 /** 511 * Add a user to Pivot 512 * 513 * @param array $user 514 */ 515 function addUser($user) { 516 global $PIVOTX; 517 518 // Make sure the username is OK.. 519 $user['username'] = strtolower(safeString($user['username'])); 520 521 if ($this->getUser($user['username'])!==false) { 522 // this username is already taken.. 523 return false; 524 } 525 526 $newuser = array( 527 'username' => $user['username'], 528 'email' => $user['email'], 529 'userlevel' => $user['userlevel'], 530 'nickname' => $user['nickname'], 531 'language' => $user['language'], 532 'author_user' => $user['author_user'], 533 'image' => $user['image'], 534 'text_processing' => $user['text_processing'] 535 ); 536 537 if (!isset($user['pass1']) && isset($user['md5_pass'])) { 538 // User comes from old (1.x) config so we don't have the clear text password. 539 $newuser['password'] = $user['md5_pass']; 540 $newuser['salt'] = ''; 541 } else { 542 $newuser = $this->hashPassword($newuser, $user['pass1']); 543 } 544 545 $this->data[] = $newuser; 546 547 $this->saveConfig(true); 548 549 } 550 551 function deleteUser($username) { 552 553 if ($this->count() > 1) { 554 foreach($this->data as $key=>$user) { 555 if ($username == $user['username']) { 556 unset($this->data[$key]); 557 } 558 } 559 } 560 561 $this->saveConfig(true); 562 563 } 564 565 /** 566 * Update a given property of a user 567 * 568 * @param string $username 569 * @param array $properties 570 * @see $this->save 571 */ 572 function updateUser($username, $properties) { 573 574 // Select the correct user 575 foreach ($this->data as $key=>$user) { 576 if ($username == $user['username']) { 577 578 // Set the properties 579 foreach($properties as $property => $value) { 580 581 switch ($property) { 582 case "email": 583 case "nickname": 584 case "language": 585 case "text_processing": 586 case "lastseen": 587 case "userlevel": 588 case "image": 589 case "author_user": 590 $this->data[$key][$property] = $value; 591 break; 592 593 case "reset_id": 594 if ($value!="") { 595 $this->data[$key][$property] = $value; 596 } else { 597 unset($this->data[$key][$property]); 598 } 599 break; 600 601 case "pass1": 602 if ( ($value!="") && ($value!="******")) { 603 $this->data[$key] = $this->hashPassword($user, $value); 604 } 605 606 default: 607 break; 608 } 609 610 } 611 612 } 613 614 } 615 616 $this->saveConfig(true); 617 } 618 619 /** 620 * Check if a given password matches the one stored. 621 * 622 * @param string $username 623 * @param string $password 624 * @return boolean 625 */ 626 function checkPassword($username, $password) { 627 global $PIVOTX; 628 629 foreach($this->data as $user) { 630 631 if ($username==$user['username']) { 632 if ($user['salt'] == 'phpass') { 633 require_once($PIVOTX['paths']['pivotx_path'] . 'includes/PasswordHash.php'); 634 // We don't really need to set portability correctly when checking 635 // the password (since the hashing method is stored in the hash), 636 // but it's clearer to use the same code everywhere. 637 if ($PIVOTX['config']->get('password_non_portable_hashes')) { 638 $portable = false; 639 } else { 640 $portable = PIVOTX_PASSWORD_PORTABLE_HASHES; 641 } 642 $phpass = new PasswordHash(PIVOTX_PASSWORD_HASH_COUNT, $portable); 643 return $phpass->CheckPassword($password, $user['password']); 644 } else { 645 if (md5($password . $user['salt']) == $user['password']) { 646 return true; 647 } 648 } 649 break; 650 } 651 652 } 653 654 return false; 655 656 } 657 658 /** 659 * Hash a given password (for a given user). 660 * 661 * @param array $user 662 * @param string $password 663 * @return boolean 664 */ 665 function hashPassword($user, $password) { 666 global $PIVOTX; 667 668 if (!$PIVOTX['config']->get('disable_phpass')) { 669 require_once($PIVOTX['paths']['pivotx_path'] . 'includes/PasswordHash.php'); 670 if ($PIVOTX['config']->get('password_non_portable_hashes')) { 671 $portable = false; 672 } else { 673 $portable = PIVOTX_PASSWORD_PORTABLE_HASHES; 674 } 675 $phpass = new PasswordHash(PIVOTX_PASSWORD_HASH_COUNT, $portable); 676 $user['salt'] = 'phpass'; 677 $user['password'] = $phpass->HashPassword($password); 678 } else { 679 $user['salt'] = md5(rand(1,999999) . mktime()); 680 $user['password'] = md5( $password . $user['salt']); 681 } 682 683 return $user; 684 685 } 686 687 /** 688 * Check if a given $username is a user. 689 * 690 * @param string $name 691 * @return boolean 692 */ 693 function isUser($username) { 694 695 if ($this->getUser($username) === false) { 696 return false; 697 } else { 698 return true; 699 } 700 701 } 702 703 /** 704 * Get the specifics for a given user by its username. 705 * 706 * @param string $username 707 * @return array 708 */ 709 function getUser($username) { 710 711 foreach($this->data as $user) { 712 713 if ( ($username==$user['username']) ) { 714 return $user; 715 } 716 717 } 718 719 return false; 720 721 } 722 723 /** 724 * Get the specifics for a given user by its nickname. 725 * 726 * @param string $username 727 * @return array 728 */ 729 function getUserByNickname($username) { 730 731 foreach($this->data as $user) { 732 733 if ( strtolower($username) == strtolower($user['nickname']) ) { 734 return $user; 735 } 736 737 } 738 739 return false; 740 741 } 742 743 /** 744 * Get a list of the Usernames 745 * 746 * @return array 747 */ 748 function getUsernames() { 749 750 $res = array(); 751 752 foreach($this->data as $user) { 753 $res[]=$user['username']; 754 } 755 756 return $res; 757 758 } 759 760 /** 761 * Get a list of the Users Nicknames 762 * 763 * @return array 764 */ 765 function getUserNicknames() { 766 767 $res = array(); 768 769 foreach($this->data as $user) { 770 $res[ $user['username'] ] = $user['nickname']; 771 } 772 773 return $res; 774 775 } 776 777 /** 778 * Get a list of the Users Email adresses 779 * 780 * @return array 781 */ 782 function getUserEmail() { 783 784 $res = array(); 785 786 foreach($this->data as $user) { 787 $res[ $user['username'] ] = $user['email']; 788 } 789 790 return $res; 791 792 } 793 794 /** 795 * Get all users as an array 796 * 797 * @return array 798 */ 799 function getUsers() { 800 801 return $this->data; 802 803 } 804 805 /** 806 * Determines if $currentuser (or 'the current user', if left empty) is allowed 807 * to edit a page or entry that's owned by $contentowner. 808 * 809 * @param string $contentowner 810 * @param string $currentuser 811 * @return boolean 812 */ 813 function allowEdit($contenttype, $contentowner="", $currentuser="") { 814 global $PIVOTX; 815 816 // Default to the current logged in user. 817 if (empty($currentuser)) { 818 $currentuser = $PIVOTX['session']->currentUsername(); 819 } 820 821 // Fetch the current user.. 822 $currentuser = $PIVOTX['users']->getUser( $currentuser ); 823 $currentuserlevel = (!$currentuser?PIVOTX_UL_NOBODY:$currentuser['userlevel']); 824 825 // Always allow editing for superadmins - no matter content type. 826 if ($currentuserlevel==PIVOTX_UL_SUPERADMIN) { 827 return true; 828 } 829 830 // Fetch the owner.. 831 $contentowner = $PIVOTX['users']->getUser( $contentowner ); 832 $contentownerlevel = (!$contentowner?PIVOTX_UL_NOBODY:$contentowner['userlevel']); 833 834 // Now run the checks for different content types 835 if ($contenttype == 'chapter') { 836 837 // Only sdministrator and superadmins can add, edit and delete chapters. 838 if ($currentuserlevel>=PIVOTX_UL_ADMIN) { 839 return true; 840 } 841 842 } else if (($contenttype == 'entry') || ($contenttype == 'page')) { 843 844 // Get the value (if any) of allow_edit_for_own_userlevel setting 845 $allowsamelevel = getDefault( $PIVOTX['config']->get('allow_edit_for_own_userlevel'), PIVOTX_UL_SUPERADMIN); 846 847 if ($contentowner['username']==$currentuser['username']) { 848 // Always allow editing of your own content.. 849 return true; 850 } else if ($currentuserlevel > $contentownerlevel) { 851 // Allow editing content for items owned by lower levels. 852 return true; 853 } else if ( ($currentuserlevel == $contentownerlevel) && ( $currentuserlevel >= $allowsamelevel) ) { 854 // Allow if userlevel is the same, and greater than or equal to $allowsamelevel 855 return true; 856 } 857 858 } else if (($contenttype == 'comment') || ($contenttype == 'trackback')) { 859 860 if ($contentowner['username']==$currentuser['username']) { 861 // Always allow editing of comments/trackback on your own entries. 862 return true; 863 } else if ($currentuserlevel >= PIVOTX_UL_ADVANCED) { 864 return true; 865 } 866 867 } else { 868 debug('Unknown content type'); 869 } 870 871 // Disallow editing 872 return false; 873 } 874 875 /** 876 * Sort the users based on string comparison of username. 877 * 878 * @param array $a 879 * @param array $b 880 * @return int 881 */ 882 function sort($a, $b) { 883 global $PIVOTX; 884 885 return strcmp($a['username'],$b['username']); 886 } 887 } 888 889 /** 890 * This class deals with the Weblogs. 891 * 892 */ 893 class Weblogs extends BaseConfig { 894 895 var $default; 896 var $current; 897 898 public function __construct() { 899 parent::__construct('ser_weblogs.php'); 900 } 901 902 public function Weblogs() { 903 $this->__construct(); 904 } 905 906 public function verifyConfig() { 907 if ($this->count() < 1) { 908 return false; 909 } 910 } 911 912 public function fixConfig() { 913 if ($this->count() < 1) { 914 $this->readOld(); 915 916 $this->setChanged(); 917 } 918 919 if ($this->count() < 1) { 920 // No weblogs, create one from scratch 921 $this->add('weblog', __('My weblog'), 'pivotxdefault'); 922 } 923 } 924 925 protected function initConfig() { 926 global $PIVOTX; 927 928 foreach ($this->data as $key => $weblog) { 929 930 // Unset '$subkey' weblog -> compensates for an old bug 931 if (!empty($this->data[$key]['sub_weblog']['$subkey'])) { 932 unset($this->data[$key]['sub_weblog']['$subkey']); 933 } 934 935 // Make sure all categories are arrays. 936 if (is_array($weblog['sub_weblog'])) { 937 foreach ($weblog['sub_weblog'] as $subkey => $subweblog) { 938 if (!is_array($subweblog['categories'])) { 939 $this->data[$key]['sub_weblog'][$subkey]['categories'] = array($subweblog['categories']); 940 } 941 } 942 } 943 944 // Set the correct link to the weblog. 945 if (empty($this->data[$key]['site_url'])) { 946 $this->data[$key]['site_url'] = ""; 947 } 948 $this->data[$key]['link'] = $this->_getLink($key, $this->data[$key]['site_url']); 949 950 // Set the 'categories' for the combined subweblogs.. 951 $this->data[$key]['categories'] = $this->getCategories($key); 952 953 } 954 955 // Make sure the weblogs are sorted as intended. 956 $this->organizeConfig(); 957 958 // Set default weblog either as specified by the root in the config 959 // or just by selecting the first in the weblo 960 list($type, $root) = explode(":", $PIVOTX['config']->get('root')); 961 if ($type=="w" && !empty($root) && isset($this->data[$root]) ) { 962 $this->default = $root; 963 } else { 964 // Nothing to do but fall back to the first available weblog.. 965 reset($this->data); 966 $this->default = key($this->data); 967 } 968 } 969 970 protected function organizeConfig() { 971 uasort($this->data, array($this, 'sort')); 972 973 return true; 974 } 975 976 /** 977 * Read old weblogs data.. 978 */ 979 function readOld() { 980 981 if ((!file_exists(dirname(__FILE__)."/pv_cfg_weblogs.php")) || (!is_readable(dirname(__FILE__)."/pv_cfg_weblogs.php"))) { 982 return false; 983 } 984 985 // Mark this configuration as updated from old PivotX 986 $this->setUpgraded(); 987 988 $oldweblogs = loadSerialize(dirname(__FILE__)."/pv_cfg_weblogs.php", true); 989 990 // Looping over old weblogs. For each old weblog, add a new one with 991 // defaults values and then override the ones already set in the 992 // old config. This way we remove settings no longer present in 993 // PivotX. We also make sure the categories are all 'safe strings'.. 994 if(is_array($oldweblogs)) { 995 foreach($oldweblogs as $weblogkey => $weblog) { 996 $newweblogkey = safeString($weblogkey,true); 997 $this->add($newweblogkey, $oldweblogs[$weblogkey]['name'], 'pivotxdefault'); 998 foreach ($this->data[$newweblogkey] as $key => $value) { 999 if (isset($weblog[$key])) { 1000 $this->data[$newweblogkey][$key] = $weblog[$key]; 1001 } 1002 } 1003 foreach($this->data[$newweblogkey]['sub_weblog'] as $subweblogkey => $subweblog) { 1004 foreach($subweblog['categories'] as $categorykey => $category) { 1005 $this->data[$newweblogkey]['sub_weblog'][$subweblogkey]['categories'][$categorykey] = 1006 safeString($category, true); 1007 } 1008 } 1009 foreach($this->data[$newweblogkey]['categories'] as $categorykey => $category) { 1010 $this->data[$newweblogkey]['categories'][$categorykey] = safeString($category, true); 1011 } 1012 } 1013 } 1014 1015 } 1016 1017 /** 1018 * Sort the weblogs based on string comparison of name. 1019 * 1020 * @param array $a 1021 * @param array $b 1022 * @return int 1023 */ 1024 function sort($a, $b) { 1025 global $PIVOTX; 1026 1027 if ( (empty($a['sortorder']) && empty($b['sortorder'])) || ($a['sortorder'] == $b['sortorder']) ) { 1028 return strcmp($a['name'],$b['name']); 1029 } else { 1030 return ($a['sortorder'] < $b['sortorder']) ? -1 : 1; 1031 } 1032 1033 } 1034 1035 /** 1036 * Return all weblogs as an array 1037 * 1038 * @return array 1039 */ 1040 function getWeblogs() { 1041 1042 return $this->data; 1043 1044 } 1045 1046 /** 1047 * Returns an array with the weblog names. 1048 * 1049 * @return array 1050 */ 1051 function getWeblogNames() { 1052 1053 $names = array(); 1054 1055 foreach($this->data as $name=>$data) { 1056 $names[] = $name; 1057 } 1058 1059 return $names; 1060 1061 } 1062 1063 /** 1064 * Check if a given $name is a weblog. 1065 * 1066 * @param string $name 1067 * @return boolean 1068 */ 1069 function isWeblog($weblogname) { 1070 1071 foreach ($this->data as $name=>$data) { 1072 if ($weblogname==$name) { return true; } 1073 } 1074 1075 return false; 1076 } 1077 1078 1079 /** 1080 * Return the weblogs that have the given category or categories assigned 1081 * to them. 1082 * 1083 * @param array $categories 1084 */ 1085 function getWeblogsWithCat($categories) { 1086 1087 // $cats might be a string with one cat, if so, convert to array 1088 if (is_string($categories)) { 1089 $categories= array($categories); 1090 } 1091 1092 $res=array(); 1093 1094 // search every weblog for all cats 1095 foreach ($this->data as $key => $weblog) { 1096 1097 $weblogcategories = $this->getCategories($key); 1098 1099 foreach ($categories as $cat) { 1100 if (in_array($cat, $weblogcategories)) { 1101 $res[]=$key; 1102 } 1103 } 1104 1105 } 1106 1107 return array_unique($res); 1108 } 1109 1110 /** 1111 * Get the categories from a certain weblog. 1112 * 1113 * @param string $weblogname 1114 * @return array 1115 */ 1116 function getCategories($weblogname='') { 1117 1118 // if no weblogname was given, use the 'current'.. 1119 if (empty($weblogname)) { $weblogname = $this->getCurrent(); } 1120 1121 $results = array(); 1122 1123 // Group the categories from the subweblogs together.. 1124 if (is_array($this->data[$weblogname]['sub_weblog'])) { 1125 foreach ($this->data[$weblogname]['sub_weblog'] as $key=>$sub) { 1126 1127 $cats = $sub['categories']; 1128 // $cats might be a string with one cat, if so, convert to array 1129 if (is_string($cats)) { 1130 $cats= array($cats); 1131 } 1132 1133 // Add them to results 1134 foreach($cats as $cat) { 1135 $results[] = $cat; 1136 } 1137 } 1138 } 1139 1140 return array_unique($results); 1141 1142 } 1143 1144 /** 1145 * Returns the given weblog as an array. If no weblogname was given, use 1146 * the current weblog. 1147 * 1148 * @param string $weblogname 1149 * @return array 1150 */ 1151 function getWeblog($weblogname='') { 1152 1153 // if no weblogname was given, use the 'current'.. 1154 if (empty($weblogname)) { $weblogname = $this->getCurrent(); } 1155 1156 return $this->data[$weblogname]; 1157 1158 } 1159 1160 /** 1161 * Return a subweblog as an array 1162 * 1163 * @param string $weblogname 1164 * @return array 1165 */ 1166 function getSubweblog($weblogname='', $subweblogname) { 1167 1168 // if no weblogname was given, use the 'current'.. 1169 if (empty($weblogname)) { $weblogname = $this->getCurrent(); } 1170 1171 return $this->data[$weblogname]['sub_weblog'][$subweblogname]; 1172 1173 } 1174 1175 /** 1176 * Return the subweblogs of a given weblog as an array. It does this 1177 * by grabbing all [[weblog]] and [[ subweblog ]] tags from the templates 1178 * in the same folder as the template that was selected as the frontpage 1179 * template. Updates the subweblog info in the weblogs object. 1180 * 1181 * @param string $weblogname 1182 * @return array 1183 */ 1184 function getSubweblogs($weblogname='') { 1185 global $PIVOTX; 1186 1187 // if no weblogname was given, use the 'current'.. 1188 if (empty($weblogname)) { 1189 $weblogname = $this->getCurrent(); 1190 } 1191 1192 $results = array(); 1193 1194 $weblog = $this->getWeblog($weblogname); 1195 $dirname = dirname($weblog['front_template']); 1196 1197 if ( !is_dir($PIVOTX['paths']['templates_path'] . $dirname) || !is_readable($PIVOTX['paths']['templates_path'] . $dirname) ) { 1198 debug("Template folder $dirname doesn't exist or isn't readable"); 1199 return array(); 1200 } 1201 1202 $dir = dir($PIVOTX['paths']['templates_path'] . $dirname); 1203 1204 // Iterate through the files in the folder.. 1205 while (false !== ($filename = $dir->read())) { 1206 $ext = getExtension($filename); 1207 if (in_array($ext, array('html', 'htm', 'tpl'))) { 1208 1209 $template_html = loadTemplate($dirname . "/" . $filename); 1210 1211 preg_match_all("/\[\[\s?(sub)?weblog([: ])(.*)?\]\]/mUi", $template_html, $matches); 1212 1213 foreach($matches[3] as $key=>$match) { 1214 1215 // if $matches[2][$key] was a ':', we know it's an old pivot 1.x style [[ subweblog:name ]] 1216 // We also must handle optional arguments to the subweblog. 1217 if ($matches[2][$key]==":") { 1218 $name = explode(':',$match); 1219 $results[] = trim($name[0]); 1220 } else { 1221 preg_match("/name=['\"]([^'\"]*)/mi", $match, $name); 1222 // subweblog 'archive' has a special role so skip it here (not when in the dashboard) 1223 // this is to disregard its number of entries e.g. for the pager display and initial building of the front page 1224 // as no pager is allowed for archive display the number is only irrelevant. 1225 if ($name[1]=='archive' && isset($PIVOTX['parser'])) { 1226 $name[1] = ''; 1227 } 1228 if ($name[1]!="") { 1229 $results[] = $name[1]; 1230 } 1231 1232 } 1233 1234 } 1235 1236 } 1237 } 1238 $dir->close(); 1239 1240 $results = array_unique($results); 1241 1242 // Remove any subweblogs that no longer exists from the weblog data. 1243 $updated = false; 1244 foreach ($this->data[$weblogname]['sub_weblog'] as $name => $value) { 1245 if (!in_array($name,$results)) { 1246 unset($this->data[$weblogname]['sub_weblog'][$name]); 1247 $updated = true; 1248 } 1249 } 1250 if ($updated) { 1251 $this->saveConfig(true); 1252 } 1253 1254 return $results; 1255 1256 } 1257 1258 /** 1259 * Sets a given weblog as 'current' and returns false if the weblog 1260 * doesn't exist. 1261 * 1262 * @param string $weblogname 1263 * @return boolean 1264 */ 1265 function setCurrent($weblogname='') { 1266 global $PIVOTX; 1267 1268 $exists = true; 1269 1270 if ( !isset($this->data[$weblogname]) ) { 1271 $exists = false; 1272 $weblogname = ''; 1273 } 1274 1275 if (empty($weblogname)) { 1276 $this->current = $this->default; 1277 } else { 1278 $this->current = $weblogname; 1279 } 1280 1281 return $exists; 1282 1283 } 1284 1285 /** 1286 * Sets a given weblog as 'current' based on a given category and returns false 1287 * if no matching weblog could be set. 1288 * 1289 * @param mixed $categories 1290 * @return boolean 1291 */ 1292 function setCurrentFromCategory($categories) { 1293 1294 // $categories might be a string (with comma seperated categories). 1295 if (is_string($categories)) { 1296 $categories = explode(",", $categories); 1297 $categories = array_map('trim', $categories); 1298 } 1299 1300 // Check categories in current weblog first (if set) and then the 1301 // default weblog 1302 if (!empty($this->current)) { 1303 $weblogcategories = $this->data[$this->current]['categories']; 1304 foreach ($categories as $cat) { 1305 if (in_array($cat, $weblogcategories)) { 1306 return true; 1307 } 1308 } 1309 } else { 1310 $weblogcategories = $this->data[$this->default]['categories']; 1311 foreach ($categories as $cat) { 1312 if (in_array($cat, $weblogcategories)) { 1313 $this->setCurrent($this->default); 1314 return true; 1315 } 1316 } 1317 } 1318 1319 $skip_weblogs = array($this->current, $this->default); 1320 1321 // search every weblog for all cats 1322 foreach ($this->data as $key => $weblog) { 1323 1324 // Skip current and default since we checked them above 1325 if (in_array($key, $skip_weblogs)) { 1326 continue; 1327 } 1328 1329 $weblogcategories = $this->getCategories($key); 1330 1331 foreach ($categories as $cat) { 1332 if (in_array($cat, $weblogcategories)) { 1333 $this->setCurrent($key); 1334 return true; 1335 } 1336 } 1337 } 1338 1339 return false; 1340 1341 } 1342 1343 /** 1344 * Gets the currently active weblog. 1345 * 1346 * @return 1347 */ 1348 function getCurrent() { 1349 1350 // Set the current weblog, just to be sure. 1351 if (empty($this->current)) { $this->setCurrent(""); } 1352 1353 return $this->current; 1354 1355 } 1356 1357 /** 1358 * Gets the default weblog. 1359 * 1360 * @return 1361 */ 1362 function getDefault() { 1363 1364 return $this->default; 1365 1366 } 1367 1368 /** 1369 * Add a new weblog, based on $theme. returns the internal name used for 1370 * the weblog. 1371 * 1372 * @param string $internal 1373 * @param string $name 1374 * @param string $theme 1375 * @return string 1376 */ 1377 function add($internal, $name, $theme) { 1378 1379 if ( ($internal=="") || isset($this->data[$internal])) { 1380 // Make a new 'name'.. 1381 for($i=1;$i<1000;$i++) { 1382 if (!isset($this->data[$internal . "_" . $i])) { 1383 $internal = $internal . "_" . $i; 1384 break; 1385 } 1386 } 1387 } 1388 1389 if ($theme=="blank") { 1390 1391 $this->data[$internal]['name']=$name; 1392 1393 $this->saveConfig(true); 1394 1395 } else if ($theme=="pivotxdefault") { 1396 1397 $weblog = getDefaultWeblog(); 1398 1399 $weblog['name'] = $name; 1400 1401 if (empty($weblog['sortorder'])) { $weblog['sortorder'] = 10; } 1402 $this->data[$internal] = $weblog; 1403 1404 $this->saveConfig(true); 1405 1406 1407 } else { 1408 1409 $weblog = loadSerialize($theme, true); 1410 1411 $weblog['name'] = $name; 1412 1413 if (empty($weblog['sortorder'])) { $weblog['sortorder'] = 10; } 1414 1415 $this->data[$internal] = $weblog; 1416 1417 $this->saveConfig(true); 1418 1419 } 1420 1421 return $internal; 1422 1423 } 1424 1425 /** 1426 * Delete a weblog 1427 * 1428 * @param string $weblogname 1429 */ 1430 function delete($weblogname) { 1431 1432 unset($this->data[$weblogname]); 1433 1434 $this->saveConfig(true); 1435 1436 } 1437 1438 /** 1439 * Export a weblog as a theme file. The file is saved in the same folder as 1440 * the weblog's frontpage template. 1441 * 1442 * @param string $weblogname 1443 */ 1444 function export($weblogname) { 1445 1446 $weblog = $this->data[$weblogname]; 1447 $filename = dirname("./templates/".$weblog['front_template'])."/".$weblogname.".theme"; 1448 1449 saveSerialize($filename, $weblog); 1450 1451 } 1452 1453 /** 1454 * Sets a property of a given weblog 1455 * 1456 * @param string $weblogname 1457 * @param string $key 1458 * @param string $value 1459 */ 1460 function set($weblogname, $key, $value) { 1461 1462 if (isset($this->data[$weblogname])) { 1463 1464 if (strpos($key, "#")>0) { 1465 // we're setting something in a subweblog 1466 // we get these as linkdump#categories = linkdump,books,movies 1467 list($sub, $key) = explode("#", str_replace("[]", "", $key)); 1468 1469 1470 if (strpos($value, ",")>0) { 1471 $value = explode(",", $value); 1472 } 1473 1474 $this->data[$weblogname]['sub_weblog'][$sub][$key] = $value; 1475 1476 // we must update the list of categories for the weblog 1477 $categories = array(); 1478 foreach ($this->data[$weblogname]['sub_weblog'] as $subweblog) { 1479 $categories = array_merge($categories,$subweblog['categories']); 1480 } 1481 $this->data[$weblogname]['categories'] = array_unique($categories); 1482 1483 } else { 1484 1485 if ($key == 'site_url') { 1486 $this->data[$weblogname]['link'] = $this->_getLink($weblogname, $value); 1487 } 1488 1489 $this->data[$weblogname][$key] = $value; 1490 1491 } 1492 1493 $this->saveConfig(true); 1494 1495 } else { 1496 1497 debug('tried to set a setting without passing a weblogname, or non-existing weblog'); 1498 1499 } 1500 1501 } 1502 1503 /** 1504 * Gets a property of a given weblog 1505 * 1506 * @param string $weblogname 1507 * @param string $key 1508 */ 1509 function get($weblogname, $key) { 1510 1511 if ($weblogname=="") { 1512 $weblogname = $this->getCurrent(); 1513 } 1514 1515 if (empty($this->data[$weblogname])) { 1516 static $noted = array(); 1517 if (!isset($noted[$weblogname])) { 1518 // only show this message once in the lifetime of the request 1519 debug("Weblog '$weblogname' doesn't exist!"); 1520 1521 $noted[$weblogname] = true; 1522 } 1523 $weblogname = key($this->data); 1524 } 1525 1526 return $this->data[$weblogname][$key]; 1527 1528 } 1529 1530 /** 1531 * Calculates the link for a given weblog 1532 * 1533 * @param string $value 1534 * @param string $weblogname 1535 */ 1536 function _getLink($weblogname, $value) { 1537 global $PIVOTX; 1538 1539 $link = trim($value); 1540 if ($link == '') { 1541 if ($PIVOTX['config']->get('mod_rewrite')==0) { 1542 $link = $PIVOTX['paths']['site_url'] . '?w=' . $weblogname; 1543 } else { 1544 $prefix = getDefault( $PIVOTX['config']->get('localised_weblog_prefix'), "weblog"); 1545 $link = $PIVOTX['paths']['site_url'] . $prefix . "/" . $weblogname; 1546 } 1547 } else { 1548 $ext = getExtension(basename($link)); 1549 if ($ext == '') { 1550 $link = addTrailingSlash($link); 1551 } 1552 } 1553 1554 return $link; 1555 } 1556 1557 function deleteCategoryFromWeblogs($name) { 1558 1559 // Remove it from all weblogs as well. 1560 $weblogs = $this->data; 1561 1562 foreach($weblogs as $weblogkey=>$weblog) { 1563 foreach($weblog['sub_weblog'] as $subweblogkey=>$subweblog) { 1564 foreach($subweblog['categories'] as $catkey => $cat) { 1565 if ($cat==$name) { 1566 unset($weblogs[$weblogkey]['sub_weblog'][$subweblogkey]['categories'][$catkey]); 1567 } 1568 } 1569 1570 } 1571 foreach($weblogs[$weblogkey]['categories'] as $catkey => $cat) { 1572 if ($cat==$name) { 1573 unset($weblogs[$weblogkey]['categories'][$catkey]); 1574 } 1575 } 1576 } 1577 1578 $this->data = $weblogs; 1579 1580 $this->saveConfig(true); 1581 } 1582 } 1583 1584 /** 1585 * This class deals with the categories 1586 * 1587 */ 1588 class Categories extends BaseConfig { 1589 public function __construct() { 1590 parent::__construct('ser_categories.php'); 1591 } 1592 1593 public function Categories() { 1594 $this->__construct(); 1595 } 1596 1597 protected function verifyConfig() { 1598 if ($this->count() < 1) { 1599 return false; 1600 } 1601 1602 return true; 1603 } 1604 1605 protected function fixConfig() { 1606 $save = false; 1607 1608 if ($this->count() < 1) { 1609 // hmm, couldn't find the data.. Perhaps try to import it from old Pivot 1.x 1610 $this->readOld(); 1611 $save = true; 1612 } 1613 1614 if ($this->count()<1) { 1615 // if there still are no categories, load the defaults 1616 $this->data = getDefaultCategories(); 1617 $save = true; 1618 } 1619 1620 if ($save) { 1621 $this->saveConfig(true); 1622 } 1623 } 1624 1625 protected function organizeConfig() { 1626 usort($this->data, array($this, 'sort')); 1627 1628 return true; 1629 } 1630 1631 /** 1632 * Sort the categories based on the order and string comparison 1633 * of display name if order is identical. 1634 * 1635 * @param array $a 1636 * @param array $b 1637 * @return int 1638 */ 1639 function sort($a, $b) { 1640 global $PIVOTX; 1641 1642 if ($PIVOTX['config']->get('sort_categories_by_alphabet')==true) { 1643 // If we set 'sort_categories_by_alphabet' to true, always sort by alphabet.. 1644 return strcmp($a['display'],$b['display']); 1645 } else if ($a['order'] == $b['order']) { 1646 // Else sort by alphabet, if order is the same.. 1647 return strcmp($a['display'],$b['display']); 1648 } else { 1649 // else sort by order.. 1650 return ($a['order'] < $b['order']) ? -1 : 1; 1651 } 1652 1653 } 1654 1655 /** 1656 * Old save function 1657 */ 1658 public function saveCategories() { 1659 return $this->saveConfig(true); 1660 } 1661 1662 protected function readOld() { 1663 global $pivotx_path; 1664 1665 // If the old config file doesn't exist or it isn't readable, we return false.. 1666 if (!file_exists($pivotx_path.'pv_cfg_settings.php') || (!is_readable($pivotx_path.'pv_cfg_settings.php'))) { 1667 return false; 1668 } 1669 1670 // Mark this configuration as updated from old PivotX 1671 $this->setUpgraded(); 1672 1673 // get the config file 1674 $fh = file($pivotx_path.'pv_cfg_settings.php'); 1675 1676 foreach ($fh as $fh_this) { 1677 @list($name, $val) = explode("!", $fh_this); 1678 $Cfg[trim($name)] = trim($val); 1679 } 1680 //GetUserInfo(); 1681 //ExpandSessions(); 1682 1683 $catnames = explode("|",$Cfg['cats']); 1684 1685 // Check which categories are "hidden".. 1686 if (isset($Cfg['cats-searchexclusion'])) { 1687 $hiddenarray = explode('|', strtolower($Cfg['cats-searchexclusion'])); 1688 } else { 1689 $hiddenarray = array(); 1690 } 1691 1692 // Check the category order.. 1693 if (isset($Cfg['cats-order'])) { 1694 $temp = explode('|-|', strtolower($Cfg['cats-order'])); 1695 1696 foreach($temp as $item) { 1697 list($catname, $order) = explode("|", $item); 1698 $orderarray[strtolower($catname)] = $order; 1699 } 1700 } else { 1701 $orderarray = array(); 1702 } 1703 1704 1705 $cats = array(); 1706 1707 foreach ($catnames as $cat) { 1708 1709 // Skip empty category names. 1710 $catname = trim($cat); 1711 if ($catname == '') { 1712 continue; 1713 } 1714 1715 $catname = strtolower($catname); 1716 1717 if (isset($Cfg['cat-'.$cat])) { 1718 $users = explode('|', strtolower($Cfg['cat-'.$cat])); 1719 } else { 1720 $users = array(); 1721 } 1722 1723 // Make sure the users are 'safe strings' 1724 foreach($users as $key=>$user) { 1725 $users[$key] = safeString($user, true); 1726 } 1727 1728 $cats[] = array ( 1729 'name' => safeString($catname, true), 1730 'display' => $cat, 1731 'users' => $users, 1732 'hidden' => (in_array($catname, $hiddenarray)) ? 1 : 0, 1733 'order' => (isset($orderarray[$catname])) ? $orderarray[$catname] : 110, 1734 ); 1735 1736 } 1737 1738 1739 $this->data = $cats; 1740 1741 } 1742 1743 /** 1744 * change the settings for an existing category, or modify an existing one. 1745 * 1746 * @param string $name 1747 * @param array $cat 1748 */ 1749 function setCategory($name, $cat) { 1750 1751 $name = strtolower(safeString($name)); 1752 $cat['name'] = strtolower(safeString($cat['name'])); 1753 1754 foreach($this->data as $key=>$val) { 1755 1756 if ($name==$val['name']) { 1757 1758 $this->data[$key] = $cat; 1759 $this->saveConfig(true); 1760 return; 1761 } 1762 1763 } 1764 1765 // Otherwise it must be a new one, let's add it: 1766 if(!empty($cat['name'])){ 1767 $this->data[] = $cat; 1768 $this->saveConfig(true); 1769 } 1770 1771 1772 } 1773 1774 /** 1775 * Get an array with all the categories. We filter the users to make sure we only 1776 * return users that still exist 1777 * 1778 * @return array 1779 */ 1780 function getCategories() { 1781 global $PIVOTX; 1782 1783 $results = $this->data; 1784 1785 $users = $PIVOTX['users']->getUsernames(); 1786 1787 // Filter only existing users.. 1788 foreach ($results as $key=>$value) { 1789 // Categories doesn't have to be assigned to any users. 1790 if (isset($results[$key]['users']) && is_array($results[$key]['users'])) { 1791 $results[$key]['users'] = array_intersect($results[$key]['users'], $users); 1792 } else { 1793 $results[$key]['users'] = array(); 1794 } 1795 1796 } 1797 1798 return $results; 1799 1800 } 1801 1802 1803 1804 1805 1806 /** 1807 * Get a list of categories the user is allowed to post into 1808 */ 1809 function allowedCategories($username) { 1810 1811 $allowed = array(); 1812 1813 foreach($this->data as $cat) { 1814 1815 if (in_array($username, $cat['users'])) { 1816 $allowed[$cat['name']] = $cat['name']; 1817 } 1818 1819 } 1820 1821 return $allowed; 1822 1823 } 1824 1825 /** 1826 * Allow a user to post in this category 1827 * 1828 * @param string $catname 1829 * @param string $username 1830 */ 1831 function allowUser($catname, $username) { 1832 1833 // Loop through all available categories 1834 foreach($this->data as $key=>$cat) { 1835 1836 if ($cat['name']==$catname) { 1837 1838 // Add the username 1839 $this->data[$key]['users'][] = $username; 1840 1841 // But remove duplicates 1842 $this->data[$key]['users'] = array_unique($this->data[$key]['users']); 1843 1844 } 1845 1846 } 1847 1848 } 1849 1850 1851 /** 1852 * Disallow a user to post in this category 1853 * 1854 * @param string $catname 1855 * @param string $username 1856 */ 1857 function disallowUser($catname, $username) { 1858 1859 // Loop through all available categories 1860 foreach($this->data as $key=>$cat) { 1861 1862 if ($cat['name']==$catname) { 1863 1864 // Loop through the users, and remove $username if present. 1865 foreach($cat['users'] as $userkey=>$catuser){ 1866 if ($catuser==$username) { 1867 unset($this->data[$key]['users'][$userkey]); 1868 } 1869 } 1870 1871 } 1872 1873 } 1874 1875 } 1876 1877 1878 /** 1879 * Get a single category 1880 * 1881 * @param string $name 1882 * @return array 1883 */ 1884 function getCategory($name) { 1885 1886 foreach($this->data as $key=>$cat) { 1887 1888 if ($cat['name']==$name) { 1889 return $cat; 1890 } 1891 1892 } 1893 1894 return array(); 1895 1896 } 1897 1898 /** 1899 * Get a list of all category names 1900 * 1901 * @return array 1902 */ 1903 function getCategorynames() { 1904 1905 $names = array(); 1906 1907 foreach($this->data as $cat) { 1908 $names[]=$cat['name']; 1909 } 1910 return $names; 1911 1912 } 1913 1914 1915 /** 1916 * Check if a given $name is a category. 1917 * 1918 * @param string $name 1919 * @return boolean 1920 */ 1921 function isCategory($name) { 1922 1923 foreach($this->data as $cat) { 1924 if($name==$cat['name']) { return true; } 1925 } 1926 1927 return false; 1928 1929 } 1930 1931 1932 1933 /** 1934 * Get a list of all category names in which we should NOT search 1935 * 1936 * @return array 1937 */ 1938 function getSearchCategorynames() { 1939 1940 $names = array(); 1941 1942 foreach($this->data as $cat) { 1943 if ($cat['hidden']!=1) { 1944 $names[]=$cat['name']; 1945 } 1946 } 1947 1948 return $names; 1949 1950 1951 1952 } 1953 1954 1955 /** 1956 * Delete a single category 1957 * 1958 * @param string $name 1959 */ 1960 function deleteCategory($name) { 1961 global $PIVOTX; 1962 1963 foreach($this->data as $key=>$cat) { 1964 1965 if ($cat['name']==$name) { 1966 unset($this->data[$key]); 1967 $this->saveConfig(true); 1968 break; 1969 } 1970 1971 } 1972 1973 $PIVOTX['weblogs']->deleteCategoryFromWeblogs($name); 1974 1975 } 1976 1977 } 1978 1979 /** 1980 * This class deals with Sessions: logging in, logging out, saving sessions 1981 * and performing checks for required userlevels. 1982 * 1983 * This class protects the cookie/session against standard XSS attacks and 1984 * sidejacking. 1985 * 1986 */ 1987 class Session { 1988 1989 var $permsessions, $logins, $maxlogins, $message; 1990 /** 1991 * Initialisation 1992 * 1993 * @return Session 1994 */ 1995 function Session() { 1996 global $PIVOTX; 1997 1998 $this->cookie_lifespan = 60*60*24*30; // 30 days 1999 $this->cookie_name = "pivotxsession"; 2000 2001 $this->maxlogins = getDefault($PIVOTX['config']->get('loginlog_length'), 200); 2002 if (intval($this->maxlogins) < 10) { 2003 $this->maxlogins = 200; 2004 } 2005 2006 // Select the secure bit for the session cookie. Setting it to true if 2007 // using HTTPS which stops sidejacking / session hijacking. 2008 if (isSecureConn()) { 2009 $this->cookie_secure = true; 2010 } else { 2011 $this->cookie_secure = false; 2012 } 2013 2014 // Force cookie to be "HTTP only" to make cookie stealing harder - stops 2015 // standard XSS attacks. (Introduced in PHP 5.2.0.) 2016 if (checkVersion(phpversion(), '5.2.0')) { 2017 $this->cookie_httponly = true; 2018 } else { 2019 $this->cookie_httponly = false; 2020 } 2021 2022 // On second thought, our CSRF check (that uses the double cookie submit 2023 // test) needs to access the cookie ... We just can't use "HTTP only". 2024 $this->cookie_httponly = false; 2025 2026 // Set to 'site url' instead of 'pivotx_url', because then we 2027 // can use 'edit this entry' and the like. 2028 $this->cookie_path = $PIVOTX['paths']['site_url']; 2029 2030 // Don't set the domain for a cookie on a "TLD" - like localhost ... 2031 if (strpos($_SERVER["SERVER_NAME"], ".") > 0) { 2032 if (preg_match("/^www./",$_SERVER["SERVER_NAME"])) { 2033 $this->cookie_domain = "." . preg_replace("/^www./", "", $_SERVER["SERVER_NAME"]); 2034 } else { 2035 $this->cookie_domain = $_SERVER["SERVER_NAME"]; 2036 } 2037 } else { 2038 $this->cookie_domain = ""; 2039 } 2040 2041 // Only set "HTTP only" if supported 2042 if ($this->cookie_httponly) { 2043 session_set_cookie_params($this->cookie_lifespan, 2044 $this->cookie_path, $this->cookie_domain, $this->cookie_secure, $this->cookie_httponly); 2045 } else { 2046 session_set_cookie_params($this->cookie_lifespan, 2047 $this->cookie_path, $this->cookie_domain, $this->cookie_secure); 2048 } 2049 2050 session_start(); 2051 2052 } 2053 2054 /** 2055 * Sets a cookie taking into account the path, domain, secure connection 2056 * and if "HTTP only" is supported. Basically a wrapper around setcookie. 2057 * 2058 * @param string $name 2059 * @param string $value 2060 * @param string $time 2061 */ 2062 function setCookie($name, $value, $time='') { 2063 if ($time == '') { 2064 $time = time() + $this->cookie_lifespan; 2065 } 2066 if ($this->cookie_httponly) { 2067 $res = setcookie($name, $value, $time, $this->cookie_path, 2068 $this->cookie_domain, $this->cookie_secure, $this->cookie_httponly ); 2069 } else { 2070 $res = setcookie($name, $value, $time, $this->cookie_path, 2071 $this->cookie_domain, $this->cookie_secure ); 2072 } 2073 2074 // Add some debug output, if we couldn't set the cookie. 2075 if ($res==false) { 2076 debug("Couldn't set cookies! (probably because output has already started)"); 2077 if (headers_sent($filename, $linenum)) { 2078 debug("Headers already sent in $filename on line $linenum"); 2079 } else { 2080 debug("Headers have not been sent yet. Something's wonky."); 2081 } 2082 } 2083 2084 } 2085 2086 /** 2087 * Verify if whomever requested the current page is logged in as a user, 2088 * or else attempt to (transparently) continue from a saved session. 2089 * 2090 * @return boolean 2091 */ 2092 function isLoggedIn() { 2093 global $PIVOTX; 2094 2095 $this->loadPermsessions(); 2096 2097 $sessioncookie = (!empty($_COOKIE['pivotxsession'])) ? $_COOKIE['pivotxsession'] : $_POST['pivotxsession']; 2098 2099 if (isset($_SESSION['user']) && isset($_SESSION['user']['username']) && ($_SESSION['user']['username']!="") ) { 2100 2101 // User is logged in! 2102 2103 // Check if we're in the saved sessions.. 2104 if (!empty($sessioncookie) && !isset($this->permsessions[$sessioncookie])) { 2105 2106 $this->permsessions[ $sessioncookie ] = array( 2107 'username' => $_SESSION['user']['username'], 2108 'ip' => $_SERVER['REMOTE_ADDR'], 2109 'lastseen' => time() 2110 ); 2111 $this->savePermsessions(); 2112 } 2113 2114 return true; 2115 2116 } else { 2117 2118 // See if we can continue a stored session.. 2119 2120 // Check if we have a pivotxsession cookie that matches a saved session.. 2121 if ( (!empty($sessioncookie)) && (isset($this->permsessions[$sessioncookie])) ) { 2122 2123 $savedsess = $this->permsessions[ $sessioncookie ]; 2124 2125 // Check if the IP in the saved session matches the IP of the user.. 2126 if ($_SERVER['REMOTE_ADDR'] == $savedsess['ip']) { 2127 2128 // Check if the 'lastseen' wasn't too long ago.. 2129 if (time() < ($savedsess['lastseen'] + $this->cookie_lifespan) ) { 2130 2131 // Finally check if the user in the stored session still exists. 2132 if (!$PIVOTX['users']->isUser($savedsess['username'])) { 2133 return false; 2134 } 2135 2136 // If we get here, we can restore the session! 2137 2138 $_SESSION['user']= $PIVOTX['users']->getUser($savedsess['username']); 2139 2140 // Update the 'lastseen' in permanent sessions. 2141 $this->permsessions[ $sessioncookie ]['lastseen'] = time(); 2142 $this->savePermsessions(); 2143 2144 // Add the 'lastseen' to the user settings. 2145 $PIVOTX['users']->updateUser($savedsess['username'], array('lastseen'=>time()) ); 2146 $_SESSION['user']['lastseen'] = time(); 2147 2148 // Set the session cookie as session variable. 2149 $_SESSION['pivotxsession'] = $sessioncookie; 2150 2151 return true; 2152 2153 } 2154 2155 } 2156 2157 } 2158 return false; 2159 2160 } 2161 2162 } 2163 2164 /** 2165 * Attempt to log in a user, using the passed credentials. If succesfull, 2166 * the session info is updated and 'true' is returned. When unsuccesful 2167 * the session remains unaltered, and false is returned 2168 * 2169 * 2170 * @param string $username 2171 * @param string $password 2172 * @param int $stay 2173 * @return boolean 2174 */ 2175 function login($username, $password, $stay) { 2176 global $PIVOTX; 2177 2178 $this->loadLogins(); 2179 2180 if (!$this->checkFailedLogins()) { 2181 debug(sprintf(__("Blocked login attempt from '%s'."), $_SERVER['REMOTE_ADDR'])); 2182 $this->message = __('Too many failed login attempts from this IP address. ' . 2183 'Please contact your site administrator to unblock your account.'); 2184 return false; 2185 } 2186 2187 $username = strtolower($username); 2188 2189 $match = $PIVOTX['users']->checkPassword($username, $password); 2190 2191 if (!$match) { 2192 2193 $this->message = __('Incorrect username/password'); 2194 $this->logFailedLogin(); 2195 return false; 2196 2197 } else { 2198 2199 $this->message = __('Successfully logged in'); 2200 $key = makeKey(16); 2201 $_SESSION['pivotxsession'] = $key; 2202 2203 // Add the 'lastseen' to the user settings and remove and reset_ids. 2204 $PIVOTX['users']->updateUser($username, array('lastseen'=>time(), 'reset_id'=>'') ); 2205 2206 // Keep track of people logging in (and remove any failed logins 2207 // for IP if any). 2208 $this->logins['succeeded'][] = array( 2209 'username' => $username, 2210 'time' => time(), 2211 'ip' => $_SERVER['REMOTE_ADDR'] 2212 ); 2213 unset($this->logins['failed'][$_SERVER['REMOTE_ADDR']]); 2214 $this->saveLogins(); 2215 2216 $_SESSION['user']= $PIVOTX['users']->getUser($username); 2217 2218 $path = $PIVOTX['paths']['site_url']; // Set to 'site url' instead of 'pivotx_url', because then we 2219 // can use 'edit this entry' and the like. 2220 2221 if ($stay==1) { 2222 2223 $this->setCookie($this->cookie_name, $key); 2224 2225 } else { 2226 2227 $this->setCookie($this->cookie_name, $key, 0); 2228 2229 } 2230 2231 $this->permsessions[ $key ] = array( 2232 'username' => $username, 2233 'ip' => $_SERVER['REMOTE_ADDR'], 2234 'lastseen' => time() 2235 ); 2236 2237 $this->savePermsessions(); 2238 2239 return true; 2240 } 2241 2242 } 2243 2244 /** 2245 * Logs failed login attempts so PivotX can block brute force attacks. 2246 * 2247 */ 2248 function logFailedLogin() { 2249 global $PIVOTX; 2250 2251 $ip = $_SERVER['REMOTE_ADDR']; 2252 2253 2254 $this->logins['failed'][ $ip ] = array( 2255 'attempts' => $this->logins['failed'][ $ip ]['attempts'] + 1, 2256 'time' => mktime() 2257 ); 2258 2259 $this->saveLogins(); 2260 debug(sprintf(__("Failed login attempt from '%s'."), $_SERVER['REMOTE_ADDR'])); 2261 } 2262 2263 /** 2264 * Checks failed login attempts so PivotX can block brute force attacks. 2265 * 2266 */ 2267 function checkFailedLogins() { 2268 global $PIVOTX; 2269 2270 $limit = getDefault($PIVOTX['config']->get('failed_logins_limit'), 8); 2271 $ip = $_SERVER['REMOTE_ADDR']; 2272 2273 if ($this->logins['failed'][ $ip ]['attempts'] > $limit) { 2274 return false; 2275 } else { 2276 return true; 2277 } 2278 } 2279 2280 /** 2281 * Log out a user: clear the session, and delete the cookie 2282 * 2283 */ 2284 function logout() { 2285 global $PIVOTX; 2286 2287 $this->loadPermsessions(); 2288 2289 // remove current session (by username, so if the user logs out on 2290 // one location, he logs out everywhere).. 2291 foreach ($this->permsessions as $key => $session) { 2292 if ($session['username']==$_SESSION['user']['username']) { 2293 unset($this->permsessions[$key]); 2294 } 2295 } 2296 2297 $PIVOTX['events']->add('logout'); 2298 $this->savePermsessions(); 2299 2300 // End the session.. 2301 unset($_SESSION['user']); 2302 $this->setCookie($this->cookie_name, '', time()-10000 ); 2303 2304 session_destroy(); 2305 2306 } 2307 2308 /** 2309 * Returns the latest/current message. 2310 * 2311 * @return array 2312 */ 2313 function getMessage() { 2314 2315 return $this->message; 2316 2317 } 2318 2319 /** 2320 * Returns the current user. 2321 * 2322 * @return array 2323 */ 2324 function currentUser() { 2325 2326 return $_SESSION['user']; 2327 2328 } 2329 2330 2331 /** 2332 * Sets the specifics for the current user.. 2333 * 2334 * @param array $user 2335 */ 2336 function setUser($user) { 2337 2338 $_SESSION['user'] = $user; 2339 2340 } 2341 2342 2343 /** 2344 * Returns the username of the current user. 2345 * 2346 * @return array 2347 */ 2348 function currentUsername() { 2349 2350 return $_SESSION['user']['username']; 2351 2352 } 2353 2354 2355 /** 2356 * Checks if the currently logged in user has at least the required level 2357 * to view the page he/she is trying to access. 2358 * 2359 * If not, the user is logged out of the system. 2360 * 2361 * @param int $level 2362 */ 2363 function minLevel($level) { 2364 global $PIVOTX; 2365 2366 $this->isLoggedIn(); 2367 2368 if ($level>$_SESSION['user']['userlevel']) { 2369 debug("logged out because the user's level was too low, or not logged in at all"); 2370 2371 // If $PIVOTX['paths'] is set and the headers aren't sent yet, redirect 2372 // to the login page, via the logout page. 2373 if (!empty($PIVOTX['paths']['pivotx_url']) && !headers_sent() ) { 2374 header("Location: ". $PIVOTX['paths']['pivotx_url']."?page=logout"); 2375 } else { 2376 // otherwise, just display it, as good as we can. 2377 pageLogout(); 2378 } 2379 die(); 2380 } 2381 2382 } 2383 2384 2385 /** 2386 * Checks if the current request is accompanied by the correct 2387 * CSRF check. 2388 * 2389 * If not, the user is logged out of the system. 2390 * 2391 * @param int $value 2392 */ 2393 function checkCSRF($value) { 2394 2395 if ($value != $_SESSION['pivotxsession']) { 2396 debug( sprintf("CSRF check failed: '%s..' vs. '%s..'", 2397 substr($value,0,8), substr($_SESSION['pivotxsession'],0,8) )); 2398 pageLogout(); 2399 die(); 2400 } 2401 2402 } 2403 2404 /** 2405 * Get the key to use in the CSRF checks. 2406 * 2407 */ 2408 function getCSRF() { 2409 2410 return $_SESSION['pivotxsession']; 2411 2412 } 2413 2414 2415 /** 2416 * Save permanent sessions to the filesystem, for users that check 'keep 2417 * me logged in'. 2418 * 2419 * The sessions are saved in db/ser_sessions.php, and they look somewhat like 2420 * Array 2421 * ( 2422 * [8nkvr62i3s37] => Array 2423 * ( 2424 * [username] => admin 2425 * [ip] => 127.0.0.1 2426 * [lastseen] => 1168177821 2427 * ) 2428 * ) 2429 * 2430 */ 2431 function savePermsessions() { 2432 global $PIVOTX; 2433 2434 saveSerialize($PIVOTX['paths']['db_path'] . "ser_sessions.php", $this->permsessions); 2435 2436 } 2437 2438 2439 /** 2440 * Load the permanent sessions from the filesystem. 2441 * 2442 */ 2443 function loadPermsessions() { 2444 global $PIVOTX; 2445 2446 $this->permsessions = loadSerialize($PIVOTX['paths']['db_path'] . "ser_sessions.php", true); 2447 2448 // Remove stale sessions after loading. 2449 foreach ($this->permsessions as $key=>$session) { 2450 if(($session['lastseen']+ $this->cookie_lifespan) < time() ) { 2451 unset($this->permsessions[$key]); 2452 } 2453 } 2454 2455 } 2456 2457 /** 2458 * Save login attempts from the filesystem. 2459 */ 2460 function saveLogins() { 2461 global $PIVOTX; 2462 2463 // Trim the logins log, if it's too long. 2464 if (count($this->logins['failed']) > $this->maxlogins) { 2465 $this->logins['failed'] = array_slice($this->logins['failed'], -$this->maxlogins); 2466 } 2467 if (count($this->logins['succeeded']) > $this->maxlogins) { 2468 $this->logins['succeeded'] = array_slice($this->logins['succeeded'], -$this->maxlogins); 2469 } 2470 2471 saveSerialize($PIVOTX['paths']['db_path'] . "ser_logins.php", $this->logins); 2472 2473 } 2474 2475 2476 /** 2477 * Load stored login attempts from the filesystem. 2478 */ 2479 function loadLogins() { 2480 global $PIVOTX; 2481 2482 $timeout = getDefault($PIVOTX['config']->get('failed_logins_timeout'), 24); 2483 2484 $this->logins = loadSerialize($PIVOTX['paths']['db_path'] . "ser_logins.php", true); 2485 2486 // Set timeout to the timestamp at which the block needs to be dropped. 2487 $timeout = mktime() - ($timeout*3600); 2488 2489 // Iterate over the failed attempts, to see if they need to be dropped. 2490 foreach ($this->logins['failed'] as $ip => $item) { 2491 if ($item['time']<$timeout) { 2492 unset($this->logins['failed'][$ip]); 2493 } 2494 } 2495 2496 } 2497 2498 2499 /** 2500 * Sets a session value, and then saves it. 2501 * 2502 * @param string $key 2503 * @param unknown_type $value 2504 */ 2505 function setValue($key, $value=false) { 2506 if($value) { 2507 $_SESSION[$key] = $value; 2508 } else { 2509 unset($_SESSION[$key]); 2510 2511 } 2512 2513 } 2514 2515 2516 /** 2517 * Gets a single session value 2518 * 2519 * @param string $key 2520 * @return string 2521 */ 2522 function getValue($key) { 2523 return $_SESSION[$key]; 2524 2525 } 2526 } 2527 2528 2529 2530 2531 /** 2532 * This class deals with Pages. 2533 * 2534 */ 2535 class Pages { 2536 2537 var $index; 2538 var $currentpage; 2539 2540 /** 2541 * Initialisation 2542 * 2543 * @return Pages 2544 */ 2545 function Pages() { 2546 global $PIVOTX; 2547 2548 if ($PIVOTX['config']->get('db_model')=="flat") { 2549 require_once ("modules/pages_flat.php"); 2550 $this->db = new PagesFlat(); 2551 } else if ( ($PIVOTX['config']->get('db_model')=="mysql") || 2552 ($PIVOTX['config']->get('db_model')=="sqlite") || 2553 ($PIVOTX['config']->get('db_model')=="postgresql") ) { 2554 require_once ("modules/pages_sql.php"); 2555 $this->db = new PagesSql(); 2556 } else { 2557 // TODO: In case of a fatal error, we should give the user the chance to reset the 2558 // Config to the default state, and try again. 2559 die("Unknown DB Model! PivotX can not continue!"); 2560 } 2561 2562 $this->currentpage = array(); 2563 2564 $this->getIndex(); 2565 2566 } 2567 2568 /** 2569 * Get the index of the available chapters and pages. 2570 * 2571 * @return array 2572 */ 2573 function getIndex($excerpts=false, $links=false) { 2574 global $PIVOTX; 2575 2576 $filteruser = ""; 2577 2578 // Check if we need to filter for a user, based on the 'show_only_own_userlevel' 2579 // settings.. We do this only when not rendering a weblog, otherwise the 2580 // pages that are filtered out won't show up on the site. 2581 if (!defined('PIVOTX_INWEBLOG')) { 2582 $currentuser = $PIVOTX['users']->getUser( $PIVOTX['session']->currentUsername() ); 2583 $currentuserlevel = (!$currentuser?1:$currentuser['userlevel']); 2584 2585 if ( $currentuserlevel <= $PIVOTX['config']->get('show_only_own_userlevel') ) { 2586 $filteruser = $currentuser['username']; 2587 } 2588 } 2589 2590 $this->index = $this->db->getIndex($filteruser, $excerpts, $links); 2591 2592 return $this->index; 2593 2594 } 2595 2596 /** 2597 * Get the information for a specific Chapter 2598 * 2599 * @param integer $id 2600 * @return array 2601 */ 2602 function getChapter($id) { 2603 2604 return $this->index[$id]; 2605 2606 } 2607 2608 /** 2609 * Add a chapter, and save the index 2610 * 2611 * @param array $chapter 2612 */ 2613 function addChapter($chapter) { 2614 2615 $this->index = $this->db->addChapter($chapter); 2616 2617 $this->saveIndex(false); 2618 2619 } 2620 2621 2622 /** 2623 * Delete a chapter, and save the index 2624 * 2625 * @param integer $uid 2626 */ 2627 function delChapter($uid) { 2628 2629 $this->index = $this->db->delChapter($uid); 2630 2631 $this->saveIndex(false); 2632 2633 } 2634 2635 2636 /** 2637 * Update the information for a chapter, and save the index 2638 * 2639 * @param integer $uid 2640 * @param array $chapter 2641 */ 2642 function updateChapter($uid,$chapter) { 2643 2644 $this->index = $this->db->updateChapter($uid,$chapter); 2645 2646 $this->saveIndex(false); 2647 } 2648 2649 2650 /** 2651 * Save the index to the DB, using the selected model. 2652 * 2653 * @param boolean $reindex 2654 */ 2655 function saveIndex($reindex=true) { 2656 2657 uasort($this->index, array($this, 'chapSort')); 2658 2659 $this->db->setIndex($this->index); 2660 2661 $this->db->saveIndex($reindex); 2662 2663 } 2664 2665 2666 /** 2667 * Get a single page 2668 * 2669 * @param integer $uid 2670 * @return array 2671 */ 2672 function getPage($uid, $language=false) { 2673 2674 $page = $this->db->getPage($uid,$language); 2675 2676 $this->currentpage = $page; 2677 2678 return $page; 2679 2680 } 2681 2682 /** 2683 * Get a single page in all languages 2684 * 2685 * @param integer $uid 2686 * @return array 2687 */ 2688 function getPageInAllLanguages($uid) { 2689 2690 if (method_exists($this->db,'getPageInAllLanguages')) { 2691 $page = $this->db->getPageInAllLanguages($uid); 2692 } 2693 else { 2694 $page = $this->db->getPage($uid); 2695 } 2696 2697 $this->currentpage = $page; 2698 2699 return $page; 2700 2701 } 2702 2703 /** 2704 * Get a single page, as defined by its URI 2705 * 2706 * @param string $uri 2707 * @return array 2708 */ 2709 function getPageByUri($uri,$language=false) { 2710 2711 $page = $this->db->getPageByUri($uri,$language); 2712 2713 $this->currentpage = $page; 2714 2715 return $page; 2716 2717 } 2718 2719 /** 2720 * Gets the current page 2721 */ 2722 function getCurrentPage() { 2723 2724 return $this->currentpage; 2725 2726 } 2727 2728 /** 2729 * Gets the $amount latest pages as an array, suitable for displaying an 2730 * overview 2731 * 2732 * @param integer $amount 2733 */ 2734 function getLatestPages($amount, $filter_user="") { 2735 2736 $pages = $this->db->getLatestPages($amount, $filter_user); 2737 2738 return $pages; 2739 2740 } 2741 2742 /** 2743 * Delete a single page 2744 * 2745 * @param integer $uid 2746 */ 2747 function delPage($uid) { 2748 2749 $this->db->delPage($uid); 2750 2751 } 2752 2753 2754 /** 2755 * Save a single page. Returns the uid of the inserted page. 2756 * 2757 * @param array $page 2758 * @return integer. 2759 */ 2760 function savePage($page) { 2761 2762 $this->currentpage = $page; 2763 2764 if (method_exists($this->db,'savePageInAllLanguages')) { 2765 return $this->db->savePageInAllLanguages($page); 2766 } 2767 2768 return $this->db->savePage($page); 2769 2770 2771 } 2772 2773 /** 2774 * Sort the chapters based on the order and string comparison 2775 * of chapter name if order is identical. 2776 * 2777 * @param array $a 2778 * @param array $b 2779 * @return int 2780 */ 2781 function chapSort($a, $b) { 2782 global $PIVOTX; 2783 2784 if ($PIVOTX['config']->get('sort_chapters_by_alphabet')==true) { 2785 // If we set 'sort_chapters_by_alphabet' to true, always sort by alphabet.. 2786 return strcmp($a['chaptername'],$b['chaptername']); 2787 } else if ($a['sortorder'] == $b['sortorder']) { 2788 // Else sort by alphabet, if order is the same.. 2789 return strcmp($a['chaptername'],$b['chaptername']); 2790 } else { 2791 // else sort by order.. 2792 return ($a['sortorder'] < $b['sortorder']) ? -1 : 1; 2793 } 2794 2795 } 2796 2797 2798 /** 2799 * Checks if any pages set to 'timed publish' should be published. 2800 * 2801 */ 2802 function checkTimedPublish() { 2803 $this->db->checkTimedPublish(); 2804 } 2805 2806 } 2807 2808 2809 2810 /** 2811 * The class that does the work for the paging and paging_subweblog snippets. 2812 * 2813 * @author Hans Fredrik Nordhaug <hansfn@gmail.com>, The PivotX dev. Team. 2814 */ 2815 class Paging { 2816 var $offset; 2817 var $name; 2818 2819 function Paging($name) { 2820 $this->name = $name; 2821 } 2822 2823 function sanity_check($action) { 2824 global $PIVOTX; 2825 list($action,$dummy) = explode('|',$action); 2826 if (($action != "next") && ($action != "prev") && 2827 ($action != "curr") && ($action != "digg")) { 2828 return "<!-- snippet {$this->name} error: unknow action '$action' -->\n"; 2829 } 2830 2831 // Only display the paging snippet on weblog pages 2832 $modifier = $PIVOTX['parser']->get('modifier'); 2833 if (($modifier['action'] != 'weblog') || !empty($modifier['archive'])) { 2834 return "<!-- snippet {$this->name} ($action): only output on weblog pages -->\n"; 2835 } 2836 return; 2837 } 2838 2839 function setup() { 2840 // Determine the offset 2841 if (!isset($_GET['o'])) { 2842 $this->offset = 0; 2843 } elseif (is_numeric($_GET['o'])) { 2844 $this->offset = $_GET['o']; 2845 } else { 2846 return "<!-- snippet {$this->name} error: offset isn't numeric -->\n"; 2847 } 2848 2849 2850 return; 2851 } 2852 2853 2854 function doit($action, $text, $cats, $amountperpage, $params) { 2855 global $PIVOTX; 2856 2857 $Current_weblog = $PIVOTX['weblogs']->getCurrent(); 2858 $modifier = $PIVOTX['parser']->get('modifier'); 2859 2860 // $amountperpage must be numeric, one or larger 2861 if (!is_numeric($amountperpage) || ($amountperpage<1)) { 2862 return "<!-- snippet {$this->name} error: invalid number of entries to skip ($amountperpage) -->\n"; 2863 } 2864 2865 // Preserving some query parameters 2866 $query = array(); 2867 if (isset($_GET['w']) && (empty($_GET['rewrite']) || ($_GET['rewrite'] == 'offset'))) { 2868 $query['w'] = 'w=' . $_GET['w']; 2869 } 2870 if (isset($_GET['t'])) { 2871 $query['t'] = 't=' . $_GET['t']; 2872 } 2873 if (!empty($_GET['u'])) { 2874 $query['u'] = 'u=' . $_GET['u']; 2875 } 2876 2877 // Setting the text for the links 2878 if ($action == "next") { 2879 $text = getDefault($params['format'], __("Next page")." »" ); 2880 } elseif ($action == "prev") { 2881 $text = getDefault($params['format'], "« ".__("Previous page")); 2882 } elseif ($action == "digg") { 2883 $text_prev = getDefault($params['format_prev'], "« ".__("Previous page")); 2884 $text_next = getDefault($params['format_next'], __("Next page")." »" ); 2885 } else { 2886 $text = getDefault($params['format'], __("Displaying entries %num_from%-%num_to% of %num_tot%") ); 2887 } 2888 2889 // Get the maximum amount of pages to show. 2890 $max_digg_pages = getDefault($params['maxpages'], 9); 2891 2892 // Get the id to attach to the <ul> for Digg style navigation. 2893 $digg_id = getDefault($params['id'], "pages"); 2894 2895 // Start the real work. 2896 $eachcatshash = md5(implodeDeep("", $cats)); 2897 2898 if ($PIVOTX['cache']->get('paging', $eachcatshash)) { 2899 // Check if this is in our simple cache? 2900 list($temp_tot, $num_tot) = $PIVOTX['cache']->get('paging', $eachcatshash); 2901 } else { 2902 2903 // Get the total amount of entries. How we do this depends on the used DB-model.. 2904 // What we do is we get the amount of entries for each item in $cats. 2905 // For example, let's say we have 10 entries per page and 90 entries in one subweblog, and 2906 // 65 in the other. In this case we don't need (90+65)/10 pages, but (max(90,65))/10 pages. 2907 if ($PIVOTX['db']->db_type == "flat" ) { 2908 // Get the amount from the Flat files DB.. 2909 $tot = $PIVOTX['db']->get_entries_count(); 2910 foreach ($cats as $eachcats) { 2911 if (!is_array($eachcats) && (trim($eachcats) == '')) { 2912 continue; 2913 } 2914 $temp_tot = count($PIVOTX['db']->read_entries(array( 2915 'show'=>$tot, 'cats'=>$eachcats, 'user'=>$_GET['u'], 'status'=>'publish'))); 2916 $num_tot = max( $num_tot, $temp_tot); 2917 } 2918 } else { 2919 // Get the amount from our SQL db.. 2920 $sql = new Sql('mysql', $PIVOTX['config']->get('db_databasename'), $PIVOTX['config']->get('db_hostname'), 2921 $PIVOTX['config']->get('db_username'), $PIVOTX['config']->get('db_password')); 2922 $entriestable = safeString($PIVOTX['config']->get('db_prefix')."entries", true); 2923 $categoriestable = safeString($PIVOTX['config']->get('db_prefix')."categories", true); 2924 2925 foreach ($cats as $eachcats) { 2926 if (is_array($eachcats)) { 2927 $eachcats = implode("','", $eachcats); 2928 } else if (trim($eachcats) == '') { 2929 continue; 2930 } 2931 $sql->query("SELECT COUNT(DISTINCT(e.uid)) FROM $entriestable AS e, $categoriestable as c 2932 WHERE e.status='publish' AND e.uid=c.target_uid AND c.category IN ('$eachcats');"); 2933 $temp_tot = current($sql->fetch_row()); 2934 $num_tot = max( $num_tot, $temp_tot); 2935 } 2936 } 2937 $PIVOTX['cache']->set('paging', $eachcatshash, array($temp_tot, $num_tot)); 2938 } 2939 2940 $offset = intval($modifier['offset']); 2941 $num_pages = ceil($num_tot / $amountperpage); 2942 2943 if ($num_tot == 0) { 2944 return "<!-- snippet {$this->name}: no entries -->\n"; 2945 } elseif ($offset >= $num_pages) { 2946 return "<!-- snippet {$this->name}: no more entries -->\n"; 2947 } 2948 2949 if ($action == "next") { 2950 2951 $offset++; 2952 2953 if ($offset >= $num_pages) { 2954 return "<!-- snippet {$this->name} (next): no more entries -->\n"; 2955 } 2956 2957 } elseif ($action == "prev") { 2958 2959 if ($offset == 0) { 2960 return "<!-- snippet {$this->name} (previous): no previous entries -->\n"; 2961 } else { 2962 $offset--; 2963 } 2964 2965 } else { 2966 if ($num_tot == 0) { 2967 return "<!-- snippet {$this->name} (curr): no current entries -->\n"; 2968 } else { 2969 $num = min($num,$num_tot); 2970 } 2971 2972 } 2973 2974 $num_from = $offset * $amountperpage + 1; 2975 $num_to = min($num_tot, ($offset+1) * $amountperpage); 2976 2977 $text = str_replace("%num%", min($num_tot, $amountperpage), $text); 2978 $text = str_replace("%num_tot%", $num_tot, $text); 2979 $text = str_replace("%num_from%", $num_from, $text); 2980 $text = str_replace("%num_to%", $num_to, $text); 2981 2982 if ($action == "curr") { 2983 return $text; 2984 } 2985 2986 $site_url = getDefault($PIVOTX['weblogs']->get($Current_weblog, 'site_url'), $PIVOTX['paths']['site_url']); 2987 2988 if ( (!empty($modifier['category']) || $params['catsinlink']==true) && $params['category']!="*" ) { 2989 // Ensure that we get a sorted list of unique categories in 2990 // the URL - better SEO, one unique URL. 2991 $catslink = implodeDeep(",",$cats); 2992 $catslink = array_unique(explode(",",$catslink)); 2993 sort($catslink, SORT_STRING); 2994 $catslink = implode(",",$catslink); 2995 } 2996 2997 if ($PIVOTX['config']->get('mod_rewrite')==0) { 2998 if ( (!empty($modifier['category']) || $params['catsinlink']==true) && $params['category']!="*" ) { 2999 $link = $site_url . "?c=" . $catslink . "&o="; 3000 } else { 3001 $link = $site_url . "?o="; 3002 } 3003 } else { 3004 if ( (!empty($modifier['category']) || $params['catsinlink']==true) && $params['category']!="*" ) { 3005 $categoryname = getDefault( $PIVOTX['config']->get('localised_category_prefix'), "category"); 3006 $link = $site_url . $categoryname . "/" . $catslink . "/"; 3007 } else { 3008 $pagesname = getDefault( $PIVOTX['config']->get('localised_browse_prefix'), "browse"); 3009 $link = $site_url. $pagesname . "/"; 3010 } 3011 } 3012 3013 3014 if ($action == 'digg') { 3015 $link .= '%offset%'; 3016 } else { 3017 $link .= $offset; 3018 } 3019 3020 if (!isset($query['w']) && paraWeblogNeeded($Current_weblog)) { 3021 if ($PIVOTX['config']->get('mod_rewrite')==0) { 3022 $link .= "&w=".para_weblog($Current_weblog); 3023 } else { 3024 $link .= "/".para_weblog($Current_weblog); 3025 } 3026 } 3027 3028 // Add the query parameters (if any) 3029 if (count($query) > 0) { 3030 $query = implode('&', $query); 3031 if ($PIVOTX['config']->get('mod_rewrite')==0) { 3032 $link .= '&' . $query; 3033 } else { 3034 $link .= '?' . $query; 3035 } 3036 } 3037 3038 $link = str_replace(array('"',"'"), "", $link); 3039 3040 if ($action != 'digg') { 3041 3042 // Perhaps add some extra attributes to the <a> tag 3043 $extra = ""; 3044 if (!empty($params['target'])) { $extra .= " target='" . $params['target'] ."'"; } 3045 if (!empty($params['class'])) { $extra .= " class='" . $params['class'] ."'"; } 3046 if (!empty($params['id'])) { $extra .= " id='" . $params['id'] ."'"; } 3047 if (!empty($params['datarole'])) { $extra .= " data-role='" . $params['datarole'] ."'"; } 3048 3049 $output = sprintf('<a href="%s" %s>%s</a>', $link, $extra, $text); 3050 3051 return $output; 3052 3053 } else { 3054 3055 $output =" 3056 <div id=\"{$digg_id}\"> 3057 <ul> 3058 %links% 3059 </ul> 3060 </div>"; 3061 $links = ''; 3062 3063 // Adding the previous link 3064 if ($offset == 0) { 3065 $links .= '<li class="nolink">%text_prev%</li>'; 3066 } else { 3067 $links .= '<li><a href="%url%">%text_prev%</a></li>'; 3068 3069 $url = str_replace('%offset%',max(0,$offset-1),$link); 3070 $links = str_replace('%url%',$url,$links); 3071 } 3072 3073 if ($num_pages > $max_digg_pages ) { 3074 // Limit the number of links/listed pages. 3075 3076 $max_digg_pages = intval($max_digg_pages); 3077 3078 $start = (int) ($offset - 0.5 * ($max_digg_pages-1)); 3079 $start = max(0,$start) + 1; 3080 $stop = (int) ($offset + 0.5 * ($max_digg_pages-1)); 3081 $stop = max(min(1000,$stop),3); 3082 $page = $offset; 3083 3084 if ($offset==0) { 3085 $links .= '<li class="current">1</li>'; 3086 } else if ($start>=1) { 3087 $links .= '<li><a href="%url%">1</a></li>'; 3088 if ($start>=2) { 3089 $links .= '<li class="skip">…</li>'; 3090 } 3091 $url = str_replace('%offset%',0,$link); 3092 $links = str_replace('%url%',$url,$links); 3093 } 3094 } else { 3095 // Display all links/listed pages. 3096 $start = 0; 3097 $stop = 100; 3098 } 3099 3100 3101 // Adding all links before the current page 3102 while ($start < $offset) { 3103 $links .= '<li><a href="%url%">'.($start+1).'</a></li>'; 3104 $url = str_replace('%offset%', $start, $link); 3105 $links = str_replace('%url%', $url, $links); 3106 $start++; 3107 } 3108 3109 // Current page.. 3110 if ($start == $offset) { 3111 $links .= '<li class="current">' . ($start+1) . '</li>'; 3112 $start++; 3113 } 3114 3115 // Adding all links after the current page 3116 while ($start < $num_pages) { 3117 if ($start < $stop) { 3118 $links .= '<li><a href="%url%">'.($start+1).'</a></li>'; 3119 $url = str_replace('%offset%', $start, $link); 3120 $links = str_replace('%url%', $url, $links); 3121 } else if ($start == ($num_pages-2) ) { 3122 $links .= '<li class="skip">…</li>'; 3123 } else if ($start == ($num_pages-1) ) { 3124 $links .= '<li><a href="%url%">'.($start+1).'</a></li>'; 3125 $url = str_replace('%offset%', $start, $link); 3126 $links = str_replace('%url%', $url, $links); 3127 } 3128 $page++; 3129 $start++; 3130 } 3131 3132 3133 // Adding the next link 3134 if ( ($offset+1) >= $num_pages) { 3135 $links .= '<li class="nolink">%text_next%</li>'; 3136 } else { 3137 $links .= '<li><a href="%url%">%text_next%</a></li>'; 3138 $url = str_replace('%offset%', $offset + 1, $link); 3139 $links = str_replace('%url%', $url, $links); 3140 } 3141 $output = str_replace('%links%', $links, $output); 3142 $output = str_replace('%text_prev%', $text_prev, $output); 3143 $output = str_replace('%text_next%', $text_next, $output); 3144 return $output; 3145 } 3146 } 3147 3148 } 3149 3150 3151 /** 3152 * A Class that provides for very simple, in-memory caching. 3153 * 3154 * @author Bob, The PivotX dev. Team. 3155 */ 3156 class Simplecache { 3157 3158 var $cache; 3159 var $stats; 3160 var $keepstats; 3161 var $itemlimit; 3162 var $memlimit; 3163 3164 function SimpleCache() { 3165 global $PIVOTX; 3166 3167 $this->cache = array(); 3168 $this->stats = array( 3169 'hits' => 0, 3170 'misses' => 0, 3171 'items' => 0, 3172 'size' => 0, 3173 'flushed' => 0 3174 ); 3175 3176 // Set the maximum number of items in the simple cache. 3177 $this->itemlimit = 400; 3178 3179 // Set the maximum amount of memory available. 3180 $this->memlimit = ini_get('memory_limit'); 3181 list($this->memlimit) = sscanf($this->memlimit, "%dM"); 3182 if (!empty($this->memlimit)) { 3183 $this->memlimit = $this->memlimit * 1048576; 3184 } else { 3185 $this->memlimit = 64 * 1048576; 3186 } 3187 3188 $this->keepstats = $PIVOTX['config']->get('debug_cachestats'); 3189 3190 } 3191 3192 /** 3193 * Set a single item in the cache 3194 * 3195 * @param string $type 3196 * @param string $key 3197 * @param mixed $value 3198 * @return bool 3199 */ 3200 function set($type="general", $key, $value) { 3201 3202 // Check if the $type and $key are OK 3203 if (empty($key) || (!is_string($key) && !is_integer($key) ) || !is_string($type)) { 3204 // debug("Not Set: $type - $key"); 3205 return false; 3206 } 3207 3208 if ($this->keepstats) { 3209 $this->stats['sets'][$type][$key]++; 3210 } 3211 3212 if (!isset($this->cache[$type][$key])) { 3213 $this->stats['items']++; 3214 } 3215 3216 $this->cache[$type][$key] = $value; 3217 3218 $this->trim(); 3219 3220 return true; 3221 3222 } 3223 3224 /** 3225 * Set multiple items in the cache 3226 * 3227 * @param string $type 3228 * @param array $values 3229 * @return bool 3230 */ 3231 function setMultiple($type="general", $values) { 3232 3233 // Check if the $type and $key are OK 3234 if (empty($values) || !is_array($values) || !is_string($type)) { 3235 return false; 3236 } 3237 3238 foreach($values as $key=>$value) { 3239 $this->set($type, $key, $value); 3240 } 3241 3242 return true; 3243 3244 } 3245 3246 /** 3247 * Get a single item from the cache. Returns the value on success, or false 3248 * when it's a miss. So, storing booleans in the cache isn't very convenient. 3249 * 3250 * @param string $type 3251 * @param string $key 3252 * @return mixed 3253 */ 3254 function get($type="general", $key) { 3255 3256 if ($this->keepstats) { 3257 $this->stats['gets'][$type][$key]++; 3258 } 3259 3260 if (!empty($this->cache[$type][$key])) { 3261 // debug("Get(hit): $type - $key"); 3262 $this->stats['hits']++; 3263 return $this->cache[$type][$key]; 3264 } else { 3265 // debug("Get(miss): $type - $key"); 3266 $this->stats['misses']++; 3267 return false; 3268 } 3269 3270 } 3271 3272 3273 /** 3274 * Trims the cache, if it's getting too large. 3275 */ 3276 function trim() { 3277 3278 // Get the percentage of used memory.. 3279 if (function_exists('memory_get_usage')) { 3280 $mem = memory_get_usage(); 3281 } 3282 3283 if (!empty($mem) ) { 3284 $percentage = $mem / $this->memlimit; 3285 } else { 3286 $percentage = 0; 3287 } 3288 3289 // check if we need to trim items. 3290 if ( ($this->stats['items'] > $this->itemlimit) || ($percentage > 0.8) ) { 3291 3292 reset($this->cache); 3293 3294 // Remove one item from each cached type. 3295 // Note: we don't use foreach, because it uses more memory, which is 3296 // exactly what we don't want, if we have to trim the cache.. 3297 while ($key = key($this->cache)) { 3298 3299 if ($this->keepstats) { 3300 debug('Simple cache flush: $key - ' . key($this->cache[$key]) ); 3301 } 3302 3303 array_shift($this->cache[$key]); 3304 $this->stats['items']--; 3305 $this->stats['flushed']++; 3306 3307 next($this->cache); 3308 } 3309 3310 3311 } 3312 3313 } 3314 3315 /** 3316 * Return some basic statistics for the cache.. 3317 * 3318 * @return array 3319 */ 3320 function stats() { 3321 3322 $this->stats['size'] = strlen(serialize($this->cache)); 3323 3324 return $this->stats; 3325 3326 } 3327 3328 function clear() { 3329 $this->cache = array(); 3330 } 3331 3332 } 3333 3334 class Minify { 3335 3336 var $html; 3337 var $head; 3338 var $jsfiles; 3339 var $cssfiles; 3340 var $base; 3341 3342 function Minify($html) { 3343 global $PIVOTX; 3344 3345 $this->html = $html; 3346 3347 // Set the base path.. 3348 if (defined('PIVOTX_INWEBLOG')) { 3349 $this->base = $PIVOTX['paths']['site_url']; 3350 } else { 3351 $this->base = $PIVOTX['paths']['pivotx_url']; 3352 } 3353 3354 } 3355 3356 function minifyURLS() { 3357 3358 // if the PHP version is too low, we return the HTML, without doing anything. 3359 if (!checkVersion(phpversion(), '5.1.6')) { 3360 debug('PHPversion too low to us Minify: ' . phpversion() ); 3361 return $this->html; 3362 } 3363 3364 $head = $this->_getHead(); 3365 3366 if (empty($this->head)) { 3367 debug("Couldn't find a <head> section to minify"); 3368 } else { 3369 $this->_getScripts(); 3370 $this->_minifyScriptURLs(); 3371 } 3372 3373 $this->_getStylesheets(); 3374 $this->_minifyStylesheetURLs(); 3375 3376 return $this->html; 3377 } 3378 3379 /** 3380 * Get the head section. 3381 **/ 3382 function _getHead() { 3383 3384 preg_match("/<head([^>]+)?".">.*?<\/head>/is", $this->html, $matches); 3385 3386 if(!empty($matches[0])) { 3387 3388 $head = $matches[0]; 3389 3390 // Pull out the comment blocks, so as to avoid touching conditional comments 3391 $head = preg_replace("/<!-- .*? -->/is", '', $head); 3392 3393 } else { 3394 $head = ""; 3395 } 3396 3397 $this->head = $head; 3398 3399 } 3400 3401 3402 /** 3403 * Get the scripts from the head section. 3404 **/ 3405 function _getScripts() { 3406 3407 $scripts = array(); 3408 3409 $regex = "/<script[^>]+type=(['\"])(text\/javascript)\\1([^>]+)?".">(.*)<\/script>/iUs"; 3410 preg_match_all($regex, $this->head, $matches); 3411 3412 3413 if (!empty($matches[0])) { 3414 3415 3416 $scripts = $matches[0]; 3417 3418 // remove 'inline' js, and links to external resources.. 3419 // We also skip files with an '?', because they have extra paremeters, indicating 3420 // that they are generated, so we shouldn't minify them. 3421 foreach ($scripts as $key => $script) { 3422 preg_match('/src=([\'"])(.*)\1/iUs', $script, $res); 3423 3424 $res = $res[2]; 3425 $ext = getExtension($res); 3426 3427 if ( empty($res) || ($ext!="js") || (strpos($res, "ttp://")==1) || (strpos($res, "ttps://")==1) || (strpos($res, "?")>0) ) { 3428 unset($scripts[$key]); 3429 continue; 3430 } 3431 3432 3433 } 3434 3435 } 3436 3437 $this->jsfiles = $scripts; 3438 3439 } 3440 3441 /** 3442 * convert the found js files into one minify-link.. 3443 */ 3444 function _minifyScriptURLs() { 3445 global $PIVOTX; 3446 3447 $sources = array(); 3448 3449 foreach ($this->jsfiles as $jsfile) { 3450 preg_match('/src=([\'"])(.*)\1/iUs', $jsfile, $res); 3451 3452 $res = $res[2]; 3453 // Add file paths to relative URLs.. 3454 if (strpos($res, "/") !== 0) { 3455 $res = $this->base . $res; 3456 } 3457 $source = preg_replace('#'.$PIVOTX['paths']['site_url'].'#', '', $res, 1); 3458 // Only add a source once 3459 if (!in_array($source, $sources)) { 3460 $sources[] = $source; 3461 } 3462 } 3463 3464 3465 if (!empty($sources)) { 3466 3467 $minifylink = sprintf("<scr"."ipt type=\"text/javascript\" src=\"%sincludes/minify/?f=%s\"></scr"."ipt>" , 3468 $PIVOTX['paths']['pivotx_url'], 3469 implode(",", $sources) 3470 ); 3471 3472 // Replace the first link to PivotX JS file with the minify link and remove 3473 // all other links to PivotX JS files: 3474 $this->html = str_replace(array_shift($this->jsfiles), $minifylink, $this->html); 3475 $this->html = str_replace($this->jsfiles, "", $this->html); 3476 3477 } 3478 3479 } 3480 3481 3482 /** 3483 * Get the stylesheets from the entire document. 3484 **/ 3485 function _getStylesheets() { 3486 3487 $stylesheets = array(); 3488 3489 $regex = "/<link[^>]+text\/css[^>]+>/iUs"; 3490 preg_match_all($regex, $this->html, $matches); 3491 3492 if (!empty($matches[0])) { 3493 3494 // remove links to external resources, and organize by 'media' type.. 3495 foreach ($matches[0] as $key => $stylesheet) { 3496 preg_match('/href=[\'"](.*)[\'"]/iUs', $stylesheet, $res); 3497 3498 $href = $res[1]; 3499 $ext = getExtension($href); 3500 3501 // We also skip files with an '?', because they have extra paremeters, indicating 3502 // that they are generated, so we shouldn't minify them. 3503 if ( empty($href) || ($ext!="css") || (strpos($href, "ttp://")==1) || (strpos($href, "ttps://")==1) || (strpos($href, "?")>0) ) { 3504 continue; 3505 } 3506 3507 preg_match('/media=[\'"](.*)[\'"]/iUs', $stylesheet, $res); 3508 3509 $media = $res[1]; 3510 3511 if ( empty($media) || ($media=="screen") ) { 3512 $stylesheets['screen'][] = $stylesheet; 3513 } else { 3514 $stylesheets[$media][] = $stylesheet; 3515 } 3516 3517 } 3518 3519 } 3520 3521 $this->cssfiles = $stylesheets; 3522 3523 } 3524 3525 3526 /** 3527 * convert the found css files into one minify-link.. 3528 */ 3529 function _minifyStylesheetURLs() { 3530 global $PIVOTX; 3531 3532 // Loop for each separate mediatype.. 3533 foreach($this->cssfiles as $mediatype => $cssfiles) { 3534 3535 $sources = array(); 3536 3537 foreach ($cssfiles as $cssfile) { 3538 preg_match('/href=[\'"](.*)[\'"]/iUs', $cssfile, $res); 3539 3540 // Add file paths to relative URLs.. 3541 if (strpos($res[1], "/") !== 0) { 3542 $res[1] = $this->base . $res[1]; 3543 } 3544 $source = preg_replace('#'.$PIVOTX['paths']['site_url'].'#', '', $res[1], 1); 3545 // Only add a source once 3546 if (!in_array($source, $sources)) { 3547 $sources[] = $source; 3548 } 3549 3550 } 3551 3552 if (!empty($sources)) { 3553 3554 3555 $minifylink = sprintf('<link href="%sincludes/minify/?url=%s&f=%s" ' . 3556 ' rel="stylesheet" type="text/css" media="%s" />' , 3557 $PIVOTX['paths']['pivotx_url'], 3558 substr($PIVOTX['paths']['site_url'],0,strlen($PIVOTX['paths']['site_url'])-1), 3559 implode(",", $sources), 3560 $mediatype 3561 ); 3562 3563 // Replace the javascript links in the source with the minify-link: 3564 $this->html = str_replace($cssfiles[0], $minifylink, $this->html); 3565 3566 foreach($cssfiles as $cssfile) { 3567 $this->html = str_replace($cssfile, "", $this->html); 3568 } 3569 3570 } 3571 3572 } 3573 3574 } 3575 3576 /** 3577 * OutputSystem Filter Method 3578 */ 3579 public static function osFilter($html) 3580 { 3581 $minify = new Minify($html); 3582 return $minify->minifyURLS(); 3583 } 3584 } 3585 3586 3587 /** 3588 * Takes care of the systemwide events, such as "Mike logged in." or "Pablo changed 3589 * the config setting 'xxx'." 3590 * 3591 */ 3592 class Events { 3593 3594 var $data; 3595 var $filename; 3596 var $edit_timeout; 3597 var $maxevents; 3598 3599 function Events() { 3600 global $PIVOTX; 3601 3602 $this->filename = "ser_events.php"; 3603 3604 $this->edittimeout = 60; 3605 $this->maxevents = getDefault($PIVOTX['config']->get('eventlog_length'), 200); 3606 3607 $this->data = loadSerialize($PIVOTX['paths']['db_path'] . $this->filename, true); 3608 3609 // Make sure we have a proper $this->maxevents.. 3610 if (intval($this->maxevents) < 10) { 3611 $this->maxevents = 200; 3612 } 3613 3614 // Make sure $this->data is set. 3615 if (empty($this->data) || !is_array($this->data)) { 3616 $this->data = array(); 3617 } 3618 3619 } 3620 3621 function add($what, $uid, $extrainfo="") { 3622 global $PIVOTX; 3623 3624 $timestamp = formatDate("", "%year%-%month%-%day%-%hour24%-%minute%"); 3625 if (defined('PIVOTX_INADMIN') || defined('PIVOTX_INAJAXHELPER')) { 3626 $user = $PIVOTX['users']->getUser( $PIVOTX['session']->currentUsername() ); 3627 $username = $user['username']; 3628 } else { 3629 $username = __('A visitor'); 3630 } 3631 3632 $event = array($timestamp, $username, $what, $uid, $extrainfo); 3633 3634 array_push($this->data, $event); 3635 3636 $this->save(); 3637 3638 } 3639 3640 function save() { 3641 global $PIVOTX; 3642 3643 // Trim the event log, if it's too long. 3644 if (count($this->data) > ($this->maxevents+10)) { 3645 $this->data = array_slice($this->data, -$this->maxevents); 3646 } 3647 3648 saveSerialize($PIVOTX['paths']['db_path'] . $this->filename, $this->data); 3649 3650 } 3651 3652 3653 /** 3654 * Get the last $amount events.. 3655 */ 3656 function get($amount=8) { 3657 global $PIVOTX; 3658 3659 for ($i = count($this->data)-1; ($i>0 && $amount>0) ; $i-- ) { 3660 3661 $event = $this->data[$i]; 3662 3663 // If $event[3] holds more than one uid, implode it to a string for printing. 3664 if (is_array($event[3])) { 3665 $event[3] = implode(", ", $event[3]); 3666 } 3667 3668 $name = "<strong>" . $event[1] ."</strong>"; 3669 3670 $format = ""; 3671 3672 switch ($event[2]) { 3673 3674 case 'edit_entry': 3675 if (!$saved['entry'][$event[1]][$event[3]]) { 3676 $format = sprintf( __("%s started editing entry '%s'."), $name, $event[4] ); 3677 $saved['entry'][$event[1]][$event[3]] = true; 3678 } 3679 break; 3680 3681 case 'edit_page': 3682 if (!$saved['page'][$event[1]][$event[3]]) { 3683 $format = sprintf( __("%s started editing page '%s'."), $name, $event[4] ); 3684 $saved['page'][$event[1]][$event[3]] = true; 3685 } 3686 break; 3687 3688 case 'save_entry': 3689 $saved['entry'][$event[1]][$event[3]] = true; 3690 $format = sprintf( __("%s saved entry '%s'."), $name, $event[4] ); 3691 break; 3692 3693 case 'save_page': 3694 $saved['page'][$event[1]][$event[3]] = true; 3695 $format = sprintf( __("%s saved page '%s'."), $name, $event[4] ); 3696 break; 3697 3698 case 'login': 3699 $format = sprintf( __("%s logged in."), $name ); 3700 break; 3701 3702 case 'logout': 3703 $format = sprintf( __("%s logged out."), $name ); 3704 break; 3705 3706 case 'failed_login': 3707 $format = sprintf( __("Failed login attempt for '%s'."), $event[4] ); 3708 break; 3709 3710 case 'edit_config': 3711 $format = sprintf( __("%s edited the setting for '%s'."), $name, $event[3] ); 3712 break; 3713 3714 case 'add_weblog': 3715 $format = sprintf( __("%s added weblog '%s'."), $name, $event[4] ); 3716 break; 3717 3718 case 'edit_weblog': 3719 $format = sprintf( __("%s edited a weblog setting for '%s'."), $name, $event[4] ); 3720 break; 3721 3722 case 'delete_weblog': 3723 $format = sprintf( __("%s deleted weblog '%s'."), $name, $event[4] ); 3724 break; 3725 3726 case 'save_file': 3727 $format = sprintf( __("%s saved the file '%s'."), $name, $event[4] ); 3728 break; 3729 3730 case 'add_user': 3731 $format = sprintf( __("%s added user '%s'."), $name, $event[4] ); 3732 break; 3733 3734 case 'edit_user': 3735 $format = sprintf( __("%s edited user '%s'."), $name, $event[4] ); 3736 break; 3737 3738 case 'delete_user': 3739 $format = sprintf( __("%s deleted user '%s'."), $name, $event[4] ); 3740 break; 3741 3742 case 'edit_category': 3743 $format = sprintf( __("%s edited category '%s'."), $name, $event[4] ); 3744 break; 3745 3746 case 'delete_category': 3747 $format = sprintf( __("%s deleted category '%s'."), $name, $event[4] ); 3748 break; 3749 3750 case 'add_chapter': 3751 $format = sprintf( __("%s added chapter '%s'."), $name, $event[4] ); 3752 break; 3753 3754 case 'edit_chapter': 3755 $format = sprintf( __("%s edited chapter '%s'."), $name, $event[4] ); 3756 break; 3757 3758 3759 default: 3760 if (!empty($event[4])) { 3761 // Note: should we add a specific format for generic events with four parameters? 3762 // I think not. If it's important enough, we should add a specific notice. 3763 $format = sprintf( __("%s did '%s' on '%s'."), $name, $event[2], $event[4] ); 3764 } else if (!empty($event[3])) { 3765 $format = sprintf( __("%s did '%s' on '%s'."), $name, $event[2], $event[3] ); 3766 } else { 3767 $format = sprintf( __("%s did '%s'."), $name, $event[2] ); 3768 } 3769 break; 3770 3771 } 3772 3773 3774 if (!empty($format)) { 3775 3776 $output[] = sprintf("<acronym title=\"%s\">%s</acronym>: %s", 3777 formatDate($event[0], $PIVOTX['config']->get('fulldate_format')), 3778 formatDateFuzzy($event[0]), 3779 $format 3780 ); 3781 3782 $amount--; 3783 } 3784 3785 } 3786 3787 return $output; 3788 3789 } 3790 3791 3792 } 3793 3794 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon May 21 01:08:32 2012 | Cross-referenced by PHPXref 0.6 |