OSDN Git Service

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