]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/gnusocial.php
Type-hint is array here.
[quix0rs-gnu-social.git] / lib / gnusocial.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2009-2010 StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 if (!defined('GNUSOCIAL')) { exit(1); }
22
23 global $config, $_server, $_path;
24
25 /**
26  * Global configuration setup and management.
27  */
28 class GNUsocial
29 {
30     protected static $config_files = array();
31     protected static $have_config;
32     protected static $is_api;
33     protected static $is_ajax;
34     protected static $plugins = array();
35
36     /**
37      * Configure and instantiate a plugin into the current configuration.
38      * Class definitions will be loaded from standard paths if necessary.
39      * Note that initialization events won't be fired until later.
40      *
41      * @param string $name class name & plugin file/subdir name
42      * @param array $attrs key/value pairs of public attributes to set on plugin instance
43      *
44      * @throws ServerException if plugin can't be found
45      */
46     public static function addPlugin($name, array $attrs=array())
47     {
48         $name = ucfirst($name);
49
50         if (isset(self::$plugins[$name])) {
51             // We have already loaded this plugin. Don't try to
52             // do it again with (possibly) different values.
53             // Försten till kvarn får mala.
54             return true;
55         }
56
57         $pluginclass = "{$name}Plugin";
58
59         if (!class_exists($pluginclass)) {
60
61             $files = array("local/plugins/{$pluginclass}.php",
62                            "local/plugins/{$name}/{$pluginclass}.php",
63                            "local/{$pluginclass}.php",
64                            "local/{$name}/{$pluginclass}.php",
65                            "plugins/{$pluginclass}.php",
66                            "plugins/{$name}/{$pluginclass}.php");
67
68             foreach ($files as $file) {
69                 $fullpath = INSTALLDIR.'/'.$file;
70                 if (@file_exists($fullpath)) {
71                     include_once($fullpath);
72                     break;
73                 }
74             }
75             if (!class_exists($pluginclass)) {
76                 throw new ServerException("Plugin $name not found.", 500);
77             }
78         }
79
80         // Doesn't this $inst risk being garbage collected or something?
81         // TODO: put into a static array that makes sure $inst isn't lost.
82         $inst = new $pluginclass();
83         foreach ($attrs as $aname => $avalue) {
84             $inst->$aname = $avalue;
85         }
86
87         // Record activated plugins for later display/config dump
88         self::$plugins[$name] = $attrs;
89
90         return true;
91     }
92
93     public static function delPlugin($name)
94     {
95         // Remove our plugin if it was previously loaded
96         $name = ucfirst($name);
97         if (isset(self::$plugins[$name])) {
98             unset(self::$plugins[$name]);
99         }
100
101         // make sure initPlugins will avoid this
102         common_config_set('plugins', 'disable-'.$name, true);
103
104         return true;
105     }
106
107     /**
108      * Get a list of activated plugins in this process.
109      * @return array of (string $name, array $args) pairs
110      */
111     public static function getActivePlugins()
112     {
113         return self::$plugins;
114     }
115
116     /**
117      * Initialize, or re-initialize, GNU social global configuration
118      * and plugins.
119      *
120      * If switching site configurations during script execution, be
121      * careful when working with leftover objects -- global settings
122      * affect many things and they may not behave as you expected.
123      *
124      * @param $server optional web server hostname for picking config
125      * @param $path optional URL path for picking config
126      * @param $conffile optional configuration file path
127      *
128      * @throws NoConfigException if config file can't be found
129      */
130     public static function init($server=null, $path=null, $conffile=null)
131     {
132         Router::clear();
133
134         self::initDefaults($server, $path);
135         self::loadConfigFile($conffile);
136
137         $sprofile = common_config('site', 'profile');
138         if (!empty($sprofile)) {
139             self::loadSiteProfile($sprofile);
140         }
141         // Load settings from database; note we need autoload for this
142         Config::loadSettings();
143
144         self::initPlugins();
145     }
146
147     /**
148      * Get identifier of the currently active site configuration
149      * @return string
150      */
151     public static function currentSite()
152     {
153         return common_config('site', 'nickname');
154     }
155
156     /**
157      * Change site configuration to site specified by nickname,
158      * if set up via Status_network. If not, sites other than
159      * the current will fail horribly.
160      *
161      * May throw exception or trigger a fatal error if the given
162      * site is missing or configured incorrectly.
163      *
164      * @param string $nickname
165      */
166     public static function switchSite($nickname)
167     {
168         if ($nickname == self::currentSite()) {
169             return true;
170         }
171
172         $sn = Status_network::getKV('nickname', $nickname);
173         if (empty($sn)) {
174             return false;
175             throw new Exception("No such site nickname '$nickname'");
176         }
177
178         $server = $sn->getServerName();
179         self::init($server);
180     }
181
182     /**
183      * Pull all local sites from status_network table.
184      *
185      * Behavior undefined if site is not configured via Status_network.
186      *
187      * @return array of nicknames
188      */
189     public static function findAllSites()
190     {
191         $sites = array();
192         $sn = new Status_network();
193         $sn->find();
194         while ($sn->fetch()) {
195             $sites[] = $sn->nickname;
196         }
197         return $sites;
198     }
199
200     /**
201      * Fire initialization events for all instantiated plugins.
202      */
203     protected static function initPlugins()
204     {
205         // User config may have already added some of these plugins, with
206         // maybe configured parameters. The self::addPlugin function will
207         // ignore the new call if it has already been instantiated.
208
209         // Load core plugins
210         foreach (common_config('plugins', 'core') as $name => $params) {
211             call_user_func('self::addPlugin', $name, $params);
212         }
213
214         // Load default plugins
215         foreach (common_config('plugins', 'default') as $name => $params) {
216             $key = 'disable-' . $name;
217             if (common_config('plugins', $key)) {
218                 continue;
219             }
220
221             // TODO: We should be able to avoid this is_null and assume $params
222             // is an array, since that's how it is typed in addPlugin
223             if (is_null($params)) {
224                 self::addPlugin($name);
225             } else if (is_array($params)) {
226                 if (count($params) == 0) {
227                     self::addPlugin($name);
228                 } else {
229                     $keys = array_keys($params);
230                     if (is_string($keys[0])) {
231                         self::addPlugin($name, $params);
232                     } else {
233                         foreach ($params as $paramset) {
234                             self::addPlugin($name, $paramset);
235                         }
236                     }
237                 }
238             }
239         }
240
241         // XXX: if plugins should check the schema at runtime, do that here.
242         if (common_config('db', 'schemacheck') == 'runtime') {
243             Event::handle('CheckSchema');
244         }
245
246         // Give plugins a chance to initialize in a fully-prepared environment
247         Event::handle('InitializePlugin');
248     }
249
250     /**
251      * Quick-check if configuration has been established.
252      * Useful for functions which may get used partway through
253      * initialization to back off from fancier things.
254      *
255      * @return bool
256      */
257     public static function haveConfig()
258     {
259         return self::$have_config;
260     }
261
262     /**
263      * Returns a list of configuration files that have
264      * been loaded for this instance of GNU social.
265      */
266     public static function configFiles()
267     {
268         return self::$config_files;
269     }
270
271     public static function isApi()
272     {
273         return self::$is_api;
274     }
275
276     public static function setApi($mode)
277     {
278         self::$is_api = $mode;
279     }
280
281     public static function isAjax()
282     {
283         return self::$is_ajax;
284     }
285
286     public static function setAjax($mode)
287     {
288         self::$is_ajax = $mode;
289     }
290
291     /**
292      * Build default configuration array
293      * @return array
294      */
295     protected static function defaultConfig()
296     {
297         global $_server, $_path;
298         require(INSTALLDIR.'/lib/default.php');
299         return $default;
300     }
301
302     /**
303      * Establish default configuration based on given or default server and path
304      * Sets global $_server, $_path, and $config
305      */
306     public static function initDefaults($server, $path)
307     {
308         global $_server, $_path, $config, $_PEAR;
309
310         Event::clearHandlers();
311         self::$plugins = array();
312
313         // try to figure out where we are. $server and $path
314         // can be set by including module, else we guess based
315         // on HTTP info.
316
317         if (isset($server)) {
318             $_server = $server;
319         } else {
320             $_server = array_key_exists('SERVER_NAME', $_SERVER) ?
321               strtolower($_SERVER['SERVER_NAME']) :
322             null;
323         }
324
325         if (isset($path)) {
326             $_path = $path;
327         } else {
328             $_path = (array_key_exists('SERVER_NAME', $_SERVER) && array_key_exists('SCRIPT_NAME', $_SERVER)) ?
329               self::_sn_to_path($_SERVER['SCRIPT_NAME']) :
330             null;
331         }
332
333         // Set config values initially to default values
334         $default = self::defaultConfig();
335         $config = $default;
336
337         // default configuration, overwritten in config.php
338         // Keep DB_DataObject's db config synced to ours...
339
340         $config['db'] = &$_PEAR->getStaticProperty('DB_DataObject','options');
341
342         $config['db'] = $default['db'];
343
344         if (function_exists('date_default_timezone_set')) {
345             /* Work internally in UTC */
346             date_default_timezone_set('UTC');
347         }
348     }
349
350     public static function loadSiteProfile($name)
351     {
352         global $config;
353         $settings = SiteProfile::getSettings($name);
354         $config = array_replace_recursive($config, $settings);
355     }
356
357     protected static function _sn_to_path($sn)
358     {
359         $past_root = substr($sn, 1);
360         $last_slash = strrpos($past_root, '/');
361         if ($last_slash > 0) {
362             $p = substr($past_root, 0, $last_slash);
363         } else {
364             $p = '';
365         }
366         return $p;
367     }
368
369     /**
370      * Load the default or specified configuration file.
371      * Modifies global $config and may establish plugins.
372      *
373      * @throws NoConfigException
374      */
375     protected static function loadConfigFile($conffile=null)
376     {
377         global $_server, $_path, $config;
378
379         // From most general to most specific:
380         // server-wide, then vhost-wide, then for a path,
381         // finally for a dir (usually only need one of the last two).
382
383         if (isset($conffile)) {
384             $config_files = array($conffile);
385         } else {
386             $config_files = array('/etc/gnusocial/config.php',
387                                   '/etc/gnusocial/config.d/'.$_server.'.php');
388
389             if (strlen($_path) > 0) {
390                 $config_files[] = '/etc/gnusocial/config.d/'.$_server.'_'.$_path.'.php';
391             }
392
393             $config_files[] = INSTALLDIR.'/config.php';
394         }
395
396         self::$have_config = false;
397
398         foreach ($config_files as $_config_file) {
399             if (@file_exists($_config_file)) {
400                 // Ignore 0-byte config files
401                 if (filesize($_config_file) > 0) {
402                     include($_config_file);
403                     self::$config_files[] = $_config_file;
404                     self::$have_config = true;
405                 }
406             }
407         }
408
409         if (!self::$have_config) {
410             throw new NoConfigException("No configuration file found.",
411                                         $config_files);
412         }
413
414         // Check for database server; must exist!
415
416         if (empty($config['db']['database'])) {
417             throw new ServerException("No database server for this site.");
418         }
419     }
420
421     /**
422      * Are we running from the web with HTTPS?
423      *
424      * @return boolean true if we're running with HTTPS; else false
425      */
426
427     static function isHTTPS()
428     {
429         // There are some exceptions to this; add them here!
430         if (empty($_SERVER['HTTPS'])) {
431             return false;
432         }
433
434         // If it is _not_ "off", it is on, so "true".
435         return strtolower($_SERVER['HTTPS']) !== 'off';
436     }
437
438     /**
439      * Can we use HTTPS? Then do! Only return false if it's not configured ("never").
440      */
441     static function useHTTPS()
442     {
443         return self::isHTTPS() || common_config('site', 'ssl') != 'never';
444     }
445 }
446
447 class NoConfigException extends Exception
448 {
449     public $configFiles;
450
451     function __construct($msg, $configFiles) {
452         parent::__construct($msg);
453         $this->configFiles = $configFiles;
454     }
455 }