| [ Index ] |
PHP Cross Reference of Nucleus CMS 3.32 |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/) 4 * Copyright (C) 2002-2007 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-2007 The Nucleus Group 23 * @version $Id: MANAGER.php 1150 2007-05-19 23:26:02Z kaigreve $ 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 function &getBlog($blogid) { 135 $blog =& $this->blogs[$blogid]; 136 137 if (!$blog) { 138 // load class if needed 139 $this->_loadClass('BLOG','BLOG.php'); 140 // load blog object 141 $blog =& new BLOG($blogid); 142 $this->blogs[$blogid] =& $blog; 143 } 144 return $blog; 145 } 146 147 function existsBlog($name) { 148 $this->_loadClass('BLOG','BLOG.php'); 149 return BLOG::exists($name); 150 } 151 152 function existsBlogID($id) { 153 $this->_loadClass('BLOG','BLOG.php'); 154 return BLOG::existsID($id); 155 } 156 157 /** 158 * Returns a previously read template 159 */ 160 function &getTemplate($templateName) { 161 $template =& $this->templates[$templateName]; 162 163 if (!$template) { 164 $template = TEMPLATE::read($templateName); 165 $this->templates[$templateName] =& $template; 166 } 167 return $template; 168 } 169 170 /** 171 * Returns a KARMA object (karma votes) 172 */ 173 function &getKarma($itemid) { 174 $karma =& $this->karma[$itemid]; 175 176 if (!$karma) { 177 // load class if needed 178 $this->_loadClass('KARMA','KARMA.php'); 179 // create KARMA object 180 $karma =& new KARMA($itemid); 181 $this->karma[$itemid] =& $karma; 182 } 183 return $karma; 184 } 185 186 /** 187 * Returns a MEMBER object 188 */ 189 function &getMember($memberid) { 190 $mem =& $this->members[$memberid]; 191 192 if (!$mem) { 193 // load class if needed 194 $this->_loadClass('MEMBER','MEMBER.php'); 195 // create MEMBER object 196 $mem =& MEMBER::createFromID($memberid); 197 $this->members[$memberid] =& $mem; 198 } 199 return $mem; 200 } 201 202 /** 203 * Global parser preferences 204 */ 205 function setParserProperty($name, $value) { 206 $this->parserPrefs[$name] = $value; 207 } 208 function getParserProperty($name) { 209 return $this->parserPrefs[$name]; 210 } 211 212 /** 213 * A private helper class to load classes 214 */ 215 function _loadClass($name, $filename) { 216 if (!class_exists($name)) { 217 global $DIR_LIBS; 218 include($DIR_LIBS . $filename); 219 } 220 } 221 222 function _loadPlugin($name) { 223 if (!class_exists($name)) { 224 global $DIR_PLUGINS; 225 226 $fileName = $DIR_PLUGINS . $name . '.php'; 227 228 if (!file_exists($fileName)) 229 { 230 ACTIONLOG::add(WARNING, 'Plugin ' . $name . ' was not loaded (File not found)'); 231 return 0; 232 } 233 234 // load plugin 235 include($fileName); 236 237 // check if class exists (avoid errors in eval'd code) 238 if (!class_exists($name)) 239 { 240 ACTIONLOG::add(WARNING, 'Plugin ' . $name . ' was not loaded (Class not found in file, possible parse error)'); 241 return 0; 242 } 243 244 // add to plugin array 245 eval('$this->plugins[$name] =& new ' . $name . '();'); 246 247 // get plugid 248 $this->plugins[$name]->plugid = $this->getPidFromName($name); 249 250 // unload plugin if a prefix is used and the plugin cannot handle this^ 251 global $MYSQL_PREFIX; 252 if (($MYSQL_PREFIX != '') && !$this->plugins[$name]->supportsFeature('SqlTablePrefix')) 253 { 254 unset($this->plugins[$name]); 255 ACTIONLOG::add(WARNING, 'Plugin ' . $name . ' was not loaded (does not support SqlTablePrefix)'); 256 return 0; 257 } 258 259 // call init method 260 $this->plugins[$name]->init(); 261 262 } 263 } 264 265 function &getPlugin($name) { 266 $plugin =& $this->plugins[$name]; 267 268 if (!$plugin) { 269 // load class if needed 270 $this->_loadPlugin($name); 271 $plugin =& $this->plugins[$name]; 272 } 273 return $plugin; 274 } 275 276 /** 277 * checks if the given plugin IS loaded or not 278 */ 279 function &pluginLoaded($name) { 280 $plugin =& $this->plugins[$name]; 281 return $plugin; 282 } 283 function &pidLoaded($pid) { 284 $plugin=false; 285 reset($this->plugins); 286 while (list($name) = each($this->plugins)) { 287 if ($pid!=$this->plugins[$name]->getId()) continue; 288 $plugin= & $this->plugins[$name]; 289 break; 290 } 291 return $plugin; 292 } 293 294 /** 295 * checks if the given plugin IS installed or not 296 */ 297 function pluginInstalled($name) { 298 $this->_initCacheInfo('installedPlugins'); 299 return ($this->getPidFromName($name) != -1); 300 } 301 function pidInstalled($pid) { 302 $this->_initCacheInfo('installedPlugins'); 303 return ($this->cachedInfo['installedPlugins'][$pid] != ''); 304 } 305 function getPidFromName($name) { 306 $this->_initCacheInfo('installedPlugins'); 307 foreach ($this->cachedInfo['installedPlugins'] as $pid => $pfile) 308 { 309 if ($pfile == $name) 310 return $pid; 311 } 312 return -1; 313 } 314 function clearCachedInfo($what) { 315 unset($this->cachedInfo[$what]); 316 } 317 318 /** 319 * Loads some info on the first call only 320 */ 321 function _initCacheInfo($what) 322 { 323 if (isset($this->cachedInfo[$what]) && is_array($this->cachedInfo[$what])) 324 return; 325 switch ($what) 326 { 327 // 'installedPlugins' = array ($pid => $name) 328 case 'installedPlugins': 329 $this->cachedInfo['installedPlugins'] = array(); 330 $res = sql_query('SELECT pid, pfile FROM ' . sql_table('plugin')); 331 while ($o = mysql_fetch_object($res)) 332 { 333 $this->cachedInfo['installedPlugins'][$o->pid] = $o->pfile; 334 } 335 break; 336 } 337 } 338 339 /** 340 * A function to notify plugins that something has happened. Only the plugins 341 * that are subscribed to the event will get notified. 342 * Upon the first call, the list of subscriptions will be fetched from the 343 * database. The plugins itsself will only get loaded when they are first needed 344 * 345 * @param $eventName 346 * Name of the event (method to be called on plugins) 347 * @param $data 348 * Can contain any type of data, depending on the event type. Usually this is 349 * an itemid, blogid, ... but it can also be an array containing multiple values 350 */ 351 function notify($eventName, $data) { 352 // load subscription list if needed 353 if (!is_array($this->subscriptions)) 354 $this->_loadSubscriptions(); 355 356 357 // get listening objects 358 $listeners = false; 359 if (isset($this->subscriptions[$eventName])) { 360 $listeners = $this->subscriptions[$eventName]; 361 } 362 363 // notify all of them 364 if (is_array($listeners)) { 365 foreach($listeners as $listener) { 366 // load class if needed 367 $this->_loadPlugin($listener); 368 // do notify (if method exists) 369 if (method_exists($this->plugins[$listener], 'event_' . $eventName)) 370 call_user_func(array(&$this->plugins[$listener],'event_' . $eventName), $data); 371 } 372 } 373 374 } 375 376 /** 377 * Loads plugin subscriptions 378 */ 379 function _loadSubscriptions() { 380 // initialize as array 381 $this->subscriptions = array(); 382 383 $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'); 384 while ($o = mysql_fetch_object($res)) { 385 $pluginName = $o->pfile; 386 $eventName = $o->event; 387 $this->subscriptions[$eventName][] = $pluginName; 388 } 389 390 } 391 392 /* 393 Ticket functions. These are uses by the admin area to make it impossible to simulate certain GET/POST 394 requests. tickets are user specific 395 */ 396 397 var $currentRequestTicket = ''; 398 399 /** 400 * GET requests: Adds ticket to URL (URL should NOT be html-encoded!, ticket is added at the end) 401 */ 402 function addTicketToUrl($url) 403 { 404 $ticketCode = 'ticket=' . $this->_generateTicket(); 405 if (strstr($url, '?')) 406 return $url . '&' . $ticketCode; 407 else 408 return $url . '?' . $ticketCode; 409 } 410 411 /** 412 * POST requests: Adds ticket as hidden formvar 413 */ 414 function addTicketHidden() 415 { 416 $ticket = $this->_generateTicket(); 417 418 echo '<input type="hidden" name="ticket" value="', htmlspecialchars($ticket), '" />'; 419 } 420 421 /** 422 * Get a new ticket 423 * (xmlHTTPRequest AutoSaveDraft uses this to refresh the ticket) 424 */ 425 function getNewTicket() 426 { 427 $this->currentRequestTicket = ''; 428 return $this->_generateTicket(); 429 } 430 431 /** 432 * Checks the ticket that was passed along with the current request 433 */ 434 function checkTicket() 435 { 436 global $member; 437 438 // get ticket from request 439 $ticket = requestVar('ticket'); 440 441 // no ticket -> don't allow 442 if ($ticket == '') 443 return false; 444 445 // remove expired tickets first 446 $this->_cleanUpExpiredTickets(); 447 448 // get member id 449 if (!$member->isLoggedIn()) 450 $memberId = -1; 451 else 452 $memberId = $member->getID(); 453 454 // check if ticket is a valid one 455 $query = 'SELECT COUNT(*) as result FROM ' . sql_table('tickets') . ' WHERE member=' . intval($memberId). ' and ticket=\''.addslashes($ticket).'\''; 456 if (quickQuery($query) == 1) 457 { 458 // [in the original implementation, the checked ticket was deleted. This would lead to invalid 459 // tickets when using the browsers back button and clicking another link/form 460 // leaving the keys in the database is not a real problem, since they're member-specific and 461 // only valid for a period of one hour 462 // ] 463 // sql_query('DELETE FROM '.sql_table('tickets').' WHERE member=' . intval($memberId). ' and ticket=\''.addslashes($ticket).'\''); 464 return true; 465 } else { 466 // not a valid ticket 467 return false; 468 } 469 470 } 471 472 /** 473 * (internal method) Removes the expired tickets 474 */ 475 function _cleanUpExpiredTickets() 476 { 477 // remove tickets older than 1 hour 478 $oldTime = time() - 60 * 60; 479 $query = 'DELETE FROM ' . sql_table('tickets'). ' WHERE ctime < \'' . date('Y-m-d H:i:s',$oldTime) .'\''; 480 sql_query($query); 481 } 482 483 /** 484 * (internal method) Generates/returns a ticket (one ticket per page request) 485 */ 486 function _generateTicket() 487 { 488 if ($this->currentRequestTicket == '') 489 { 490 // generate new ticket (only one ticket will be generated per page request) 491 // and store in database 492 global $member; 493 // get member id 494 if (!$member->isLoggedIn()) 495 $memberId = -1; 496 else 497 $memberId = $member->getID(); 498 499 $ok = false; 500 while (!$ok) 501 { 502 // generate a random token 503 srand((double)microtime()*1000000); 504 $ticket = md5(uniqid(rand(), true)); 505 506 // add in database as non-active 507 $query = 'INSERT INTO ' . sql_table('tickets') . ' (ticket, member, ctime) '; 508 $query .= 'VALUES (\'' . addslashes($ticket). '\', \'' . intval($memberId). '\', \'' . date('Y-m-d H:i:s',time()) . '\')'; 509 if (sql_query($query)) 510 $ok = true; 511 } 512 513 $this->currentRequestTicket = $ticket; 514 } 515 return $this->currentRequestTicket; 516 } 517 518 } 519 520 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Tue Feb 12 15:34:36 2008 | Cross-referenced by PHPXref 0.7 |