Index: core/admin_templates/config/custom_variables.tpl =================================================================== --- core/admin_templates/config/custom_variables.tpl (revision 14917) +++ core/admin_templates/config/custom_variables.tpl (working copy) @@ -82,3 +82,21 @@ - + + + + + + + + \ No newline at end of file Index: core/admin_templates/login.tpl =================================================================== --- core/admin_templates/login.tpl (revision 14917) +++ core/admin_templates/login.tpl (working copy) @@ -126,33 +126,37 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + Index: core/install/english.lang =================================================================== --- core/install/english.lang (revision 14929) +++ core/install/english.lang (working copy) @@ -33,6 +33,7 @@ RWRpdCBDb250ZW50 RWRpdCBEZXNpZ24= R2VuZXJhdGU= + R2VuZXJhdGUgUGFnZQ== R2V0IFZhbHVl TG9jYXRl TW92ZSBEb3du @@ -162,6 +163,7 @@ UmVkaXJlY3QgdG8gSFRUUCB3aGVuIFNTTCBpcyBub3QgcmVxdWlyZWQ= RnVsbCBpbWFnZSBIZWlnaHQ= RnVsbCBpbWFnZSBXaWR0aA== + VGVtcGxhdGUgZm9yIEhhcmQgTWFpbnRlbmFuY2U= QnlwYXNzIEhUVFAgQXV0aGVudGljYXRpb24gZnJvbSBJUHMgKHNlcGFyYXRlZCBieSBzZW1pY29sb25zKQ== UGFzc3dvcmQgZm9yIEhUVFAgQXV0aGVudGljYXRpb24= VXNlcm5hbWUgZm9yIEhUVFAgQXV0aGVudGljYXRpb24= @@ -169,6 +171,8 @@ TWFpbCBGdW5jdGlvbiBIZWFkZXIgU2VwYXJhdG9y TWFpbGluZyBMaXN0IFF1ZXVlIFBlciBTdGVw TWFpbGluZyBMaXN0IFNlbmQgUGVyIFN0ZXA= + TWFpbnRlbmFuY2UgTWVzc2FnZSBmb3IgQWRtaW4= + TWFpbnRlbmFuY2UgTWVzc2FnZSBmb3IgRnJvbnQgRW5k TWF4aW11bSBudW1iZXIgb2YgaW1hZ2Vz RGVmYXVsdCBVUkwgRW5kaW5nIGluIFNFTy1mcmllbmRseSBtb2Rl VGVtcGxhdGUgZm9yICJJbnN1ZmZpY2llbnQgUGVybWlzc2lvbnMiIEVycm9y @@ -189,6 +193,7 @@ U2Vzc2lvbiBTZWN1cml0eSBDaGVjayBiYXNlZCBvbiBJUA== V2Vic2l0ZSBTdWJ0aXRsZQ== VGltZSB6b25lIG9mIHRoZSBzaXRl + VGVtcGxhdGUgZm9yIFNvZnQgTWFpbnRlbmFuY2U= U1NMIEZ1bGwgVVJMIChodHRwczovL3d3dy5kb21haW4uY29tL3BhdGgp VXNlIFN0aWNreSBHcmlkIFNlbGVjdGlvbg== VGh1bWJuYWlsIEhlaWdodA== @@ -1038,6 +1043,7 @@ QWRtaW4gQ29uc29sZSBTZXR0aW5ncw== Q1NWIEV4cG9ydCBTZXR0aW5ncw== TWFpbGluZyBTZXR0aW5ncw== + TWFpbnRlbmFuY2UgU2V0dGluZ3M= U2Vzc2lvbiBTZXR0aW5ncw== U1NMIFNldHRpbmdz U3lzdGVtIFNldHRpbmdz Index: core/install/install_data.sql =================================================================== --- core/install/install_data.sql (revision 14929) +++ core/install/install_data.sql (working copy) @@ -42,6 +42,10 @@ INSERT INTO ConfigurationValues VALUES(DEFAULT, 'UseVisitorTracking', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_UseVisitorTracking', 'checkbox', '', '', 10.08, 0, 0, NULL); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'cms_DefaultTrackingCode', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_DefaultTrackingCode', 'textarea', NULL, 'COLS=40 ROWS=5', 10.09, 0, 0, NULL); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'PerformExactSearch', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_PerformExactSearch', 'checkbox', '', '', '10.10', 0, 0, 'hint:la_config_PerformExactSearch'); +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'MaintenanceMessageFront', 'Website is currently undergoing the upgrades. Please come back shortly!', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_MaintenanceMessageFront', 'textarea', '', 'style="width: 100%; height: 100px;"', '15.01', 0, 0, 'hint:la_config_MaintenanceMessageFront'); +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'MaintenanceMessageAdmin', 'Website is currently undergoing the upgrades. Please come back shortly!', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_MaintenanceMessageAdmin', 'textarea', '', 'style="width: 100%; height: 100px;"', '15.02', 0, 0, 'hint:la_config_MaintenanceMessageAdmin'); +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'SoftMaintenanceTemplate', 'maintenance', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_SoftMaintenanceTemplate', 'text', '', 'style="width: 200px;"', '15.03', 0, 0, 'hint:la_config_SoftMaintenanceTemplate'); +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'HardMaintenanceTemplate', 'maintenance', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_HardMaintenanceTemplate', 'text', '', 'style="width: 200px;"', '15.04', 0, 0, 'hint:la_config_HardMaintenanceTemplate'); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'CookieSessions', '2', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_prompt_session_management', 'select', NULL, '0=lu_opt_QueryString||1=lu_opt_Cookies||2=lu_opt_AutoDetect', 20.01, 0, 1, NULL); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'SessionCookieName', 'sid', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_prompt_session_cookie_name', 'text', '', '', 20.02, 0, 1, NULL); INSERT INTO ConfigurationValues VALUES(DEFAULT, 'SessionCookieDomains', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_config_SessionCookieDomains', 'textarea', '', 'rows="5" cols="40"', 20.021, 0, 0, NULL); Index: core/install/upgrades.sql =================================================================== --- core/install/upgrades.sql (revision 14929) +++ core/install/upgrades.sql (working copy) @@ -2473,3 +2473,8 @@ INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:promo_block_groups.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:promo_block_groups.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:promo_block_groups.delete', 11, 1, 1, 0); + +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'MaintenanceMessageFront', 'Website is currently undergoing the upgrades. Please come back shortly!', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_MaintenanceMessageFront', 'textarea', '', 'style="width: 100%; height: 100px;"', '15.01', 0, 0, 'hint:la_config_MaintenanceMessageFront'); +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'MaintenanceMessageAdmin', 'Website is currently undergoing the upgrades. Please come back shortly!', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_MaintenanceMessageAdmin', 'textarea', '', 'style="width: 100%; height: 100px;"', '15.02', 0, 0, 'hint:la_config_MaintenanceMessageAdmin'); +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'SoftMaintenanceTemplate', 'maintenance', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_SoftMaintenanceTemplate', 'text', '', 'style="width: 200px;"', '15.03', 0, 0, 'hint:la_config_SoftMaintenanceTemplate'); +INSERT INTO ConfigurationValues VALUES(DEFAULT, 'HardMaintenanceTemplate', 'maintenance', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMaintenance', 'la_config_HardMaintenanceTemplate', 'text', '', 'style="width: 200px;"', '15.04', 0, 0, 'hint:la_config_HardMaintenanceTemplate'); Index: core/kernel/application.php =================================================================== --- core/kernel/application.php (revision 14921) +++ core/kernel/application.php (working copy) @@ -921,6 +921,21 @@ } /** + * Performs redirect to hard maintenance template + * + * @access public + */ + public function redirectToMaintenance() + { + $maintenance_page = WRITEBALE_BASE . '/maintenance.html'; + + if ( file_exists(FULL_PATH . $maintenance_page) ) { + header('Location: ' . BASE_PATH . $maintenance_page, true, 301); + exit; + } + } + + /** * Actually runs the parser against current template and stores parsing result * * This method gets t variable passed to the script, loads the template given in t variable and @@ -930,6 +945,27 @@ */ function Run() { + // process maintenance mode redirect: begin + $maintenance_mode = $this->getMaintenanceMode(); + + if ( $maintenance_mode == MaintenanceMode::HARD ) { + $this->redirectToMaintenance(); + } + elseif ( $maintenance_mode == MaintenanceMode::SOFT ) { + $maintenance_template = $this->isAdmin ? 'login' : $this->ConfigValue('SoftMaintenanceTemplate'); + + if ( $this->GetVar('t') != $maintenance_template ) { + $redirect_params = Array ('response_code' => 301); + + if (!$this->isAdmin) { + $redirect_params['next_template'] = urlencode($_SERVER['REQUEST_URI']); + } + + $this->Redirect($maintenance_template, $redirect_params); + } + } + // process maintenance mode redirect: end + if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Run:'); } @@ -1016,19 +1052,19 @@ */ function Done() { - $this->HandleEvent( new kEvent('adm:OnBeforeShutdown') ); + $this->HandleEvent(new kEvent('adm:OnBeforeShutdown')); $debug_mode = defined('DEBUG_MODE') && $this->isDebugMode(); - if ($debug_mode && kUtil::constOn('DBG_PROFILE_MEMORY')) { + if ( $debug_mode && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Done:'); } - if ($debug_mode) { + if ( $debug_mode ) { $this->EventManager->runScheduledTasks(reAFTER); $this->Session->SaveData(); - if (kUtil::constOn('DBG_CACHE')) { + if ( kUtil::constOn('DBG_CACHE') ) { $this->cacheManager->printStatistics(); } @@ -1041,10 +1077,10 @@ $this->HTML = ob_get_clean() . $this->HTML; } - if ($this->UseOutputCompression()) { + if ( $this->UseOutputCompression() ) { $compression_level = $this->ConfigValue('OutputCompressionLevel'); - if (!$compression_level || $compression_level < 0 || $compression_level > 9) { + if ( !$compression_level || $compression_level < 0 || $compression_level > 9 ) { $compression_level = 7; } @@ -1058,12 +1094,12 @@ $this->cacheManager->UpdateApplicationCache(); flush(); - if (!$debug_mode) { + if ( !$debug_mode ) { $this->EventManager->runScheduledTasks(reAFTER); $this->Session->SaveData(); } - if (defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin) { + if ( defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin ) { $this->_storeStatistics(); } } @@ -1720,9 +1756,18 @@ return $this->cacheManager->ConfigValue($name); } - function SetConfigValue($name, $value) + /** + * Changes value of individual configuration variable (+resets cache, when needed) + * + * @param string $name + * @param string $value + * @param bool $local_cache_only + * @return string + * @access public + */ + public function SetConfigValue($name, $value, $local_cache_only = false) { - return $this->cacheManager->SetConfigValue($name, $value); + return $this->cacheManager->SetConfigValue($name, $value, $local_cache_only); } /** @@ -2795,4 +2840,27 @@ { $this->cacheManager->delayUnitProcessing($method, $params); } + + /** + * Returns current maintenance mode state + * + * @param bool $check_ips + * @return int + * @access public + */ + public function getMaintenanceMode($check_ips = true) + { + $exception_ips = defined('MAINTENANCE_MODE_IPS') ? MAINTENANCE_MODE_IPS : ''; + $setting_name = $this->isAdmin ? 'MAINTENANCE_MODE_ADMIN' : 'MAINTENANCE_MODE_FRONT'; + + if ( defined($setting_name) && constant($setting_name) > MaintenanceMode::NONE ) { + $exception_ip = $check_ips ? kUtil::ipMatch($exception_ips) : false; + + if ( !$exception_ip ) { + return constant($setting_name); + } + } + + return MaintenanceMode::NONE; + } } \ No newline at end of file Index: core/kernel/db/db_connection.php =================================================================== --- core/kernel/db/db_connection.php (revision 14917) +++ core/kernel/db/db_connection.php (working copy) @@ -303,6 +303,8 @@ trigger_error($error_msg, E_USER_WARNING); } else { + $this->Application->redirectToMaintenance(); + throw new Exception($error_msg); } Index: core/kernel/db/db_load_balancer.php =================================================================== --- core/kernel/db/db_load_balancer.php (revision 14917) +++ core/kernel/db/db_load_balancer.php (working copy) @@ -430,7 +430,7 @@ else { $server = $this->servers[$i]; $server['serverIndex'] = $i; - $conn =& $this->reallyOpenConnection($server); + $conn =& $this->reallyOpenConnection($server, $i == $this->getMasterIndex()); if ( $conn->connectionOpened ) { $this->connections[$i] =& $conn; @@ -454,15 +454,16 @@ * Returns a database object whether or not the connection was successful. * * @param Array $server + * @param bool $is_master * @return kDBConnection */ - protected function &reallyOpenConnection($server) + protected function &reallyOpenConnection($server, $is_master) { $db =& $this->Application->makeClass( 'kDBConnection', Array ($this->dbType, $this->errorHandler, $server['serverIndex']) ); /* @var $db kDBConnection */ $db->debugMode = $this->Application->isDebugMode(); - $db->Connect($server['DBHost'], $server['DBUser'], $server['DBUserPassword'], $this->servers[0]['DBName'], true, true); + $db->Connect($server['DBHost'], $server['DBUser'], $server['DBUserPassword'], $this->servers[0]['DBName'], true, !$is_master); return $db; } Index: core/kernel/managers/cache_manager.php =================================================================== --- core/kernel/managers/cache_manager.php (revision 14917) +++ core/kernel/managers/cache_manager.php (working copy) @@ -203,10 +203,23 @@ return false; } - function SetConfigValue($name, $value) + /** + * Changes value of individual configuration variable (+resets cache, when needed) + * + * @param string $name + * @param string $value + * @param bool $local_cache_only + * @return string + * @access public + */ + public function SetConfigValue($name, $value, $local_cache_only = false) { $this->configVariables[$name] = $value; + if ( $local_cache_only ) { + return; + } + $fields_hash = Array ('VariableValue' => $value); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'ConfigurationValues', 'VariableName = ' . $this->Conn->qstr($name)); Index: core/kernel/processors/main_processor.php =================================================================== --- core/kernel/processors/main_processor.php (revision 14917) +++ core/kernel/processors/main_processor.php (working copy) @@ -1248,4 +1248,18 @@ return $_SERVER['REQUEST_URI']; } + + /** + * Returns current maintenance mode state + * + * @param Array $params + * @return int + * @access protected + */ + protected function MaintenanceMode($params) + { + $check_ips = isset($params['check_ips']) ? $params['check_ips'] : true; + + return $this->Application->getMaintenanceMode($check_ips); + } } Index: core/kernel/startup.php =================================================================== --- core/kernel/startup.php (revision 14917) +++ core/kernel/startup.php (working copy) @@ -133,6 +133,12 @@ CacheSettings::$sectionsParsedRebuildTime = isset($vars['SectionsParsedRebuildTime']) ? $vars['SectionsParsedRebuildTime'] : 10; CacheSettings::$domainsParsedRebuildTime = isset($vars['DomainsParsedRebuildTime']) ? $vars['DomainsParsedRebuildTime'] : 2; + class MaintenanceMode { + const NONE = 0; + const SOFT = 1; + const HARD = 2; + } + unset($vars); // just in case someone will be still using it if (ini_get('safe_mode')) { Index: core/units/configuration/configuration_event_handler.php =================================================================== --- core/units/configuration/configuration_event_handler.php (revision 14917) +++ core/units/configuration/configuration_event_handler.php (working copy) @@ -16,6 +16,17 @@ class ConfigurationEventHandler extends kDBEventHandler { + function mapPermissions() + { + parent::mapPermissions(); + + $permissions = Array ( + 'OnGenerateMaintenancePage' => Array ('self' => 'add|edit'), + ); + + $this->permMapping = array_merge($this->permMapping, $permissions); + } + /** * Changes permission section to one from REQUEST, not from config * @@ -136,13 +147,24 @@ } } - if ( $object->GetDBField('VariableName') == 'AdminConsoleInterface' ) { + $variable_name = $object->GetDBField('VariableName'); + $new_value = $object->GetDBField('VariableValue'); + + if ( $variable_name == 'AdminConsoleInterface' ) { $can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange'); - if ( ($object->GetDBField('VariableValue') != $object->GetOriginalField('VariableValue')) && !$can_change ) { + if ( ($new_value != $object->GetOriginalField('VariableValue')) && !$can_change ) { $object->SetError('VariableValue', 'not_allowed', 'la_error_OperationNotAllowed'); } } + elseif ( $variable_name == 'HardMaintenanceTemplate' ) { + $compile = $event->MasterEvent->getEventParam('compile_maintenance_template'); + $compile = $compile || $new_value != $object->GetOriginalField('VariableValue'); + + if ( $compile && !$this->generateMaintenancePage($new_value) ) { + $object->SetError('VariableValue', 'template_file_missing', 'la_error_TemplateFileMissing'); + } + } } /** @@ -172,6 +194,9 @@ if ( $object->GetDBField('VariableValue') != $object->GetOriginalField('VariableValue') ) { $changed[] = $variable_name; $this->Application->SetVar($event->getPrefixSpecial() . '_changed', $changed); + + // update value in cache, so other code (during this script run) would use new value + $this->Application->SetConfigValue($variable_name, $object->GetDBField('VariableValue'), true); } if ( $variable_name == 'Require_AdminSSL' || $variable_name == 'AdminSSL_URL' ) { @@ -289,4 +314,44 @@ $event->SetRedirectParam('opener', 'u'); } + /** + * Generates maintenance page + * + * @param kEvent $event + * @return void + * @access protected + */ + protected function OnGenerateMaintenancePage(kEvent &$event) + { + $event->setEventParam('compile_maintenance_template', 1); + + $event->CallSubEvent('OnUpdate'); + } + + /** + * Generates HTML version of hard maintenance template + * + * @param string $template + * @return bool + * @access protected + */ + protected function generateMaintenancePage($template = null) + { + if ( !isset($template) ) { + $template = $this->Application->ConfigValue('HardMaintenanceTemplate'); + } + + $curl_helper =& $this->Application->recallObject('CurlHelper'); + /* @var $curl_helper kCurlHelper */ + + $html = $curl_helper->Send($this->Application->BaseURL() . '?t=' . $template); + + if ( $curl_helper->isGoodResponseCode() ) { + file_put_contents(WRITEABLE . DIRECTORY_SEPARATOR . 'maintenance.html', $html); + + return true; + } + + return false; + } } \ No newline at end of file Index: tools/debug_sample.php =================================================================== --- tools/debug_sample.php (revision 14917) +++ tools/debug_sample.php (working copy) @@ -12,6 +12,9 @@ * See http://www.in-portal.org/license for copyright notices and details. */ +// define('MAINTENANCE_MODE_FRONT', MaintenanceMode::NONE); // Set to MaintenanceMode::SOFT for SOFT Maintenance mode, set to MaintenanceMode::HARD for HARD Maintenance mode (no DB load) +// define('MAINTENANCE_MODE_ADMIN', MaintenanceMode::NONE); // Set to MaintenanceMode::SOFT for SOFT Maintenance mode, set to MaintenanceMode::HARD for HARD Maintenance mode (no DB load) +// define('MAINTENANCE_MODE_IPS', ''); // Define IP addresses/hosts, which will be able to continue accessing website // define('SILENT_LOG', 1); // Log all php errors on site to separate file (/silent_log.txt) // define('DBG_REQUREST_LOG', '/path/to/file');// Log all user requests to site into filename specified // define('DBG_ZEND_PRESENT', 0); // Set to 0 to debug debugger (because debugger automatically got disabled during zend debug sessions)