| [ Index ] |
PHP Cross Reference of Nucleus CMS 3.64 |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/) 4 * Copyright (C) 2002-2009 The Nucleus Group 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * (see nucleus/documentation/index.html#license for more info) 11 */ 12 /** 13 * This class makes sure each item/weblog/comment object gets requested from 14 * the database only once, by keeping them in a cache. The class also acts as 15 * a dynamic classloader, loading classes _only_ when they are first needed, 16 * hoping to diminish execution time 17 * 18 * The class is a singleton, meaning that there will be only one object of it 19 * active at all times. The object can be requested using MANAGER::instance() 20 * 21 * @license http://nucleuscms.org/license.txt GNU General Public License 22 * @copyright Copyright (C) 2002-2009 The Nucleus Group 23 * @version $Id: MANAGER.php 1470 2010-11-29 22:10:16Z ftruscot $ 24 */ 25 class MANAGER { 26 27 /** 28 * Cached ITEM, BLOG, PLUGIN, KARMA and MEMBER objects. When these objects are requested 29 * through the global $manager object (getItem, getBlog, ...), only the first call 30 * will create an object. Subsequent calls will return the same object. 31 * 32 * The $items, $blogs, ... arrays map an id to an object (for plugins, the name is used 33 * rather than an ID) 34 */ 35 var $items; 36 var $blogs; 37 var $plugins; 38 var $karma; 39 var $templates; 40 var $members; 41 42 /** 43 * cachedInfo to avoid repeated SQL queries (see pidInstalled/pluginInstalled/getPidFromName) 44 * e.g. which plugins exists? 45 * 46 * $cachedInfo['installedPlugins'] = array($pid -> $name) 47 */ 48 var $cachedInfo; 49 50 /** 51 * The plugin subscriptionlist 52 * 53 * The subcription array has the following structure 54 * $subscriptions[$EventName] = array containing names of plugin classes to be 55 * notified when that event happens 56 */ 57 var $subscriptions; 58 59 /** 60 * Returns the only instance of this class. Creates the instance if it 61 * does not yet exists. Users should use this function as 62 * $manager =& MANAGER::instance(); to get a reference to the object 63 * instead of a copy 64 */ 65 function &instance() { 66 static $instance = array(); 67 if (empty($instance)) { 68 $instance[0] =& new MANAGER(); 69 } 70 return $instance[0]; 71 } 72 73 /** 74 * The constructor of this class initializes the object caches 75 */ 76 function MANAGER() { 77 $this->items = array(); 78 $this->blogs = array(); 79 $this->plugins = array(); 80 $this->karma = array(); 81 $this->parserPrefs = array(); 82 $this->cachedInfo = array(); 83 } 84 85 /** 86 * Returns the requested item object. If it is not in the cache, it will 87 * first be loaded and then placed in the cache. 88 * Intended use: $item =& $manager->getItem(1234) 89 */ 90 function &getItem($itemid, $allowdraft, $allowfuture) { 91 $item =& $this->items[$itemid]; 92 93 // check the draft and future rules if the item was already cached 94 if ($item) { 95 if ((!$allowdraft) && ($item['draft'])) 96 return 0; 97 98 $blog =& $this->getBlog(getBlogIDFromItemID($itemid)); 99 if ((!$allowfuture) && ($item['timestamp'] > $blog->getCorrectTime())) 100 return 0; 101 } 102 if (!$item) { 103 // load class if needed 104 $this->loadClass('ITEM'); 105 // load item object 106 $item = ITEM::getitem($itemid, $allowdraft, $allowfuture); 107 $this->items[$itemid] = $item; 108 } 109 return $item; 110 } 111 112 /** 113 * Loads a class if it has not yet been loaded 114 */ 115 function loadClass($name) { 116 $this->_loadClass($name, $name . '.php'); 117 } 118 119 /** 120 * Checks if an item exists 121 */ 122 function existsItem($id,$future,$draft) { 123 $this->_loadClass('ITEM','ITEM.php'); 124 return ITEM::exists($id,$future,$draft); 125 } 126 127 /** 128 * Checks if a category exists 129 */ 130 function existsCategory($id) { 131 return (quickQuery('SELECT COUNT(*) as result FROM '.sql_table('category').' WHERE catid='.intval($id)) > 0); 132 } 133 134 /** 135 * Returns the blog object for a given blogid 136 */ 137 function &getBlog($blogid) { 138 $blog =& $this->blogs[$blogid]; 139 140 if (!$blog) { 141 // load class if needed 142 $this->_loadClass('BLOG','BLOG.php'); 143 // load blog object 144 $blog =& new BLOG($blogid); 145 $this->blogs[$blogid] =& $blog; 146 } 147 return $blog; 148 } 149 150 /** 151 * Checks if a blog exists 152 */ 153 function existsBlog($name) { 154 $this->_loadClass('BLOG','BLOG.php'); 155 return BLOG::exists($name); 156 } 157 158 /** 159 * Checks if a blog id exists 160 */ 161 function existsBlogID($id) { 162 $this->_loadClass('BLOG','BLOG.php'); 163 return BLOG::existsID($id); 164 } 165 166 /** 167 * Returns a previously read template 168 */ 169 function &getTemplate($templateName) { 170 $template =& $this->templates[$templateName]; 171 172 if (!$template) { 173 $template = TEMPLATE::read($templateName); 174 $this->templates[$templateName] =& $template; 175 } 176 return $template; 177 } 178 179 /** 180 * Returns a KARMA object (karma votes) 181 */ 182 function &getKarma($itemid) { 183 $karma =& $this->karma[$itemid]; 184 185 if (!$karma) { 186 // load class if needed 187 $this->_loadClass('KARMA','KARMA.php'); 188 // create KARMA object 189 $karma =& new KARMA($itemid); 190 $this->karma[$itemid] =& $karma; 191 } 192 return $karma; 193 } 194 195 /** 196 * Returns a MEMBER object 197 */ 198 function &getMember($memberid) { 199 $mem =& $this->members[$memberid]; 200 201 if (!$mem) { 202 // load class if needed 203 $this->_loadClass('MEMBER','MEMBER.php'); 204 // create MEMBER object 205 $mem =& MEMBER::createFromID($memberid); 206 $this->members[$memberid] =& $mem; 207 } 208 return $mem; 209 } 210 211 /** 212 * Set the global parser preferences 213 */ 214 function setParserProperty($name, $value) { 215 $this->parserPrefs[$name] = $value; 216 } 217 218 /** 219 * Get the global parser preferences 220 */ 221 function getParserProperty($name) { 222 return $this->parserPrefs[$name]; 223 } 224 225 /** 226 * A helper function to load a class 227 * 228 * private 229 */ 230 function _loadClass($name, $filename) { 231 if (!class_exists($name)) { 232 global $DIR_LIBS; 233 include($DIR_LIBS . $filename); 234 } 235 } 236 237 /** 238 * A helper function to load a plugin 239 * 240 * private 241 */ 242 function _loadPlugin($name) { 243 if (!class_exists($name)) { 244 global $DIR_PLUGINS; 245 246 $fileName = $DIR_PLUGINS . $name . '.php'; 247 248 if (!file_exists($fileName)) 249 { 250 if (!defined('_MANAGER_PLUGINFILE_NOTFOUND')) { 251 define('_MANAGER_PLUGINFILE_NOTFOUND', 'Plugin %s was not loaded (File not found)'); 252 } 253 ACTIONLOG::add(WARNING, sprintf(_MANAGER_PLUGINFILE_NOTFOUND, $name)); 254 return 0; 255 } 256 257 // load plugin 258 include($fileName); 259 260 // check if class exists (avoid errors in eval'd code) 261 if (!class_exists($name)) 262 { 263 ACTIONLOG::add(WARNING, sprintf(_MANAGER_PLUGINFILE_NOCLASS, $name)); 264 return 0; 265 } 266 267 // add to plugin array 268 eval('$this->plugins[$name] =& new ' . $name . '();'); 269 270 // get plugid 271 $this->plugins[$name]->plugid = $this->getPidFromName($name); 272 273 // unload plugin if a prefix is used and the plugin cannot handle this^ 274 global $MYSQL_PREFIX; 275 if (($MYSQL_PREFIX != '') && !$this->plugins[$name]->supportsFeature('SqlTablePrefix')) 276 { 277 unset($this->plugins[$name]); 278 ACTIONLOG::add(WARNING, sprintf(_MANAGER_PLUGINTABLEPREFIX_NOTSUPPORT, $name)); 279 return 0; 280 } 281 282 // unload plugin if using non-mysql handler and plugin does not support it 283 global $MYSQL_HANDLER; 284 if ((!in_array('mysql',$MYSQL_HANDLER)) && !$this->plugins[$name]->supportsFeature('SqlApi')) 285 { 286 unset($this->plugins[$name]); 287 ACTIONLOG::add(WARNING, sprintf(_MANAGER_PLUGINSQLAPI_NOTSUPPORT, $name)); 288 return 0; 289 } 290 291 // call init method 292 $this->plugins[$name]->init(); 293 294 } 295 } 296 297 /** 298 * Returns a PLUGIN object 299 */ 300 function &getPlugin($name) { 301 // retrieve the name of the plugin in the right capitalisation 302 $name = $this->getUpperCaseName ($name); 303 // get the plugin 304 $plugin =& $this->plugins[$name]; 305 306 if (!$plugin) { 307 // load class if needed 308 $this->_loadPlugin($name); 309 $plugin =& $this->plugins[$name]; 310 } 311 return $plugin; 312 } 313 314 /** 315 * Checks if the given plugin IS loaded or not 316 */ 317 function &pluginLoaded($name) { 318 $plugin =& $this->plugins[$name]; 319 return $plugin; 320 } 321 322 function &pidLoaded($pid) { 323 $plugin=false; 324 reset($this->plugins); 325 while (list($name) = each($this->plugins)) { 326 if ($pid!=$this->plugins[$name]->getId()) continue; 327 $plugin= & $this->plugins[$name]; 328 break; 329 } 330 return $plugin; 331 } 332 333 /** 334 * checks if the given plugin IS installed or not 335 */ 336 function pluginInstalled($name) { 337 $this->_initCacheInfo('installedPlugins'); 338 return ($this->getPidFromName($name) != -1); 339 } 340 341 function pidInstalled($pid) { 342 $this->_initCacheInfo('installedPlugins'); 343 return ($this->cachedInfo['installedPlugins'][$pid] != ''); 344 } 345 346 function getPidFromName($name) { 347 $this->_initCacheInfo('installedPlugins'); 348 foreach ($this->cachedInfo['installedPlugins'] as $pid => $pfile) 349 { 350 if (strtolower($pfile) == strtolower($name)) 351 return $pid; 352 } 353 return -1; 354 } 355 356 /** 357 * Retrieve the name of a plugin in the right capitalisation 358 */ 359 function getUpperCaseName ($name) { 360 $this->_initCacheInfo('installedPlugins'); 361 foreach ($this->cachedInfo['installedPlugins'] as $pid => $pfile) 362 { 363 if (strtolower($pfile) == strtolower($name)) 364 return $pfile; 365 } 366 return -1; 367 } 368 369 function clearCachedInfo($what) { 370 unset($this->cachedInfo[$what]); 371 } 372 373 /** 374 * Loads some info on the first call only 375 */ 376 function _initCacheInfo($what) 377 { 378 if (isset($this->cachedInfo[$what]) && is_array($this->cachedInfo[$what])) 379 return; 380 switch ($what) 381 { 382 // 'installedPlugins' = array ($pid => $name) 383 case 'installedPlugins': 384 $this->cachedInfo['installedPlugins'] = array(); 385 $res = sql_query('SELECT pid, pfile FROM ' . sql_table('plugin')); 386 while ($o = sql_fetch_object($res)) 387 { 388 $this->cachedInfo['installedPlugins'][$o->pid] = $o->pfile; 389 } 390 break; 391 } 392 } 393 394 /** 395 * A function to notify plugins that something has happened. Only the plugins 396 * that are subscribed to the event will get notified. 397 * Upon the first call, the list of subscriptions will be fetched from the 398 * database. The plugins itsself will only get loaded when they are first needed 399 * 400 * @param $eventName 401 * Name of the event (method to be called on plugins) 402 * @param $data 403 * Can contain any type of data, depending on the event type. Usually this is 404 * an itemid, blogid, ... but it can also be an array containing multiple values 405 */ 406 function notify($eventName, $data) { 407 // load subscription list if needed 408 if (!is_array($this->subscriptions)) 409 $this->_loadSubscriptions(); 410 411 412 // get listening objects 413 $listeners = false; 414 if (isset($this->subscriptions[$eventName])) { 415 $listeners = $this->subscriptions[$eventName]; 416 } 417 418 // notify all of them 419 if (is_array($listeners)) { 420 foreach($listeners as $listener) { 421 // load class if needed 422 $this->_loadPlugin($listener); 423 // do notify (if method exists) 424 if (isset($this->plugins[$listener]) && method_exists($this->plugins[$listener], 'event_' . $eventName)) 425 call_user_func(array(&$this->plugins[$listener],'event_' . $eventName), &$data); 426 } 427 } 428 429 } 430 431 /** 432 * Loads plugin subscriptions 433 */ 434 function _loadSubscriptions() { 435 // initialize as array 436 $this->subscriptions = array(); 437 438 $res = sql_query('SELECT p.pfile as pfile, e.event as event FROM '.sql_table('plugin_event').' as e, '.sql_table('plugin').' as p WHERE e.pid=p.pid ORDER BY p.porder ASC'); 439 while ($o = sql_fetch_object($res)) { 440 $pluginName = $o->pfile; 441 $eventName = $o->event; 442 $this->subscriptions[$eventName][] = $pluginName; 443 } 444 445 } 446 447 /* 448 Ticket functions. These are uses by the admin area to make it impossible to simulate certain GET/POST 449 requests. tickets are user specific 450 */ 451 452 var $currentRequestTicket = ''; 453 454 /** 455 * GET requests: Adds ticket to URL (URL should NOT be html-encoded!, ticket is added at the end) 456 */ 457 function addTicketToUrl($url) 458 { 459 $ticketCode = 'ticket=' . $this->_generateTicket(); 460 if (strstr($url, '?')) 461 return $url . '&' . $ticketCode; 462 else 463 return $url . '?' . $ticketCode; 464 } 465 466 /** 467 * POST requests: Adds ticket as hidden formvar 468 */ 469 function addTicketHidden() 470 { 471 $ticket = $this->_generateTicket(); 472 473 echo '<input type="hidden" name="ticket" value="', htmlspecialchars($ticket), '" />'; 474 } 475 476 /** 477 * Get a new ticket 478 * (xmlHTTPRequest AutoSaveDraft uses this to refresh the ticket) 479 */ 480 function getNewTicket() 481 { 482 $this->currentRequestTicket = ''; 483 return $this->_generateTicket(); 484 } 485 486 /** 487 * Checks the ticket that was passed along with the current request 488 */ 489 function checkTicket() 490 { 491 global $member; 492 493 // get ticket from request 494 $ticket = requestVar('ticket'); 495 496 // no ticket -> don't allow 497 if ($ticket == '') 498 return false; 499 500 // remove expired tickets first 501 $this->_cleanUpExpiredTickets(); 502 503 // get member id 504 if (!$member->isLoggedIn()) 505 $memberId = -1; 506 else 507 $memberId = $member->getID(); 508 509 // check if ticket is a valid one 510 $query = 'SELECT COUNT(*) as result FROM ' . sql_table('tickets') . ' WHERE member=' . intval($memberId). ' and ticket=\''.sql_real_escape_string($ticket).'\''; 511 if (quickQuery($query) == 1) 512 { 513 // [in the original implementation, the checked ticket was deleted. This would lead to invalid 514 // tickets when using the browsers back button and clicking another link/form 515 // leaving the keys in the database is not a real problem, since they're member-specific and 516 // only valid for a period of one hour 517 // ] 518 // sql_query('DELETE FROM '.sql_table('tickets').' WHERE member=' . intval($memberId). ' and ticket=\''.addslashes($ticket).'\''); 519 return true; 520 } else { 521 // not a valid ticket 522 return false; 523 } 524 525 } 526 527 /** 528 * (internal method) Removes the expired tickets 529 */ 530 function _cleanUpExpiredTickets() 531 { 532 // remove tickets older than 1 hour 533 $oldTime = time() - 60 * 60; 534 $query = 'DELETE FROM ' . sql_table('tickets'). ' WHERE ctime < \'' . date('Y-m-d H:i:s',$oldTime) .'\''; 535 sql_query($query); 536 } 537 538 /** 539 * (internal method) Generates/returns a ticket (one ticket per page request) 540 */ 541 function _generateTicket() 542 { 543 if ($this->currentRequestTicket == '') 544 { 545 // generate new ticket (only one ticket will be generated per page request) 546 // and store in database 547 global $member; 548 // get member id 549 if (!$member->isLoggedIn()) 550 $memberId = -1; 551 else 552 $memberId = $member->getID(); 553 554 $ok = false; 555 while (!$ok) 556 { 557 // generate a random token 558 srand((double)microtime()*1000000); 559 $ticket = md5(uniqid(rand(), true)); 560 561 // add in database as non-active 562 $query = 'INSERT INTO ' . sql_table('tickets') . ' (ticket, member, ctime) '; 563 $query .= 'VALUES (\'' . sql_real_escape_string($ticket). '\', \'' . intval($memberId). '\', \'' . date('Y-m-d H:i:s',time()) . '\')'; 564 if (sql_query($query)) 565 $ok = true; 566 } 567 568 $this->currentRequestTicket = $ticket; 569 } 570 return $this->currentRequestTicket; 571 } 572 573 } 574 575 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon May 2 16:14:08 2011 | Cross-referenced by PHPXref 0.7.1 |