OSDN Git Service

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