Merge branch 'master' into next
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / WCFACP.class.php
1 <?php
2 declare(strict_types=1);
3 namespace wcf\system;
4 use wcf\acp\form\MasterPasswordForm;
5 use wcf\acp\form\MasterPasswordInitForm;
6 use wcf\data\menu\Menu;
7 use wcf\data\menu\MenuCache;
8 use wcf\system\application\ApplicationHandler;
9 use wcf\system\cache\builder\ACPSearchProviderCacheBuilder;
10 use wcf\system\event\EventHandler;
11 use wcf\system\exception\AJAXException;
12 use wcf\system\exception\PermissionDeniedException;
13 use wcf\system\exception\SystemException;
14 use wcf\system\request\LinkHandler;
15 use wcf\system\request\RouteHandler;
16 use wcf\system\session\ACPSessionFactory;
17 use wcf\system\session\SessionHandler;
18 use wcf\system\template\ACPTemplateEngine;
19 use wcf\util\FileUtil;
20 use wcf\util\HeaderUtil;
21
22 /**
23 * Extends WCF class with functions for the ACP.
24 *
25 * @author Marcel Werk
26 * @copyright 2001-2018 WoltLab GmbH
27 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
28 * @package WoltLabSuite\Core\System
29 */
30 class WCFACP extends WCF {
31 /**
32 * rescue mode
33 * @var boolean
34 */
35 protected static $inRescueMode;
36
37 /**
38 * URL to WCF within rescue mode
39 * @var string
40 */
41 protected static $rescueModePageURL;
42
43 /** @noinspection PhpMissingParentConstructorInspection */
44 /**
45 * Calls all init functions of the WCF and the WCFACP class.
46 */
47 public function __construct() {
48 // add autoload directory
49 self::$autoloadDirectories['wcf'] = WCF_DIR . 'lib/';
50
51 // define tmp directory
52 if (!defined('TMP_DIR')) define('TMP_DIR', FileUtil::getTempFolder());
53
54 // start initialization
55 $this->initDB();
56 $this->loadOptions();
57 $this->initPackage();
58 $this->initSession();
59 $this->initLanguage();
60 $this->initTPL();
61 $this->initCronjobs();
62 $this->initCoreObjects();
63
64 // prevent application loading during setup
65 if (PACKAGE_ID) {
66 $this->initApplications();
67 }
68
69 $this->initBlacklist();
70 $this->initAuth();
71
72 EventHandler::getInstance()->fireAction($this, 'initialized');
73 }
74
75 /**
76 * Returns the main menu object.
77 *
78 * @return Menu|null menu object
79 * @since 3.0
80 */
81 public function getFrontendMenu() {
82 return MenuCache::getInstance()->getMainMenu();
83 }
84
85 /**
86 * Returns true if ACP is currently in rescue mode.
87 *
88 * @return boolean
89 */
90 public static function inRescueMode() {
91 if (self::$inRescueMode === null) {
92 self::$inRescueMode = false;
93
94 if (PACKAGE_ID && isset($_SERVER['HTTP_HOST'])) {
95 self::$inRescueMode = true;
96
97 foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
98 if ($application->domainName === $_SERVER['HTTP_HOST']) {
99 self::$inRescueMode = false;
100 break;
101 }
102 }
103
104 if (self::$inRescueMode) {
105 self::$rescueModePageURL = RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . RouteHandler::getPath(['acp']);
106 }
107 }
108 }
109
110 return self::$inRescueMode;
111 }
112
113 /**
114 * Returns URL for rescue mode page.
115 *
116 * @return string
117 */
118 public static function getRescueModePageURL() {
119 if (self::inRescueMode()) {
120 return self::$rescueModePageURL;
121 }
122
123 return '';
124 }
125
126 /**
127 * Does the user authentication.
128 */
129 protected function initAuth() {
130 // this is a work-around since neither RequestHandler
131 // nor RouteHandler are populated right now
132 $pathInfo = RouteHandler::getPathInfo();
133
134 if (self::inRescueMode()) {
135 if (!preg_match('~^/?rescue-mode/~', $pathInfo)) {
136 $redirectURI = self::$rescueModePageURL . 'acp/index.php?rescue-mode/';
137
138 HeaderUtil::redirect($redirectURI);
139 exit;
140 }
141 }
142 else if (empty($pathInfo) || !preg_match('~^/?(acp-?captcha|login|logout)/~i', $pathInfo)) {
143 if (WCF::getUser()->userID == 0) {
144 // work-around for AJAX-requests within ACP
145 if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
146 throw new AJAXException(WCF::getLanguage()->getDynamicVariable('wcf.ajax.error.sessionExpired'), AJAXException::SESSION_EXPIRED, '');
147 }
148
149 // build redirect path
150 $application = ApplicationHandler::getInstance()->getActiveApplication();
151 if ($application === null) {
152 throw new SystemException("You have aborted the installation, therefore this installation is unusable. You are required to reinstall the software.");
153 }
154
155 HeaderUtil::redirect(
156 LinkHandler::getInstance()->getLink('Login', [
157 'url' => RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . WCF::getSession()->requestURI
158 ])
159 );
160 exit;
161 }
162 else {
163 // work-around for AJAX-requests within ACP
164 if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
165 try {
166 WCF::getSession()->checkPermissions(['admin.general.canUseAcp']);
167 }
168 catch (PermissionDeniedException $e) {
169 throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS, $e->getTraceAsString());
170 }
171 }
172 else {
173 WCF::getSession()->checkPermissions(['admin.general.canUseAcp']);
174 }
175
176 // force debug mode if in ACP and authenticated
177 self::$overrideDebugMode = true;
178 }
179 }
180 }
181
182 /**
183 * @inheritDoc
184 */
185 protected function initSession() {
186 self::$sessionObj = SessionHandler::getInstance();
187 self::$sessionObj->setCookieSuffix('_acp');
188
189 $factory = new ACPSessionFactory();
190 $factory->load();
191
192 self::$sessionObj->setHasValidCookie($factory->hasValidCookie());
193 }
194
195 /**
196 * @inheritDoc
197 */
198 protected function initTPL() {
199 self::$tplObj = ACPTemplateEngine::getInstance();
200 self::getTPL()->setLanguageID(self::getLanguage()->languageID);
201 $this->assignDefaultTemplateVariables();
202 }
203
204 /**
205 * @inheritDoc
206 */
207 protected function assignDefaultTemplateVariables() {
208 parent::assignDefaultTemplateVariables();
209
210 // base tag is determined on runtime
211 $host = RouteHandler::getHost();
212 $path = RouteHandler::getPath();
213
214 // available acp search providers
215 $availableAcpSearchProviders = [];
216 foreach (ACPSearchProviderCacheBuilder::getInstance()->getData() as $searchProvider) {
217 $availableAcpSearchProviders[$searchProvider->providerName] = self::getLanguage()->get('wcf.acp.search.provider.'.$searchProvider->providerName);
218 }
219 asort($availableAcpSearchProviders);
220
221 self::getTPL()->assign([
222 'baseHref' => $host . $path,
223 'availableAcpSearchProviders' => $availableAcpSearchProviders
224 ]);
225 }
226
227 /**
228 * Initializes the active package.
229 */
230 protected function initPackage() {
231 // define active package id
232 if (!defined('PACKAGE_ID')) {
233 define('PACKAGE_ID', 1);
234 }
235 }
236
237 /**
238 * Checks whether the active user has entered the valid master password.
239 */
240 public static function checkMasterPassword() {
241 if (defined('MODULE_MASTER_PASSWORD') && MODULE_MASTER_PASSWORD == 1 && !WCF::getSession()->getVar('masterPassword')) {
242 if (file_exists(WCF_DIR.'acp/masterPassword.inc.php')) {
243 require_once(WCF_DIR.'acp/masterPassword.inc.php');
244 }
245 if (defined('MASTER_PASSWORD')) {
246 $form = new MasterPasswordForm();
247 $form->__run();
248 exit;
249 }
250 else {
251 $form = new MasterPasswordInitForm();
252 $form->__run();
253 exit;
254 }
255 }
256 }
257 }