OSDN Git Service

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