Commit | Line | Data |
---|---|---|
d335fa16 AE |
1 | <?php |
2 | namespace wcf\system; | |
3 | use wcf\data\application\Application; | |
4 | use wcf\data\option\OptionEditor; | |
5 | use wcf\data\package\Package; | |
6 | use wcf\data\package\PackageCache; | |
7 | use wcf\data\package\PackageEditor; | |
12a2ff01 AE |
8 | use wcf\data\page\Page; |
9 | use wcf\data\page\PageCache; | |
5346b183 | 10 | use wcf\page\CmsPage; |
d335fa16 | 11 | use wcf\system\application\ApplicationHandler; |
f341086b | 12 | use wcf\system\application\IApplication; |
55b402a0 | 13 | use wcf\system\box\BoxHandler; |
d335fa16 AE |
14 | use wcf\system\cache\builder\CoreObjectCacheBuilder; |
15 | use wcf\system\cache\builder\PackageUpdateCacheBuilder; | |
16 | use wcf\system\cronjob\CronjobScheduler; | |
157054c9 | 17 | use wcf\system\database\MySQLDatabase; |
d335fa16 AE |
18 | use wcf\system\event\EventHandler; |
19 | use wcf\system\exception\AJAXException; | |
b25ad8f3 | 20 | use wcf\system\exception\ErrorException; |
d335fa16 AE |
21 | use wcf\system\exception\IPrintableException; |
22 | use wcf\system\exception\NamedUserException; | |
7b9ff46b | 23 | use wcf\system\exception\ParentClassException; |
d335fa16 AE |
24 | use wcf\system\exception\PermissionDeniedException; |
25 | use wcf\system\exception\SystemException; | |
26 | use wcf\system\language\LanguageFactory; | |
27 | use wcf\system\package\PackageInstallationDispatcher; | |
11117cd5 | 28 | use wcf\system\registry\RegistryHandler; |
12a2ff01 | 29 | use wcf\system\request\Request; |
a80873d5 | 30 | use wcf\system\request\RequestHandler; |
d335fa16 AE |
31 | use wcf\system\session\SessionFactory; |
32 | use wcf\system\session\SessionHandler; | |
33 | use wcf\system\style\StyleHandler; | |
15621401 | 34 | use wcf\system\template\EmailTemplateEngine; |
d335fa16 AE |
35 | use wcf\system\template\TemplateEngine; |
36 | use wcf\system\user\storage\UserStorageHandler; | |
d335fa16 | 37 | use wcf\util\FileUtil; |
8418bed7 | 38 | use wcf\util\HeaderUtil; |
d335fa16 | 39 | use wcf\util\StringUtil; |
dc0015ab | 40 | use wcf\util\UserUtil; |
d335fa16 AE |
41 | |
42 | // try to set a time-limit to infinite | |
43 | @set_time_limit(0); | |
44 | ||
45 | // fix timezone warning issue | |
46 | if (!@ini_get('date.timezone')) { | |
47 | @date_default_timezone_set('Europe/London'); | |
48 | } | |
49 | ||
359841c3 | 50 | // define current woltlab suite version |
4ac5384a | 51 | define('WCF_VERSION', '3.1.5 pl 1'); |
d335fa16 | 52 | |
89484ba0 AE |
53 | // define current API version |
54 | define('WSC_API_VERSION', 2018); | |
55 | ||
d335fa16 AE |
56 | // define current unix timestamp |
57 | define('TIME_NOW', time()); | |
58 | ||
59 | // wcf imports | |
60 | if (!defined('NO_IMPORTS')) { | |
61 | require_once(WCF_DIR.'lib/core.functions.php'); | |
d11a8c9e | 62 | require_once(WCF_DIR.'lib/system/api/autoload.php'); |
d335fa16 AE |
63 | } |
64 | ||
65 | /** | |
e71525e4 | 66 | * WCF is the central class for the WoltLab Suite Core. |
d335fa16 AE |
67 | * It holds the database connection, access to template and language engine. |
68 | * | |
69 | * @author Marcel Werk | |
21987a12 | 70 | * @copyright 2001-2018 WoltLab GmbH |
d335fa16 | 71 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
e71525e4 | 72 | * @package WoltLabSuite\Core\System |
d335fa16 AE |
73 | */ |
74 | class WCF { | |
89484ba0 AE |
75 | /** |
76 | * list of supported legacy API versions | |
77 | * @var integer[] | |
78 | */ | |
79 | private static $supportedLegacyApiVersions = [2017]; | |
80 | ||
d335fa16 AE |
81 | /** |
82 | * list of currently loaded applications | |
893aace3 | 83 | * @var Application[] |
d335fa16 | 84 | */ |
058cbd6a | 85 | protected static $applications = []; |
d335fa16 AE |
86 | |
87 | /** | |
88 | * list of currently loaded application objects | |
7a23a706 | 89 | * @var IApplication[] |
d335fa16 | 90 | */ |
058cbd6a | 91 | protected static $applicationObjects = []; |
d335fa16 AE |
92 | |
93 | /** | |
94 | * list of autoload directories | |
95 | * @var array | |
96 | */ | |
058cbd6a | 97 | protected static $autoloadDirectories = []; |
d335fa16 AE |
98 | |
99 | /** | |
100 | * list of unique instances of each core object | |
7a23a706 | 101 | * @var SingletonFactory[] |
d335fa16 | 102 | */ |
058cbd6a | 103 | protected static $coreObject = []; |
d335fa16 AE |
104 | |
105 | /** | |
106 | * list of cached core objects | |
7a23a706 | 107 | * @var string[] |
d335fa16 | 108 | */ |
058cbd6a | 109 | protected static $coreObjectCache = []; |
d335fa16 AE |
110 | |
111 | /** | |
112 | * database object | |
4e25add7 | 113 | * @var MySQLDatabase |
d335fa16 AE |
114 | */ |
115 | protected static $dbObj = null; | |
116 | ||
117 | /** | |
118 | * language object | |
a59497a6 | 119 | * @var \wcf\data\language\Language |
d335fa16 AE |
120 | */ |
121 | protected static $languageObj = null; | |
122 | ||
123 | /** | |
124 | * overrides disabled debug mode | |
125 | * @var boolean | |
126 | */ | |
127 | protected static $overrideDebugMode = false; | |
128 | ||
129 | /** | |
130 | * session object | |
4e25add7 | 131 | * @var SessionHandler |
d335fa16 AE |
132 | */ |
133 | protected static $sessionObj = null; | |
134 | ||
135 | /** | |
136 | * template object | |
4e25add7 | 137 | * @var TemplateEngine |
d335fa16 AE |
138 | */ |
139 | protected static $tplObj = null; | |
140 | ||
141 | /** | |
142 | * true if Zend Opcache is loaded and enabled | |
143 | * @var boolean | |
144 | */ | |
145 | protected static $zendOpcacheEnabled = null; | |
146 | ||
ab84d9ca AE |
147 | /** |
148 | * force logout during destructor call | |
149 | * @var boolean | |
150 | */ | |
151 | protected static $forceLogout = false; | |
152 | ||
d335fa16 AE |
153 | /** |
154 | * Calls all init functions of the WCF class. | |
155 | */ | |
156 | public function __construct() { | |
157 | // add autoload directory | |
158 | self::$autoloadDirectories['wcf'] = WCF_DIR . 'lib/'; | |
159 | ||
160 | // define tmp directory | |
161 | if (!defined('TMP_DIR')) define('TMP_DIR', FileUtil::getTempFolder()); | |
162 | ||
163 | // start initialization | |
d335fa16 AE |
164 | $this->initDB(); |
165 | $this->loadOptions(); | |
166 | $this->initSession(); | |
167 | $this->initLanguage(); | |
168 | $this->initTPL(); | |
169 | $this->initCronjobs(); | |
170 | $this->initCoreObjects(); | |
171 | $this->initApplications(); | |
172 | $this->initBlacklist(); | |
173 | ||
174 | EventHandler::getInstance()->fireAction($this, 'initialized'); | |
175 | } | |
176 | ||
177 | /** | |
3ba197fa TD |
178 | * Flushes the output, closes the session, performs background tasks and more. |
179 | * | |
180 | * You *must* not create output in here under normal circumstances, as it might get eaten | |
181 | * when gzip is enabled. | |
d335fa16 AE |
182 | */ |
183 | public static function destruct() { | |
184 | try { | |
185 | // database has to be initialized | |
186 | if (!is_object(self::$dbObj)) return; | |
187 | ||
3ba197fa TD |
188 | $debug = self::debugModeIsEnabled(true); |
189 | if (!$debug) { | |
190 | // flush output | |
191 | if (ob_get_level()) ob_end_flush(); | |
d335fa16 | 192 | flush(); |
3ba197fa TD |
193 | |
194 | // close connection if using FPM | |
195 | if (function_exists('fastcgi_finish_request')) fastcgi_finish_request(); | |
d335fa16 AE |
196 | } |
197 | ||
198 | // update session | |
199 | if (is_object(self::getSession())) { | |
ab84d9ca AE |
200 | if (self::$forceLogout) { |
201 | // do logout | |
1eb97b3a | 202 | self::getSession()->delete(); |
ab84d9ca AE |
203 | } |
204 | else { | |
205 | self::getSession()->update(); | |
206 | } | |
d335fa16 AE |
207 | } |
208 | ||
11117cd5 AE |
209 | // execute shutdown actions of storage handlers |
210 | RegistryHandler::getInstance()->shutdown(); | |
d335fa16 AE |
211 | UserStorageHandler::getInstance()->shutdown(); |
212 | } | |
213 | catch (\Exception $exception) { | |
214 | die("<pre>WCF::destruct() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString()); | |
215 | } | |
216 | } | |
217 | ||
d335fa16 AE |
218 | /** |
219 | * Returns the database object. | |
220 | * | |
221 | * @return \wcf\system\database\Database | |
222 | */ | |
223 | public static final function getDB() { | |
224 | return self::$dbObj; | |
225 | } | |
226 | ||
227 | /** | |
228 | * Returns the session object. | |
229 | * | |
4e25add7 | 230 | * @return SessionHandler |
d335fa16 AE |
231 | */ |
232 | public static final function getSession() { | |
233 | return self::$sessionObj; | |
234 | } | |
235 | ||
236 | /** | |
237 | * Returns the user object. | |
238 | * | |
239 | * @return \wcf\data\user\User | |
240 | */ | |
241 | public static final function getUser() { | |
242 | return self::getSession()->getUser(); | |
243 | } | |
244 | ||
245 | /** | |
246 | * Returns the language object. | |
247 | * | |
248 | * @return \wcf\data\language\Language | |
249 | */ | |
250 | public static final function getLanguage() { | |
251 | return self::$languageObj; | |
252 | } | |
253 | ||
254 | /** | |
255 | * Returns the template object. | |
256 | * | |
4e25add7 | 257 | * @return TemplateEngine |
d335fa16 AE |
258 | */ |
259 | public static final function getTPL() { | |
260 | return self::$tplObj; | |
261 | } | |
262 | ||
263 | /** | |
264 | * Calls the show method on the given exception. | |
265 | * | |
266 | * @param \Exception $e | |
267 | */ | |
e09c7044 | 268 | public static final function handleException($e) { |
cbf81afa AE |
269 | // backwards compatibility |
270 | if ($e instanceof IPrintableException) { | |
271 | $e->show(); | |
272 | exit; | |
273 | } | |
274 | ||
f8434d25 | 275 | if (ob_get_level()) { |
1615fc2e | 276 | // discard any output generated before the exception occurred, prevents exception |
f8434d25 | 277 | // being hidden inside HTML elements and therefore not visible in browser output |
f46ac60e AE |
278 | // |
279 | // ob_get_level() can return values > 1, if the PHP setting `output_buffering` is on | |
280 | while (ob_get_level()) ob_end_clean(); | |
5f2c3ab4 | 281 | |
fef5bd1f AE |
282 | // Some webservers are broken and will apply gzip encoding at all cost, but they fail |
283 | // to set a proper `Content-Encoding` HTTP header and mess things up even more. | |
284 | // Especially the `identity` value appears to be unrecognized by some of them, hence | |
285 | // we'll just gzip the output of the exception to prevent them from tampering. | |
286 | // This part is copied from `HeaderUtil` in order to isolate the exception handler! | |
4d7a0b73 | 287 | if (defined('HTTP_ENABLE_GZIP') && HTTP_ENABLE_GZIP && !defined('HTTP_DISABLE_GZIP')) { |
ac8615e4 LK |
288 | if (function_exists('gzcompress') && !@ini_get('zlib.output_compression') && !@ini_get('output_handler') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) { |
289 | if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false) { | |
fef5bd1f AE |
290 | @header('Content-Encoding: x-gzip'); |
291 | } | |
292 | else { | |
293 | @header('Content-Encoding: gzip'); | |
294 | } | |
295 | ||
296 | ob_start(function($output) { | |
297 | $size = strlen($output); | |
298 | $crc = crc32($output); | |
299 | ||
300 | $newOutput = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff"; | |
301 | $newOutput .= substr(gzcompress($output, 1), 2, -4); | |
302 | $newOutput .= pack('V', $crc); | |
303 | $newOutput .= pack('V', $size); | |
304 | ||
305 | return $newOutput; | |
306 | }); | |
307 | } | |
308 | } | |
f8434d25 AE |
309 | } |
310 | ||
5bc5a7e6 | 311 | @header('HTTP/1.1 503 Service Unavailable'); |
d335fa16 | 312 | try { |
5bc5a7e6 | 313 | \wcf\functions\exception\printThrowable($e); |
d335fa16 | 314 | } |
5bc5a7e6 TD |
315 | catch (\Throwable $e2) { |
316 | echo "<pre>An Exception was thrown while handling an Exception:\n\n"; | |
6b7d6653 | 317 | echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e2); |
5bc5a7e6 | 318 | echo "\n\nwas thrown while:\n\n"; |
6b7d6653 | 319 | echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e); |
5bc5a7e6 TD |
320 | echo "\n\nwas handled.</pre>"; |
321 | exit; | |
e09c7044 | 322 | } |
5bc5a7e6 TD |
323 | catch (\Exception $e2) { |
324 | echo "<pre>An Exception was thrown while handling an Exception:\n\n"; | |
6b7d6653 | 325 | echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e2); |
5bc5a7e6 | 326 | echo "\n\nwas thrown while:\n\n"; |
6b7d6653 | 327 | echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e); |
5bc5a7e6 TD |
328 | echo "\n\nwas handled.</pre>"; |
329 | exit; | |
d335fa16 AE |
330 | } |
331 | } | |
332 | ||
333 | /** | |
b25ad8f3 | 334 | * Turns PHP errors into an ErrorException. |
d335fa16 | 335 | * |
ac52543a | 336 | * @param integer $severity |
d335fa16 | 337 | * @param string $message |
ac52543a MS |
338 | * @param string $file |
339 | * @param integer $line | |
2b770bdd | 340 | * @throws ErrorException |
d335fa16 | 341 | */ |
b25ad8f3 | 342 | public static final function handleError($severity, $message, $file, $line) { |
b2aa772d | 343 | // this is necessary for the shut-up operator |
b25ad8f3 TD |
344 | if (error_reporting() == 0) return; |
345 | ||
346 | throw new ErrorException($message, 0, $severity, $file, $line); | |
d335fa16 AE |
347 | } |
348 | ||
349 | /** | |
350 | * Loads the database configuration and creates a new connection to the database. | |
351 | */ | |
352 | protected function initDB() { | |
353 | // get configuration | |
354 | $dbHost = $dbUser = $dbPassword = $dbName = ''; | |
355 | $dbPort = 0; | |
d335fa16 AE |
356 | require(WCF_DIR.'config.inc.php'); |
357 | ||
358 | // create database connection | |
a2d822ad | 359 | self::$dbObj = new MySQLDatabase($dbHost, $dbUser, $dbPassword, $dbName, $dbPort); |
d335fa16 AE |
360 | } |
361 | ||
362 | /** | |
363 | * Loads the options file, automatically created if not exists. | |
364 | */ | |
365 | protected function loadOptions() { | |
366 | $filename = WCF_DIR.'options.inc.php'; | |
367 | ||
368 | // create options file if doesn't exist | |
369 | if (!file_exists($filename) || filemtime($filename) <= 1) { | |
370 | OptionEditor::rebuild(); | |
371 | } | |
11beb0a0 | 372 | require($filename); |
b842faa1 AE |
373 | |
374 | // check if option file is complete and writable | |
375 | if (PACKAGE_ID) { | |
376 | if (!is_writable($filename)) { | |
377 | FileUtil::makeWritable($filename); | |
378 | ||
379 | if (!is_writable($filename)) { | |
380 | throw new SystemException("The option file '" . $filename . "' is not writable."); | |
381 | } | |
382 | } | |
383 | ||
384 | // check if a previous write operation was incomplete and force rebuilding | |
385 | if (!defined('WCF_OPTION_INC_PHP_SUCCESS')) { | |
386 | OptionEditor::rebuild(); | |
387 | ||
11beb0a0 | 388 | require($filename); |
b842faa1 AE |
389 | } |
390 | } | |
d335fa16 AE |
391 | } |
392 | ||
393 | /** | |
394 | * Starts the session system. | |
395 | */ | |
396 | protected function initSession() { | |
f341086b | 397 | $factory = new SessionFactory(); |
d335fa16 AE |
398 | $factory->load(); |
399 | ||
f341086b | 400 | self::$sessionObj = SessionHandler::getInstance(); |
91082aee | 401 | self::$sessionObj->setHasValidCookie($factory->hasValidCookie()); |
d335fa16 AE |
402 | } |
403 | ||
404 | /** | |
405 | * Initialises the language engine. | |
406 | */ | |
407 | protected function initLanguage() { | |
408 | if (isset($_GET['l']) && !self::getUser()->userID) { | |
409 | self::getSession()->setLanguageID(intval($_GET['l'])); | |
410 | } | |
411 | ||
412 | // set mb settings | |
413 | mb_internal_encoding('UTF-8'); | |
414 | if (function_exists('mb_regex_encoding')) mb_regex_encoding('UTF-8'); | |
415 | mb_language('uni'); | |
416 | ||
417 | // get language | |
f341086b | 418 | self::$languageObj = LanguageFactory::getInstance()->getUserLanguage(self::getSession()->getLanguageID()); |
d335fa16 AE |
419 | } |
420 | ||
421 | /** | |
422 | * Initialises the template engine. | |
423 | */ | |
424 | protected function initTPL() { | |
f341086b | 425 | self::$tplObj = TemplateEngine::getInstance(); |
d335fa16 AE |
426 | self::getTPL()->setLanguageID(self::getLanguage()->languageID); |
427 | $this->assignDefaultTemplateVariables(); | |
428 | ||
429 | $this->initStyle(); | |
430 | } | |
431 | ||
432 | /** | |
433 | * Initializes the user's style. | |
434 | */ | |
435 | protected function initStyle() { | |
436 | if (isset($_REQUEST['styleID'])) { | |
437 | self::getSession()->setStyleID(intval($_REQUEST['styleID'])); | |
438 | } | |
439 | ||
f341086b | 440 | $styleHandler = StyleHandler::getInstance(); |
d11a8c9e | 441 | $styleHandler->changeStyle(self::getSession()->getStyleID()); |
d335fa16 AE |
442 | } |
443 | ||
444 | /** | |
445 | * Executes the blacklist. | |
446 | */ | |
447 | protected function initBlacklist() { | |
fa75ccfb AE |
448 | $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); |
449 | ||
d335fa16 | 450 | if (defined('BLACKLIST_IP_ADDRESSES') && BLACKLIST_IP_ADDRESSES != '') { |
dc0015ab | 451 | if (!StringUtil::executeWordFilter(UserUtil::convertIPv6To4(self::getSession()->ipAddress), BLACKLIST_IP_ADDRESSES)) { |
fa75ccfb | 452 | if ($isAjax) { |
a1721f86 | 453 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
454 | } |
455 | else { | |
456 | throw new PermissionDeniedException(); | |
457 | } | |
dc0015ab AE |
458 | } |
459 | else if (!StringUtil::executeWordFilter(self::getSession()->ipAddress, BLACKLIST_IP_ADDRESSES)) { | |
fa75ccfb | 460 | if ($isAjax) { |
a1721f86 | 461 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
462 | } |
463 | else { | |
464 | throw new PermissionDeniedException(); | |
465 | } | |
d335fa16 AE |
466 | } |
467 | } | |
468 | if (defined('BLACKLIST_USER_AGENTS') && BLACKLIST_USER_AGENTS != '') { | |
469 | if (!StringUtil::executeWordFilter(self::getSession()->userAgent, BLACKLIST_USER_AGENTS)) { | |
fa75ccfb | 470 | if ($isAjax) { |
a1721f86 | 471 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
472 | } |
473 | else { | |
474 | throw new PermissionDeniedException(); | |
475 | } | |
d335fa16 AE |
476 | } |
477 | } | |
478 | if (defined('BLACKLIST_HOSTNAMES') && BLACKLIST_HOSTNAMES != '') { | |
479 | if (!StringUtil::executeWordFilter(@gethostbyaddr(self::getSession()->ipAddress), BLACKLIST_HOSTNAMES)) { | |
fa75ccfb | 480 | if ($isAjax) { |
a1721f86 | 481 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
482 | } |
483 | else { | |
484 | throw new PermissionDeniedException(); | |
485 | } | |
d335fa16 AE |
486 | } |
487 | } | |
488 | ||
489 | // handle banned users | |
490 | if (self::getUser()->userID && self::getUser()->banned) { | |
fa75ccfb | 491 | if ($isAjax) { |
d335fa16 AE |
492 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned'), AJAXException::INSUFFICIENT_PERMISSIONS); |
493 | } | |
494 | else { | |
ab84d9ca AE |
495 | self::$forceLogout = true; |
496 | ||
497 | // remove cookies | |
498 | if (isset($_COOKIE[COOKIE_PREFIX.'userID'])) { | |
499 | HeaderUtil::setCookie('userID', 0); | |
500 | } | |
501 | if (isset($_COOKIE[COOKIE_PREFIX.'password'])) { | |
502 | HeaderUtil::setCookie('password', ''); | |
503 | } | |
504 | ||
d335fa16 AE |
505 | throw new NamedUserException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned')); |
506 | } | |
507 | } | |
508 | } | |
509 | ||
510 | /** | |
511 | * Initializes applications. | |
512 | */ | |
513 | protected function initApplications() { | |
514 | // step 1) load all applications | |
058cbd6a | 515 | $loadedApplications = []; |
d335fa16 AE |
516 | |
517 | // register WCF as application | |
39abe192 | 518 | self::$applications['wcf'] = ApplicationHandler::getInstance()->getApplicationByID(1); |
d335fa16 | 519 | |
39abe192 | 520 | if (!class_exists(WCFACP::class, false)) { |
b2b2c26b | 521 | static::getTPL()->assign('baseHref', self::$applications['wcf']->getPageURL()); |
39abe192 AE |
522 | } |
523 | ||
d335fa16 AE |
524 | // start main application |
525 | $application = ApplicationHandler::getInstance()->getActiveApplication(); | |
39abe192 AE |
526 | if ($application->packageID != 1) { |
527 | $loadedApplications[] = $this->loadApplication($application); | |
528 | ||
529 | // register primary application | |
530 | $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID); | |
531 | self::$applications[$abbreviation] = $application; | |
532 | } | |
d335fa16 AE |
533 | |
534 | // start dependent applications | |
535 | $applications = ApplicationHandler::getInstance()->getDependentApplications(); | |
536 | foreach ($applications as $application) { | |
39abe192 AE |
537 | if ($application->packageID == 1) { |
538 | // ignore WCF | |
539 | continue; | |
540 | } | |
f4eafe37 AE |
541 | else if ($application->isTainted) { |
542 | // ignore apps flagged for uninstallation | |
543 | continue; | |
544 | } | |
39abe192 | 545 | |
d335fa16 AE |
546 | $loadedApplications[] = $this->loadApplication($application, true); |
547 | } | |
548 | ||
549 | // step 2) run each application | |
550 | if (!class_exists('wcf\system\WCFACP', false)) { | |
f341086b | 551 | /** @var IApplication $application */ |
d335fa16 AE |
552 | foreach ($loadedApplications as $application) { |
553 | $application->__run(); | |
554 | } | |
555 | ||
556 | // refresh the session 1 minute before it expires | |
63b9817b | 557 | self::getTPL()->assign('__sessionKeepAlive', SESSION_TIMEOUT - 60); |
d335fa16 AE |
558 | } |
559 | } | |
560 | ||
561 | /** | |
562 | * Loads an application. | |
563 | * | |
2b770bdd MS |
564 | * @param Application $application |
565 | * @param boolean $isDependentApplication | |
566 | * @return IApplication | |
567 | * @throws SystemException | |
d335fa16 AE |
568 | */ |
569 | protected function loadApplication(Application $application, $isDependentApplication = false) { | |
d335fa16 AE |
570 | $package = PackageCache::getInstance()->getPackage($application->packageID); |
571 | // package cache might be outdated | |
572 | if ($package === null) { | |
573 | $package = new Package($application->packageID); | |
574 | ||
575 | // package cache is outdated, discard cache | |
576 | if ($package->packageID) { | |
577 | PackageEditor::resetCache(); | |
578 | } | |
579 | else { | |
580 | // package id is invalid | |
581 | throw new SystemException("application identified by package id '".$application->packageID."' is unknown"); | |
582 | } | |
583 | } | |
e54191ea | 584 | |
d335fa16 AE |
585 | $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID); |
586 | $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir); | |
587 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
588 | ||
589 | $className = $abbreviation.'\system\\'.strtoupper($abbreviation).'Core'; | |
59162cf3 AE |
590 | |
591 | // class was not found, possibly the app was moved, but `packageDir` has not been adjusted | |
592 | if (!class_exists($className)) { | |
593 | // check if both the Core and the app are on the same domain | |
594 | $coreApp = ApplicationHandler::getInstance()->getApplicationByID(1); | |
595 | if ($coreApp->domainName === $application->domainName) { | |
596 | // resolve the relative path and use it to construct the autoload directory | |
597 | $relativePath = FileUtil::getRelativePath($coreApp->domainPath, $application->domainPath); | |
598 | if ($relativePath !== './') { | |
599 | $packageDir = FileUtil::getRealPath(WCF_DIR.$relativePath); | |
600 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
601 | ||
602 | if (class_exists($className)) { | |
603 | // the class can now be found, update the `packageDir` value | |
604 | (new PackageEditor($package))->update(['packageDir' => $relativePath]); | |
605 | } | |
606 | } | |
607 | } | |
608 | } | |
609 | ||
157054c9 | 610 | if (class_exists($className) && is_subclass_of($className, IApplication::class)) { |
d335fa16 AE |
611 | // include config file |
612 | $configPath = $packageDir . PackageInstallationDispatcher::CONFIG_FILE; | |
b842faa1 AE |
613 | if (!file_exists($configPath)) { |
614 | Package::writeConfigFile($package->packageID); | |
615 | } | |
616 | ||
d335fa16 AE |
617 | if (file_exists($configPath)) { |
618 | require_once($configPath); | |
619 | } | |
620 | else { | |
621 | throw new SystemException('Unable to load configuration for '.$package->package); | |
622 | } | |
623 | ||
624 | // register template path if not within ACP | |
625 | if (!class_exists('wcf\system\WCFACP', false)) { | |
626 | // add template path and abbreviation | |
b2b2c26b | 627 | static::getTPL()->addApplication($abbreviation, $packageDir . 'templates/'); |
d335fa16 | 628 | } |
15621401 | 629 | EmailTemplateEngine::getInstance()->addApplication($abbreviation, $packageDir . 'templates/'); |
d335fa16 AE |
630 | |
631 | // init application and assign it as template variable | |
058cbd6a | 632 | self::$applicationObjects[$application->packageID] = call_user_func([$className, 'getInstance']); |
b2b2c26b | 633 | static::getTPL()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]); |
15621401 | 634 | EmailTemplateEngine::getInstance()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]); |
d335fa16 AE |
635 | } |
636 | else { | |
637 | unset(self::$autoloadDirectories[$abbreviation]); | |
157054c9 | 638 | throw new SystemException("Unable to run '".$package->package."', '".$className."' is missing or does not implement '".IApplication::class."'."); |
d335fa16 AE |
639 | } |
640 | ||
641 | // register template path in ACP | |
642 | if (class_exists('wcf\system\WCFACP', false)) { | |
b2b2c26b | 643 | static::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/'); |
d335fa16 AE |
644 | } |
645 | else if (!$isDependentApplication) { | |
646 | // assign base tag | |
b2b2c26b | 647 | static::getTPL()->assign('baseHref', $application->getPageURL()); |
d335fa16 AE |
648 | } |
649 | ||
650 | // register application | |
651 | self::$applications[$abbreviation] = $application; | |
652 | ||
653 | return self::$applicationObjects[$application->packageID]; | |
654 | } | |
655 | ||
656 | /** | |
657 | * Returns the corresponding application object. Does not support the 'wcf' pseudo application. | |
658 | * | |
4e25add7 MS |
659 | * @param Application $application |
660 | * @return IApplication | |
d335fa16 AE |
661 | */ |
662 | public static function getApplicationObject(Application $application) { | |
663 | if (isset(self::$applicationObjects[$application->packageID])) { | |
664 | return self::$applicationObjects[$application->packageID]; | |
665 | } | |
666 | ||
667 | return null; | |
668 | } | |
669 | ||
dbf41d8a AE |
670 | /** |
671 | * Returns the invoked application. | |
672 | * | |
673 | * @return Application | |
8e152e07 | 674 | * @since 3.1 |
dbf41d8a AE |
675 | */ |
676 | public static function getActiveApplication() { | |
677 | return ApplicationHandler::getInstance()->getActiveApplication(); | |
678 | } | |
679 | ||
d335fa16 AE |
680 | /** |
681 | * Loads an application on runtime, do not use this outside the package installation. | |
682 | * | |
683 | * @param integer $packageID | |
684 | */ | |
685 | public static function loadRuntimeApplication($packageID) { | |
686 | $package = new Package($packageID); | |
687 | $application = new Application($packageID); | |
688 | ||
689 | $abbreviation = Package::getAbbreviation($package->package); | |
690 | $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir); | |
691 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
692 | self::$applications[$abbreviation] = $application; | |
693 | self::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/'); | |
694 | } | |
695 | ||
696 | /** | |
697 | * Initializes core object cache. | |
698 | */ | |
699 | protected function initCoreObjects() { | |
700 | // ignore core objects if installing WCF | |
701 | if (PACKAGE_ID == 0) { | |
702 | return; | |
703 | } | |
704 | ||
705 | self::$coreObjectCache = CoreObjectCacheBuilder::getInstance()->getData(); | |
706 | } | |
707 | ||
708 | /** | |
709 | * Assigns some default variables to the template engine. | |
710 | */ | |
711 | protected function assignDefaultTemplateVariables() { | |
058cbd6a MS |
712 | self::getTPL()->registerPrefilter(['event', 'hascontent', 'lang']); |
713 | self::getTPL()->assign([ | |
d335fa16 | 714 | '__wcf' => $this, |
e71525e4 | 715 | '__wcfVersion' => LAST_UPDATE_TIME // @deprecated 2.1, use LAST_UPDATE_TIME directly |
058cbd6a | 716 | ]); |
54de7a5f TD |
717 | |
718 | $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); | |
719 | // Execute background queue in this request, if it was requested and AJAX isn't used. | |
720 | if (!$isAjax) { | |
721 | if (self::getSession()->getVar('forceBackgroundQueuePerform')) { | |
722 | self::getTPL()->assign([ | |
723 | 'forceBackgroundQueuePerform' => true | |
724 | ]); | |
725 | ||
726 | self::getSession()->unregister('forceBackgroundQueuePerform'); | |
727 | } | |
728 | } | |
729 | ||
15621401 TD |
730 | EmailTemplateEngine::getInstance()->registerPrefilter(['event', 'hascontent', 'lang']); |
731 | EmailTemplateEngine::getInstance()->assign([ | |
732 | '__wcf' => $this | |
733 | ]); | |
d335fa16 AE |
734 | } |
735 | ||
736 | /** | |
737 | * Wrapper for the getter methods of this class. | |
738 | * | |
739 | * @param string $name | |
740 | * @return mixed value | |
2b770bdd | 741 | * @throws SystemException |
d335fa16 AE |
742 | */ |
743 | public function __get($name) { | |
744 | $method = 'get'.ucfirst($name); | |
745 | if (method_exists($this, $method)) { | |
746 | return $this->$method(); | |
747 | } | |
748 | ||
749 | throw new SystemException("method '".$method."' does not exist in class WCF"); | |
750 | } | |
751 | ||
487db634 MW |
752 | /** |
753 | * Returns true if current application (WCF) is treated as active and was invoked directly. | |
754 | * | |
755 | * @return boolean | |
756 | */ | |
757 | public function isActiveApplication() { | |
758 | return (ApplicationHandler::getInstance()->getActiveApplication()->packageID == 1); | |
759 | } | |
760 | ||
d335fa16 AE |
761 | /** |
762 | * Changes the active language. | |
763 | * | |
764 | * @param integer $languageID | |
765 | */ | |
766 | public static final function setLanguage($languageID) { | |
5b8eae2c MW |
767 | if (!$languageID || LanguageFactory::getInstance()->getLanguage($languageID) === null) { |
768 | $languageID = LanguageFactory::getInstance()->getDefaultLanguageID(); | |
769 | } | |
2d90d60f | 770 | |
d335fa16 | 771 | self::$languageObj = LanguageFactory::getInstance()->getLanguage($languageID); |
b372bd53 AE |
772 | |
773 | // the template engine may not be available yet, usually happens when | |
774 | // changing the user (and thus the language id) during session init | |
775 | if (self::$tplObj !== null) { | |
776 | self::getTPL()->setLanguageID(self::getLanguage()->languageID); | |
777 | EmailTemplateEngine::getInstance()->setLanguageID(self::getLanguage()->languageID); | |
778 | } | |
d335fa16 AE |
779 | } |
780 | ||
781 | /** | |
782 | * Includes the required util or exception classes automatically. | |
783 | * | |
784 | * @param string $className | |
785 | * @see spl_autoload_register() | |
786 | */ | |
787 | public static final function autoload($className) { | |
788 | $namespaces = explode('\\', $className); | |
789 | if (count($namespaces) > 1) { | |
790 | $applicationPrefix = array_shift($namespaces); | |
791 | if ($applicationPrefix === '') { | |
792 | $applicationPrefix = array_shift($namespaces); | |
793 | } | |
794 | if (isset(self::$autoloadDirectories[$applicationPrefix])) { | |
795 | $classPath = self::$autoloadDirectories[$applicationPrefix] . implode('/', $namespaces) . '.class.php'; | |
796 | if (file_exists($classPath)) { | |
797 | require_once($classPath); | |
798 | } | |
799 | } | |
800 | } | |
801 | } | |
802 | ||
803 | /** | |
0fcfe5f6 | 804 | * @inheritDoc |
d335fa16 AE |
805 | */ |
806 | public final function __call($name, array $arguments) { | |
807 | // bug fix to avoid php crash, see http://bugs.php.net/bug.php?id=55020 | |
808 | if (!method_exists($this, $name)) { | |
809 | return self::__callStatic($name, $arguments); | |
810 | } | |
811 | ||
812 | return $this->$name($arguments); | |
813 | } | |
814 | ||
815 | /** | |
816 | * Returns dynamically loaded core objects. | |
817 | * | |
818 | * @param string $name | |
819 | * @param array $arguments | |
71952a87 | 820 | * @return object |
2b770bdd | 821 | * @throws SystemException |
d335fa16 AE |
822 | */ |
823 | public static final function __callStatic($name, array $arguments) { | |
824 | $className = preg_replace('~^get~', '', $name); | |
825 | ||
826 | if (isset(self::$coreObject[$className])) { | |
827 | return self::$coreObject[$className]; | |
828 | } | |
829 | ||
830 | $objectName = self::getCoreObject($className); | |
831 | if ($objectName === null) { | |
832 | throw new SystemException("Core object '".$className."' is unknown."); | |
833 | } | |
834 | ||
835 | if (class_exists($objectName)) { | |
63b9817b | 836 | if (!is_subclass_of($objectName, SingletonFactory::class)) { |
7b9ff46b | 837 | throw new ParentClassException($objectName, SingletonFactory::class); |
d335fa16 AE |
838 | } |
839 | ||
058cbd6a | 840 | self::$coreObject[$className] = call_user_func([$objectName, 'getInstance']); |
d335fa16 AE |
841 | return self::$coreObject[$className]; |
842 | } | |
843 | } | |
844 | ||
845 | /** | |
846 | * Searches for cached core object definition. | |
847 | * | |
848 | * @param string $className | |
849 | * @return string | |
850 | */ | |
851 | protected static final function getCoreObject($className) { | |
852 | if (isset(self::$coreObjectCache[$className])) { | |
853 | return self::$coreObjectCache[$className]; | |
854 | } | |
855 | ||
856 | return null; | |
857 | } | |
858 | ||
859 | /** | |
860 | * Returns true if the debug mode is enabled, otherwise false. | |
861 | * | |
862 | * @param boolean $ignoreACP | |
863 | * @return boolean | |
864 | */ | |
865 | public static function debugModeIsEnabled($ignoreACP = false) { | |
866 | // ACP override | |
867 | if (!$ignoreACP && self::$overrideDebugMode) { | |
868 | return true; | |
869 | } | |
870 | else if (defined('ENABLE_DEBUG_MODE') && ENABLE_DEBUG_MODE) { | |
871 | return true; | |
872 | } | |
873 | ||
874 | return false; | |
875 | } | |
876 | ||
877 | /** | |
878 | * Returns true if benchmarking is enabled, otherwise false. | |
879 | * | |
880 | * @return boolean | |
881 | */ | |
882 | public static function benchmarkIsEnabled() { | |
883 | // benchmarking is enabled by default | |
884 | if (!defined('ENABLE_BENCHMARK') || ENABLE_BENCHMARK) return true; | |
885 | return false; | |
886 | } | |
887 | ||
888 | /** | |
889 | * Returns domain path for given application. | |
890 | * | |
891 | * @param string $abbreviation | |
892 | * @return string | |
893 | */ | |
894 | public static function getPath($abbreviation = 'wcf') { | |
895 | // workaround during WCFSetup | |
896 | if (!PACKAGE_ID) { | |
897 | return '../'; | |
898 | } | |
899 | ||
900 | if (!isset(self::$applications[$abbreviation])) { | |
901 | $abbreviation = 'wcf'; | |
902 | } | |
903 | ||
904 | return self::$applications[$abbreviation]->getPageURL(); | |
905 | } | |
906 | ||
5d1c96bc AE |
907 | /** |
908 | * Returns the domain path for the currently active application, | |
909 | * used to avoid CORS requests. | |
910 | * | |
911 | * @return string | |
912 | */ | |
913 | public static function getActivePath() { | |
914 | if (!PACKAGE_ID) { | |
915 | return self::getPath(); | |
916 | } | |
917 | ||
918 | return self::getPath(ApplicationHandler::getInstance()->getAbbreviation(ApplicationHandler::getInstance()->getActiveApplication()->packageID)); | |
919 | } | |
920 | ||
d335fa16 AE |
921 | /** |
922 | * Returns a fully qualified anchor for current page. | |
923 | * | |
924 | * @param string $fragment | |
925 | * @return string | |
926 | */ | |
927 | public function getAnchor($fragment) { | |
694f6545 | 928 | return StringUtil::encodeHTML(self::getRequestURI() . '#' . $fragment); |
d335fa16 AE |
929 | } |
930 | ||
12a2ff01 AE |
931 | /** |
932 | * Returns the currently active page or null if unknown. | |
933 | * | |
b212ba66 | 934 | * @return Page|null |
12a2ff01 AE |
935 | */ |
936 | public static function getActivePage() { | |
a039ebd2 AE |
937 | if (self::getActiveRequest() === null) { |
938 | return null; | |
939 | } | |
940 | ||
5346b183 AE |
941 | if (self::getActiveRequest()->getClassName() === CmsPage::class) { |
942 | $metaData = self::getActiveRequest()->getMetaData(); | |
b212ba66 MS |
943 | if (isset($metaData['cms'])) { |
944 | return PageCache::getInstance()->getPage($metaData['cms']['pageID']); | |
945 | } | |
946 | ||
947 | return null; | |
5346b183 AE |
948 | } |
949 | ||
12a2ff01 AE |
950 | return PageCache::getInstance()->getPageByController(self::getActiveRequest()->getClassName()); |
951 | } | |
952 | ||
953 | /** | |
954 | * Returns the currently active request. | |
955 | * | |
956 | * @return Request | |
957 | */ | |
958 | public static function getActiveRequest() { | |
959 | return RequestHandler::getInstance()->getActiveRequest(); | |
960 | } | |
961 | ||
d335fa16 AE |
962 | /** |
963 | * Returns the URI of the current page. | |
964 | * | |
965 | * @return string | |
966 | */ | |
967 | public static function getRequestURI() { | |
c68f6161 | 968 | return preg_replace('~^(https?://[^/]+)(?:/.*)?$~', '$1', self::getTPL()->get('baseHref')) . $_SERVER['REQUEST_URI']; |
d335fa16 AE |
969 | } |
970 | ||
971 | /** | |
972 | * Resets Zend Opcache cache if installed and enabled. | |
973 | * | |
974 | * @param string $script | |
975 | */ | |
976 | public static function resetZendOpcache($script = '') { | |
977 | if (self::$zendOpcacheEnabled === null) { | |
978 | self::$zendOpcacheEnabled = false; | |
979 | ||
980 | if (extension_loaded('Zend Opcache') && @ini_get('opcache.enable')) { | |
981 | self::$zendOpcacheEnabled = true; | |
982 | } | |
983 | ||
984 | } | |
985 | ||
986 | if (self::$zendOpcacheEnabled) { | |
987 | if (empty($script)) { | |
988 | opcache_reset(); | |
989 | } | |
990 | else { | |
991 | opcache_invalidate($script, true); | |
992 | } | |
993 | } | |
994 | } | |
995 | ||
996 | /** | |
997 | * Returns style handler. | |
998 | * | |
4e25add7 | 999 | * @return StyleHandler |
d335fa16 AE |
1000 | */ |
1001 | public function getStyleHandler() { | |
1002 | return StyleHandler::getInstance(); | |
1003 | } | |
1004 | ||
55b402a0 MW |
1005 | /** |
1006 | * Returns box handler. | |
1007 | * | |
1008 | * @return BoxHandler | |
e71525e4 | 1009 | * @since 3.0 |
55b402a0 MW |
1010 | */ |
1011 | public function getBoxHandler() { | |
1012 | return BoxHandler::getInstance(); | |
1013 | } | |
1014 | ||
d335fa16 AE |
1015 | /** |
1016 | * Returns number of available updates. | |
1017 | * | |
1018 | * @return integer | |
1019 | */ | |
1020 | public function getAvailableUpdates() { | |
1021 | $data = PackageUpdateCacheBuilder::getInstance()->getData(); | |
1022 | return $data['updates']; | |
1023 | } | |
1024 | ||
1025 | /** | |
1026 | * Returns a 8 character prefix for editor autosave. | |
1027 | * | |
1028 | * @return string | |
1029 | */ | |
1030 | public function getAutosavePrefix() { | |
1031 | return substr(sha1(preg_replace('~^https~', 'http', self::getPath())), 0, 8); | |
1032 | } | |
1033 | ||
1034 | /** | |
1035 | * Returns the favicon URL or a base64 encoded image. | |
1036 | * | |
1037 | * @return string | |
1038 | */ | |
1039 | public function getFavicon() { | |
1040 | $activeApplication = ApplicationHandler::getInstance()->getActiveApplication(); | |
39abe192 | 1041 | $wcf = ApplicationHandler::getInstance()->getWCF(); |
aef117b5 | 1042 | $favicon = StyleHandler::getInstance()->getStyle()->getRelativeFavicon(); |
d335fa16 | 1043 | |
39abe192 | 1044 | if ($activeApplication->domainName !== $wcf->domainName) { |
aef117b5 AE |
1045 | if (file_exists(WCF_DIR.$favicon)) { |
1046 | $favicon = file_get_contents(WCF_DIR.$favicon); | |
d335fa16 AE |
1047 | |
1048 | return 'data:image/x-icon;base64,' . base64_encode($favicon); | |
1049 | } | |
1050 | } | |
1051 | ||
aef117b5 | 1052 | return self::getPath() . $favicon; |
d335fa16 AE |
1053 | } |
1054 | ||
6e4aa12f AE |
1055 | /** |
1056 | * Returns true if the desktop notifications should be enabled. | |
1057 | * | |
1058 | * @return boolean | |
1059 | */ | |
1060 | public function useDesktopNotifications() { | |
1061 | if (!ENABLE_DESKTOP_NOTIFICATIONS) { | |
1062 | return false; | |
1063 | } | |
1064 | else if (ApplicationHandler::getInstance()->isMultiDomainSetup()) { | |
1065 | $application = ApplicationHandler::getInstance()->getApplicationByID(DESKTOP_NOTIFICATION_PACKAGE_ID); | |
1066 | // mismatch, default to Core | |
1067 | if ($application === null) $application = ApplicationHandler::getInstance()->getApplicationByID(1); | |
1068 | ||
1069 | $currentApplication = ApplicationHandler::getInstance()->getActiveApplication(); | |
1070 | if ($currentApplication->domainName != $application->domainName) { | |
1071 | // different domain | |
1072 | return false; | |
1073 | } | |
1074 | } | |
1075 | ||
1076 | return true; | |
1077 | } | |
1078 | ||
a80873d5 AE |
1079 | /** |
1080 | * Returns true if currently active request represents the landing page. | |
1081 | * | |
8ff2cd79 | 1082 | * @return boolean |
a80873d5 | 1083 | */ |
0fdd7f96 | 1084 | public static function isLandingPage() { |
a039ebd2 AE |
1085 | if (self::getActiveRequest() === null) { |
1086 | return false; | |
1087 | } | |
1088 | ||
1089 | return self::getActiveRequest()->isLandingPage(); | |
a80873d5 AE |
1090 | } |
1091 | ||
89484ba0 AE |
1092 | /** |
1093 | * Returns true if the given API version is currently supported. | |
1094 | * | |
1095 | * @param integer $apiVersion | |
1096 | * @return boolean | |
1097 | */ | |
1098 | public static function isSupportedApiVersion($apiVersion) { | |
1099 | return ($apiVersion == WSC_API_VERSION) || in_array($apiVersion, self::$supportedLegacyApiVersions); | |
1100 | } | |
1101 | ||
1102 | /** | |
1103 | * Returns the list of supported legacy API versions. | |
1104 | * | |
1105 | * @return integer[] | |
1106 | */ | |
1107 | public static function getSupportedLegacyApiVersions() { | |
1108 | return self::$supportedLegacyApiVersions; | |
1109 | } | |
1110 | ||
d335fa16 AE |
1111 | /** |
1112 | * Initialises the cronjobs. | |
1113 | */ | |
1114 | protected function initCronjobs() { | |
1115 | if (PACKAGE_ID) { | |
63b9817b | 1116 | self::getTPL()->assign('executeCronjobs', CronjobScheduler::getInstance()->getNextExec() < TIME_NOW && defined('OFFLINE') && !OFFLINE); |
d335fa16 AE |
1117 | } |
1118 | } | |
1119 | } |