1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\Modules\CoreWebclient;
9:
10: use Aurora\Api;
11: use Aurora\System\Application;
12: use Aurora\System\Router;
13:
14: /**
15: * System module that provides Web application core functionality and UI framework.
16: *
17: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
18: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
19: * @copyright Copyright (c) 2023, Afterlogic Corp.
20: *
21: * @package Modules
22: */
23: class Module extends \Aurora\System\Module\AbstractWebclientModule
24: {
25: /***** private functions *****/
26: /**
27: * Initializes CoreWebclient Module.
28: *
29: * @ignore
30: */
31: public function init()
32: {
33: \Aurora\System\Router::getInstance()->registerArray(
34: self::GetName(),
35: [
36: 'default' => [$this, 'EntryDefault'],
37: 'error' => [$this, 'EntryDefault'],
38: 'debugmode' => [$this, 'EntryDefault'],
39: 'xdebug_session_start' => [$this, 'EntryDefault'],
40: 'install' => [$this, 'EntryCompatibility']
41: ]
42: );
43:
44: $this->subscribeEvent('Core::UpdateSettings::after', array($this, 'onAfterUpdateSettings'));
45:
46: $this->denyMethodsCallByWebApi([
47: 'SetHtmlOutputHeaders',
48: ]);
49: }
50:
51: /**
52: *
53: * @param array $aSystemList
54: * @return array
55: */
56: private function getLanguageList($aSystemList)
57: {
58: $aResultList = [];
59: $aLanguageNames = $this->getConfig('LanguageNames', false);
60: foreach ($aSystemList as $sLanguage) {
61: if (isset($aLanguageNames[$sLanguage])) {
62: $aResultList[] = [
63: 'name' => $aLanguageNames[$sLanguage],
64: 'value' => $sLanguage
65: ];
66: } else {
67: $aResultList[] = [
68: 'name' => $sLanguage,
69: 'value' => $sLanguage
70: ];
71: }
72: }
73: return $aResultList;
74: }
75: /***** private functions *****/
76:
77: /***** public functions *****/
78: /**
79: *
80: * @return array
81: */
82: public function GetSettings()
83: {
84: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
85:
86: $oUser = \Aurora\System\Api::getAuthenticatedUser();
87: $oIntegrator = \Aurora\System\Api::GetModule('Core')->getIntegratorManager();
88:
89: return array(
90: 'AllowChangeSettings' => $this->getConfig('AllowChangeSettings', false),
91: 'AllowClientDebug' => $this->getConfig('AllowClientDebug', false),
92: 'AllowDesktopNotifications' => $oUser && isset($oUser->{self::GetName().'::AllowDesktopNotifications'}) ? $oUser->{self::GetName().'::AllowDesktopNotifications'} : $this->getConfig('AllowDesktopNotifications', false),
93: 'AllowMobile' => $this->getConfig('AllowMobile', false),
94: 'AllowPrefetch' => $this->getConfig('AllowPrefetch', true),
95: 'AttachmentSizeLimit' => $this->getConfig('AttachmentSizeLimit', 0),
96: 'AutoRefreshIntervalMinutes' => $oUser && isset($oUser->{self::GetName().'::AutoRefreshIntervalMinutes'}) ? $oUser->{self::GetName().'::AutoRefreshIntervalMinutes'} : $this->getConfig('AutoRefreshIntervalMinutes', 0),
97: 'CustomLogoutUrl' => $this->getConfig('CustomLogoutUrl', ''),
98: 'DefaultAnonymScreenHash' => $this->getConfig('DefaultAnonymScreenHash', ''),
99: 'DefaultUserScreenHash' => $this->getConfig('DefaultUserScreenHash', ''),
100: 'GoogleAnalyticsAccount' => $this->getConfig('GoogleAnalyticsAccount', ''),
101: 'HeaderModulesOrder' => $this->getConfig('HeaderModulesOrder', []),
102: 'IsDemo' => $this->getConfig('IsDemo', false),
103: 'IsMobile' => $oIntegrator->isMobile(),
104: 'LanguageListWithNames' => $this->getLanguageList($oIntegrator->getLanguageList()),
105: 'MultipleFilesUploadLimit' => $this->getConfig('MultipleFilesUploadLimit', 50),
106: 'ShowQuotaBar' => $this->getConfig('ShowQuotaBar', false),
107: 'ShowQuotaBarTextAsTooltip' => $this->getConfig('ShowQuotaBarTextAsTooltip', true),
108: 'QuotaWarningPerc' => $this->getConfig('QuotaWarningPerc', 0),
109: 'Theme' => $oUser && isset($oUser->{self::GetName().'::Theme'}) ? $oUser->{self::GetName().'::Theme'} : $this->getConfig('Theme', 'Default'),
110: 'ThemeList' => $this->getConfig('ThemeList', ['Default']),
111: 'HideLogout' => $this->getConfig('HideLogout', false),
112: 'BaseUrl' => Application::getBaseUrl(),
113: );
114: }
115:
116: /**
117: *
118: * @param array $Args
119: * @param mixed $Result
120: */
121: public function onAfterUpdateSettings($Args, &$Result)
122: {
123: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
124:
125: $oUser = \Aurora\System\Api::getAuthenticatedUser();
126: if ($oUser && $oUser->isNormalOrTenant()) {
127: if (isset($Args['AllowDesktopNotifications'])) {
128: $oUser->setExtendedProp(self::GetName().'::AllowDesktopNotifications', $Args['AllowDesktopNotifications']);
129: }
130: if (isset($Args['AutoRefreshIntervalMinutes'])) {
131: $oUser->setExtendedProp(self::GetName().'::AutoRefreshIntervalMinutes', $Args['AutoRefreshIntervalMinutes']);
132: }
133: if (isset($Args['Theme'])) {
134: $oUser->setExtendedProp(self::GetName().'::Theme', $Args['Theme']);
135: }
136:
137: $oCoreDecorator = \Aurora\Modules\Core\Module::Decorator();
138: $oCoreDecorator->UpdateUserObject($oUser);
139: }
140:
141: if ($oUser && $oUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) {
142: if (isset($Args['Theme'])) {
143: $this->setConfig('Theme', $Args['Theme']);
144: }
145: $Result = $this->saveModuleConfig();
146: }
147: }
148:
149: /**
150: * @ignore
151: */
152: public function SetHtmlOutputHeaders()
153: {
154: @\header('Content-Type: text/html; charset=utf-8', true);
155: $sContentSecurityPolicy = $this->getConfig('ContentSecurityPolicy', '');
156: if (!empty($sContentSecurityPolicy)) {
157: $aArgs = [];
158: $aAddDefault = [];
159: $this->broadcastEvent(
160: 'AddToContentSecurityPolicyDefault',
161: $aArgs,
162: $aAddDefault
163: );
164: if (!empty($aAddDefault)) {
165: $aPieces = explode(';', $sContentSecurityPolicy);
166: foreach ($aPieces as $iIndex => $sPiece) {
167: $sPrepared = strtolower(trim($sPiece));
168: if (strpos($sPrepared, 'default-src') === 0) {
169: $aPieces[$iIndex] = implode(' ', array_merge([$sPiece], $aAddDefault));
170: }
171: }
172: $sContentSecurityPolicy = implode(';', $aPieces);
173: }
174: @\header('Content-Security-Policy: ' . $sContentSecurityPolicy, true);
175: }
176: }
177:
178: /**
179: * @ignore
180: */
181: public function EntryDefault()
182: {
183: $sResult = '';
184:
185: $oIntegrator = \Aurora\System\Managers\Integrator::getInstance();
186:
187: self::Decorator()->SetHtmlOutputHeaders();
188:
189: $sUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
190: if (!\strpos(\strtolower($sUserAgent), 'firefox')) {
191: @\header('Last-Modified: '.\gmdate('D, d M Y H:i:s').' GMT');
192: }
193:
194: $oSettings =& \Aurora\System\Api::GetSettings();
195: if ($oSettings) {
196: if (($oSettings->GetValue('CacheCtrl', true) && isset($_COOKIE['aft-cache-ctrl']))) {
197: @\setcookie(
198: 'aft-cache-ctrl',
199: '',
200: \strtotime('-1 hour'),
201: \Aurora\System\Api::getCookiePath(),
202: null,
203: \Aurora\System\Api::getCookieSecure()
204: );
205: \MailSo\Base\Http::SingletonInstance()->StatusHeader(304);
206: exit();
207: }
208: }
209:
210: $sResult = \file_get_contents($this->GetPath().'/templates/Index.html');
211: if (\is_string($sResult)) {
212: if ($oSettings) {
213: $sFrameOptions = $oSettings->GetValue('XFrameOptions', '');
214: if (0 < \strlen($sFrameOptions)) {
215: @\header('X-Frame-Options: '.$sFrameOptions);
216: }
217: }
218:
219: $sResult = strtr($sResult, array(
220: '{{AppVersion}}' => AU_APP_VERSION,
221: '{{IntegratorDir}}' => $oIntegrator->isRtl() ? 'rtl' : 'ltr',
222: '{{IntegratorLinks}}' => $oIntegrator->buildHeadersLink(),
223: '{{IntegratorBody}}' => $oIntegrator->buildBody()
224: ));
225: }
226:
227:
228: return $sResult;
229: }
230:
231: /**
232: * @ignore
233: */
234: public function EntryCompatibility()
235: {
236: $mResult = '';
237: if (basename(\MailSo\Base\Http::SingletonInstance()->GetFullUrl()) !== 'adminpanel') { //TODO
238: \header("Location: ./");
239: }
240:
241: $aCompatibilities = \Aurora\System\Api::GetModuleDecorator('Core')->GetCompatibilities();
242: $sContent = '';
243: $bResult = true;
244: foreach ($aCompatibilities as $sModule => $aItems) {
245: $sContent .= "<div class=\"row\">
246: <h2>Module: " . $sModule . "</h2>
247: </div><br />";
248: foreach ($aItems as $aItem) {
249: $sValue = '';
250: if ($aItem['Result']) {
251: $sValue = $this->getSuccessHtmlValue($aItem['Value']);
252: } else {
253: if (is_array($aItem['Value']) && count($aItem['Value']) > 0) {
254: $sValue = $this->getErrorHtmlValue($aItem['Value'][0], isset($aItem['Value'][1]) ? $aItem['Value'][1] : '');
255: }
256: }
257: $sContent .= "<div class=\"row\">
258: <span class=\"field_label\"><b>" . $aItem['Name'] . ":</b> </span>
259: <span class=\"field_value_limit\">" . $sValue . "</span>
260: </div>";
261: $bResult &= $aItem['Result'];
262: }
263: }
264: $sContent .= "<br />";
265:
266: $sPath = $this->GetPath().'/templates/Compatibility.html';
267: if (\file_exists($sPath)) {
268: $sResult = \file_get_contents($sPath);
269: if (\is_string($sResult)) {
270: $sResult = strtr($sResult, array(
271: '{{Compatibilities}}' => $sContent,
272: '{{Result}}' => $bResult ?
273: 'The current server environment meets all the requirements. Click Next to proceed.' :
274: 'Please make sure that all the requirements are met and click Retry.',
275:
276: '{{NextButtonHref}}' => ($bResult) ? './' : './?install',
277: '{{ResultClassSuffix}}' => ($bResult) ? '_ok' : '_error',
278: '{{NextButtonName}}' => ($bResult) ? 'next_btn' : 'retry_btn',
279: '{{NextButtonValue}}' => ($bResult) ? 'Next' : 'Retry'
280:
281: ));
282:
283: $mResult = $sResult;
284: }
285: }
286:
287: return $mResult;
288: }
289:
290: protected function getSuccessHtmlValue($sValue)
291: {
292: return '<span class="state_ok">'.$sValue.'</span>';
293: }
294:
295: protected function getErrorHtmlValue($sError, $sErrorHelp = '')
296: {
297: $sResult = '<span class="state_error">'.$sError.'</span>';
298: if (!empty($sErrorHelp)) {
299: $sResult .= '<span class="field_description">'.$sErrorHelp.'</span>';
300: }
301: return $sResult;
302: }
303:
304: protected function getWarningHtmlValue($sVarning, $sVarningHelp = '')
305: {
306: $sResult = '<span class="state_warning"><img src="./images/alarm.png"> Not detected. <br />'.$sVarning.'</span>';
307: if (!empty($sVarningHelp)) {
308: $sResult .= '<span class="field_description">'.$sVarningHelp.'</span>';
309: }
310: return $sResult;
311: }
312: }
313: