OSDN Git Service

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