Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/composite/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/composite/engine.php |
<?php namespace Bitrix\Main\Composite; use Bitrix\Main\Application; use Bitrix\Main\Composite\Debug; use Bitrix\Main\Composite\Debug\Logger; use Bitrix\Main\Composite\Internals\Model\PageTable; use Bitrix\Main\Composite\Internals\Locker; use Bitrix\Main\Composite\Internals\PageManager; use Bitrix\Main\Config\Configuration; use Bitrix\Main\Config\Option; use Bitrix\Main\Context; use Bitrix\Main\EventManager; use Bitrix\Main\Engine\Response; use Bitrix\Main\IO\File; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Main\ModuleManager; use Bitrix\Main\Page\Asset; use Bitrix\Main\Page\AssetMode; use Bitrix\Main\Page\AssetLocation; use Bitrix\Main\Text\BinaryString; use Bitrix\Main\Web\Uri; Loc::loadMessages(__FILE__); final class Engine { const PAGE_DELETION_LIMIT = 100; const PAGE_DELETION_ATTEMPTS = 2; private static $instance; private static $isEnabled = false; private static $useHTMLCache = false; private static $onBeforeHandleKey = false; private static $onRestartBufferHandleKey = false; private static $onBeforeLocalRedirect = false; private static $autoUpdate = true; private static $autoUpdateTTL = 0; private static $isCompositeInjected = false; private static $isRedirect = false; private static $isBufferRestarted = false; /** * use self::getInstance() */ private function __construct() { } /** * you can't clone it */ private function __clone() { } /** * Singleton instance. * @deprecated just use static methods * @return Engine */ public static function getInstance() { if (is_null(self::$instance)) { self::$instance = new Engine(); } return self::$instance; } /** * Sets isEnable property value and attaches needed handlers. * * @param bool $isEnabled Mode control flag. * * @return void */ public static function setEnable($isEnabled = true) { if ($isEnabled && !self::$isEnabled) { self::$onBeforeHandleKey = AddEventHandler( "main", "OnBeforeEndBufferContent", array(__CLASS__, "onBeforeEndBufferContent") ); self::$onRestartBufferHandleKey = AddEventHandler( "main", "OnBeforeRestartBuffer", array(__CLASS__, "onBeforeRestartBuffer") ); self::$onBeforeLocalRedirect = AddEventHandler( "main", "OnBeforeLocalRedirect", array(__CLASS__, "onBeforeLocalRedirect"), 2 ); self::$isEnabled = true; \CJSCore::init(array("fc")); } elseif (!$isEnabled && self::$isEnabled) { if (self::$onBeforeHandleKey >= 0) { RemoveEventHandler("main", "OnBeforeEndBufferContent", self::$onBeforeHandleKey); } if (self::$onRestartBufferHandleKey >= 0) { RemoveEventHandler("main", "OnBeforeRestartBuffer", self::$onRestartBufferHandleKey); } if (self::$onBeforeLocalRedirect >= 0) { RemoveEventHandler("main", "OnBeforeLocalRedirect", self::$onBeforeLocalRedirect); } self::$isEnabled = false; } } /** * Gets isEnabled property. * * @return boolean */ public static function isEnabled() { return self::$isEnabled; } /** * Sets useAppCache property. * * @param boolean $useAppCache AppCache mode control flag. * * @return void */ public static function setUseAppCache($useAppCache = true) { if (self::getUseAppCache()) { self::setUseHTMLCache(false); } $appCache = AppCache::getInstance(); $appCache->setEnabled($useAppCache); } /** * Gets useAppCache property. * * @return boolean */ public static function getUseAppCache() { $appCache = AppCache::getInstance(); return $appCache->isEnabled(); } /** * Sets useHTMLCache property. * * @param boolean $useHTMLCache Composite mode control flag. * * @return void */ public static function setUseHTMLCache($useHTMLCache = true) { self::$useHTMLCache = $useHTMLCache; self::setEnable(); } /** * Gets useHTMLCache property. * * @return boolean */ public static function getUseHTMLCache() { return self::$useHTMLCache; } /** * Returns true if current request was initiated by Ajax. * * @return boolean */ public static function isAjaxRequest() { return Helper::isAjaxRequest(); } public static function isInvalidationRequest() { return self::isAjaxRequest() && Context::getCurrent()->getServer()->get("HTTP_BX_INVALIDATE_CACHE") === "Y"; } /** * Returns true if we should inject banner into a page. * @return bool */ public static function isBannerEnabled() { return Option::get("main", "~show_composite_banner", "Y") == "Y"; } /** * Sets autoUpdate property * * @param bool $flag * * @return void */ public static function setAutoUpdate($flag) { self::$autoUpdate = $flag === false ? false : true; } /** * Gets autoUpdate property * @return bool */ public static function getAutoUpdate() { return self::$autoUpdate; } /** * Sets auto update ttl * * @param int $ttl - number of seconds * * @return void */ public static function setAutoUpdateTTL($ttl) { self::$autoUpdateTTL = intval($ttl); } /** * Gets auto update ttl * @return int */ public static function getAutoUpdateTTL() { return self::$autoUpdateTTL; } /** * OnBeforeEndBufferContent handler. * Prepares the stage for composite mode handler. * * @return void */ public static function onBeforeEndBufferContent() { $params = array(); if (self::getUseAppCache()) { $manifest = AppCache::getInstance(); $params = $manifest->OnBeforeEndBufferContent(); $params["CACHE_MODE"] = "APPCACHE"; $params["PAGE_URL"] = Context::getCurrent()->getServer()->getRequestUri(); } elseif (self::getUseHTMLCache()) { $page = Page::getInstance(); $page->onBeforeEndBufferContent(); if ($page->isCacheable()) { $params["CACHE_MODE"] = "HTMLCACHE"; if (self::isBannerEnabled()) { $options = Helper::getOptions(); $params["banner"] = array( "url" => GetMessage("COMPOSITE_BANNER_URL"), "text" => GetMessage("COMPOSITE_BANNER_TEXT"), "bgcolor" => $options["BANNER_BGCOLOR"] ?? "", "style" => $options["BANNER_STYLE"] ?? "" ); } } else { return; } } $params["storageBlocks"] = array(); $params["dynamicBlocks"] = array(); $dynamicAreas = StaticArea::getDynamicAreas(); foreach ($dynamicAreas as $id => $dynamicArea) { $stub = $dynamicArea->getStub(); self::replaceSessid($stub); $params["dynamicBlocks"][$dynamicArea->getId()] = mb_substr(md5($stub), 0, 12); if ($dynamicArea->getBrowserStorage()) { $realId = $dynamicArea->getContainerId() !== null ? $dynamicArea->getContainerId() : "bxdynamic_".$id; $params["storageBlocks"][] = $realId; } } $params["AUTO_UPDATE"] = self::getAutoUpdate(); $params["AUTO_UPDATE_TTL"] = self::getAutoUpdateTTL(); $params["version"] = 2; Asset::getInstance()->addString( self::getInjectedJs($params), false, AssetLocation::BEFORE_CSS, self::getUseHTMLCache() ? AssetMode::COMPOSITE : AssetMode::ALL ); self::$isCompositeInjected = true; } /** * @param $content * * @return null|string * @internal */ public static function startBuffering($content) { if (!self::isEnabled() || !is_object($GLOBALS["APPLICATION"]) || !self::$isCompositeInjected) { return null; } if (defined("BX_BUFFER_SHUTDOWN") || !defined("B_EPILOG_INCLUDED")) { Logger::log( array( "TYPE" => Logger::TYPE_PHP_SHUTDOWN, ) ); return null; } $newBuffer = $GLOBALS["APPLICATION"]->buffer_content; $cnt = count($GLOBALS["APPLICATION"]->buffer_content_type); Asset::getInstance()->setMode(AssetMode::COMPOSITE); self::$isCompositeInjected = false; //double-check for ($i = 0; $i < $cnt; $i++) { $method = $GLOBALS["APPLICATION"]->buffer_content_type[$i]["F"]; if (!is_array($method) || count($method) !== 2 || $method[0] !== $GLOBALS["APPLICATION"]) { continue; } if (in_array($method[1], array("GetCSS", "GetHeadScripts", "GetHeadStrings"))) { $newBuffer[$i * 2 + 1] = call_user_func_array( $method, $GLOBALS["APPLICATION"]->buffer_content_type[$i]["P"] ); if (self::$isCompositeInjected !== true && $method[1] === "GetHeadStrings") { self::$isCompositeInjected = strpos($newBuffer[$i * 2 + 1], "w.frameRequestStart") !== false; } } } Asset::getInstance()->setMode(AssetMode::STANDARD); if (!self::$isCompositeInjected) { Logger::log( array( "TYPE" => Logger::TYPE_COMPOSITE_NOT_INJECTED, ) ); } return self::$isCompositeInjected ? implode("", $newBuffer).$content : null; } /** * Returns true if $originalContent was modified * * @param $originalContent * @param $compositeContent * * @return bool * @internal */ public static function endBuffering(&$originalContent, $compositeContent) { if (!self::isEnabled() || $compositeContent === null || defined("BX_BUFFER_SHUTDOWN") || !defined("B_EPILOG_INCLUDED")) { //this happens when die() invokes in self::onBeforeLocalRedirect if (self::isAjaxRequest() && self::$isRedirect === false) { $originalContent = self::getAjaxError(); Page::getInstance()->delete(); return true; } return false; } if (function_exists("getmoduleevents")) { foreach (GetModuleEvents("main", "OnEndBufferContent", true) as $arEvent) { ExecuteModuleEventEx($arEvent, array(&$compositeContent)); } } $compositeContent = self::processPageContent($compositeContent); if (self::isAjaxRequest() || self::getUseAppCache()) { $originalContent = $compositeContent; return true; } return false; } /** * * There are two variants of content's modification in this method. * The first one: * If it's ajax-hit the content will be replaced by json data with dynamic blocks, * javascript files and etc. - dynamic part * * The second one: * If it's simple hit the content will be modified also, * all dynamic blocks will be cut out of the content - static part. * * @param string $content Html page content. * * @return string */ private static function processPageContent($content) { global $APPLICATION, $USER; $dividedData = self::getDividedPageData($content); $htmlCacheChanged = false; if (self::getUseHTMLCache()) { $page = Page::getInstance(); if ($page->isCacheable()) { $cacheExists = $page->exists(); $rewriteCache = $page->getMd5() !== $dividedData["md5"]; if (self::getAutoUpdate() && self::getAutoUpdateTTL() > 0 && $cacheExists) { $mtime = $page->getLastModified(); if ($mtime !== false && ($mtime + self::getAutoUpdateTTL()) > time()) { $rewriteCache = false; } } $invalidateCache = self::getAutoUpdate() === false && self::isInvalidationRequest(); $oldContent = null; if (!$cacheExists || $rewriteCache || $invalidateCache) { if ($invalidateCache || Locker::lock($page->getCacheKey())) { if ($cacheExists && Logger::isOn()) { $oldContent = $page->read(); } if ($page->getStorage() instanceof Data\FileStorage) { $freeSpace = strlen($dividedData["static"]) + strlen($dividedData["md5"]); self::ensureFileQuota($freeSpace); } $success = $page->write($dividedData["static"], $dividedData["md5"]); if ($success) { $htmlCacheChanged = true; $page->setUserPrivateKey(); } Locker::unlock($page->getCacheKey()); } } $pageId = PageManager::register( $page->getCacheKey(), array( "CHANGED" => $htmlCacheChanged, "SIZE" => $page->getSize() ) ); if ($oldContent !== null) { Logger::log( array( "TYPE" => Logger::TYPE_CACHE_REWRITING, "MESSAGE" => $oldContent, "PAGE_ID" => $pageId ) ); } } else { $page->delete(); return self::getAjaxError(); } } if (self::getUseAppCache() == true) //Do we use html5 application cache? { AppCache::getInstance()->generate($dividedData["static"]); } else { AppCache::checkObsoleteManifest(); } if (self::isAjaxRequest()) { self::sendRandHeader(); header("Content-Type: application/x-javascript; charset=".SITE_CHARSET); header("X-Bitrix-Composite: Ajax ".($htmlCacheChanged ? "(changed)" : "(stable)")); $content = array( "js" => array_unique($APPLICATION->arHeadScripts), "lang" => \CJSCore::GetCoreMessages(), "css" => $APPLICATION->GetCSSArray(), "htmlCacheChanged" => $htmlCacheChanged, "isManifestUpdated" => AppCache::getInstance()->getIsModified(), "dynamicBlocks" => $dividedData["dynamic"], "spread" => array_map(array("CUtil", "JSEscape"), $APPLICATION->GetSpreadCookieUrls()), ); $content = \CUtil::PhpToJSObject($content); } else { $content = $dividedData["static"]; } return $content; } /** * This method returns the divided content. * The content is divided by two parts - static and dynamic. * Example of returned value: * <code> * array( * "static"=>"Hello World!" * "dynamic"=>array( * array("ID"=>"someID","CONTENT"=>"someDynamicContent", "HASH"=>"md5ofDynamicContent")), * array("ID"=>"someID2","CONTENT"=>"someDynamicContent2", "HASH"=>"md5ofDynamicContent2")) * ); * </code> * * @param string $content Html page content. * * @return array */ private static function getDividedPageData($content) { $data = array( "dynamic" => array(), "static" => "", "md5" => "", ); $dynamicAreas = StaticArea::getDynamicAreas(); if (!empty($dynamicAreas) && ($areas = self::getFrameIndexes($content)) !== false) { $offset = 0; $pageBlocks = self::getPageBlocks(); foreach ($areas as $area) { $dynamicArea = StaticArea::getDynamicArea($area->id); if ($dynamicArea === null) { continue; } $realId = $dynamicArea->getContainerId() !== null ? $dynamicArea->getContainerId() : "bxdynamic_".$area->id; $assets = Asset::getInstance()->getAssetInfo($dynamicArea->getAssetId(), $dynamicArea->getAssetMode()); $areaContent = substr($content, $area->openTagEnd, $area->closingTagStart - $area->openTagEnd); $areaContentMd5 = substr(md5($areaContent), 0, 12); $blockId = $dynamicArea->getId(); $hasSameContent = isset($pageBlocks[$blockId]) && $pageBlocks[$blockId] === $areaContentMd5; if (!$hasSameContent) { $data["dynamic"][] = array( "ID" => $realId, "CONTENT" => $areaContent, "HASH" => $areaContentMd5, "PROPS" => array( "ID" => $area->id, "CONTAINER_ID" => $dynamicArea->getContainerId(), "USE_BROWSER_STORAGE" => $dynamicArea->getBrowserStorage(), "AUTO_UPDATE" => $dynamicArea->getAutoUpdate(), "USE_ANIMATION" => $dynamicArea->getAnimation(), "CSS" => $assets["CSS"], "JS" => $assets["JS"], "BUNDLE_JS" => $assets["BUNDLE_JS"], "BUNDLE_CSS" => $assets["BUNDLE_CSS"], "STRINGS" => $assets["STRINGS"], ), ); } $data["static"] .= substr($content, $offset, $area->openTagStart - $offset); if ($dynamicArea->getContainerId() === null) { $data["static"] .= '<div id="bxdynamic_'.$area->id.'_start" style="display:none"></div>'. $dynamicArea->getStub(). '<div id="bxdynamic_'.$area->id.'_end" style="display:none"></div>'; } else { $data["static"] .= $dynamicArea->getStub(); } $offset = $area->closingTagEnd; } $data["static"] .= substr($content, $offset); } else { $data["static"] = $content; } self::replaceSessid($data["static"]); Asset::getInstance()->moveJsToBody($data["static"]); $data["md5"] = md5($data["static"]); return $data; } /** * @param string $content * * @return array|bool */ private static function getFrameIndexes($content) { $openTag = "<!--'start_frame_cache_"; $closingTag = "<!--'end_frame_cache_"; $ending = "'-->"; $areas = array(); $offset = 0; while (($openTagStart = strpos($content, $openTag, $offset)) !== false) { $endingPos = strpos($content, $ending, $openTagStart); if ($endingPos === false) { break; } $idStart = $openTagStart + mb_strlen($openTag); $idLength = $endingPos - $idStart; $areaId = substr($content, $idStart, $idLength); $openTagEnd = $endingPos + mb_strlen($ending); $realClosingTag = $closingTag.$areaId.$ending; $closingTagStart = strpos($content, $realClosingTag, $openTagEnd); if ($closingTagStart === false) { $offset = $openTagEnd; continue; } $closingTagEnd = $closingTagStart + mb_strlen($realClosingTag); $area = new \stdClass(); $area->id = $areaId; $area->openTagStart = $openTagStart; $area->openTagEnd = $openTagEnd; $area->closingTagStart = $closingTagStart; $area->closingTagEnd = $closingTagEnd; $areas[] = $area; $offset = $closingTagEnd; } return !empty($areas) ? $areas : false; } private static function getPageBlocks() { $blocks = array(); $json = Context::getCurrent()->getServer()->get("HTTP_BX_CACHE_BLOCKS"); if ($json !== null && $json <> '') { $blocks = json_decode($json, true); if ($blocks === null) { $blocks = array(); } } return $blocks; } /** * Replaces bitrix sessid in the $content * * @param string $content */ private static function replaceSessid(&$content) { $methodInvocations = bitrix_sessid_post("sessid", true); if ($methodInvocations > 0) { $content = str_replace("value=\"".bitrix_sessid()."\"", "value=\"\"", $content); } } /** * OnBeforeRestartBuffer event handler. * Disables composite mode when called. * * @return void */ public static function onBeforeRestartBuffer() { self::$isBufferRestarted = true; self::setEnable(false); Logger::log( array( "TYPE" => Logger::TYPE_BUFFER_RESTART, "MESSAGE" => "Script: ". ($_SERVER["REAL_FILE_PATH"] ?? $_SERVER["SCRIPT_NAME"]) ) ); } public static function onBeforeLocalRedirect(&$url, $skip_security_check, $isExternal) { if (!self::isAjaxRequest() || ($isExternal && $skip_security_check !== true)) { return; } $response = array( "error" => true, "reason" => "redirect", "redirect_url" => $url, ); self::setEnable(false); Logger::log( array( "TYPE" => Logger::TYPE_LOCAL_REDIRECT, "MESSAGE" => "Script: ". ($_SERVER["REAL_FILE_PATH"] ?? $_SERVER["SCRIPT_NAME"])."\n". "Redirect Url: ".$url ) ); self::$isRedirect = true; Page::getInstance()->delete(); $response = new Response\Json($response); $response->addHeader('X-Bitrix-Composite', 'Ajax (error:redirect)'); $bxRandom = Helper::getAjaxRandom(); if ($bxRandom !== false) { $response->addHeader('BX-RAND', $bxRandom); } Application::getInstance()->end(0, $response); } private static function ensureFileQuota($requiredFreeSpace = 0) { static $tries = 2; if (Helper::checkQuota($requiredFreeSpace) || $tries <= 0) { return; } $records = PageTable::getList( array( "select" => array("ID", "CACHE_KEY"), "order" => array("LAST_VIEWED" => "ASC", "ID" => "ASC"), "limit" => self::getDeletionLimit() ) ); $ids = array(); $compositeOptions = Helper::getOptions(); $deletedSize = 0.0; while ($record = $records->fetch()) { $ids[] = $record["ID"]; $fileStorage = new Data\FileStorage($record["CACHE_KEY"], array(), $compositeOptions); $deletedSize += doubleval($fileStorage->delete()); } PageTable::deleteBatch(array("ID" => $ids)); Helper::updateCacheFileSize(-$deletedSize); Logger::log(array( "TYPE" => Logger::TYPE_CACHE_RESET, "MESSAGE" => "Pages: ".count($ids)."\n". "Size: ".\CFile::formatSize($deletedSize) )); if (!Helper::checkQuota($requiredFreeSpace)) { $tries--; self::ensureFileQuota($requiredFreeSpace); } } /** * @return int */ private static function getDeletionLimit() { $options = Helper::getOptions(); if (isset($options["PAGE_DELETION_LIMIT"]) && intval($options["PAGE_DELETION_LIMIT"]) > 0) { return intval($options["PAGE_DELETION_LIMIT"]); } else { return self::PAGE_DELETION_LIMIT; } } private static function getAjaxError($errorMsg = null) { $error = "unknown"; if ($errorMsg !== null) { $error = $errorMsg; } elseif (self::$isBufferRestarted) { $error = "buffer_restarted"; } elseif (!self::isEnabled()) { $error = "not_enabled"; } elseif (defined("BX_BUFFER_SHUTDOWN")) { $error = "php_shutdown"; } elseif (!Page::getInstance()->isCacheable()) { $error = "not_cacheable"; } elseif (!self::$isCompositeInjected) { $error = "not_injected"; } header("X-Bitrix-Composite: Ajax (error:".$error.")"); self::sendRandHeader(); $response = array( "error" => true, "reason" => $error, ); return \CUtil::PhpToJSObject($response); } /** * Sends BX-RAND Header */ private static function sendRandHeader() { $bxRandom = Helper::getAjaxRandom(); if ($bxRandom !== false) { header("BX-RAND: ".$bxRandom); } } /** * Returns JS minified code that will do dynamic hit to the server. * The code is returned in the 'start' key of the array. * * @param array $params * * @return array[string]string */ private static function getInjectedJs($params = array()) { $vars = \CUtil::PhpToJSObject($params); $inlineJS = <<<JS (function(w, d) { var v = w.frameCacheVars = $vars; var inv = false; if (v.AUTO_UPDATE === false) { if (v.AUTO_UPDATE_TTL && v.AUTO_UPDATE_TTL > 0) { var lm = Date.parse(d.lastModified); if (!isNaN(lm)) { var td = new Date().getTime(); if ((lm + v.AUTO_UPDATE_TTL * 1000) >= td) { w.frameRequestStart = false; w.preventAutoUpdate = true; return; } inv = true; } } else { w.frameRequestStart = false; w.preventAutoUpdate = true; return; } } var r = w.XMLHttpRequest ? new XMLHttpRequest() : (w.ActiveXObject ? new w.ActiveXObject("Microsoft.XMLHTTP") : null); if (!r) { return; } w.frameRequestStart = true; var m = v.CACHE_MODE; var l = w.location; var x = new Date().getTime(); var q = "?bxrand=" + x + (l.search.length > 0 ? "&" + l.search.substring(1) : ""); var u = l.protocol + "//" + l.host + l.pathname + q; r.open("GET", u, true); r.setRequestHeader("BX-ACTION-TYPE", "get_dynamic"); r.setRequestHeader("X-Bitrix-Composite", "get_dynamic"); r.setRequestHeader("BX-CACHE-MODE", m); r.setRequestHeader("BX-CACHE-BLOCKS", v.dynamicBlocks ? JSON.stringify(v.dynamicBlocks) : ""); if (inv) { r.setRequestHeader("BX-INVALIDATE-CACHE", "Y"); } try { r.setRequestHeader("BX-REF", d.referrer || "");} catch(e) {} if (m === "APPCACHE") { r.setRequestHeader("BX-APPCACHE-PARAMS", JSON.stringify(v.PARAMS)); r.setRequestHeader("BX-APPCACHE-URL", v.PAGE_URL ? v.PAGE_URL : ""); } r.onreadystatechange = function() { if (r.readyState != 4) { return; } var a = r.getResponseHeader("BX-RAND"); var b = w.BX && w.BX.frameCache ? w.BX.frameCache : false; if (a != x || !((r.status >= 200 && r.status < 300) || r.status === 304 || r.status === 1223 || r.status === 0)) { var f = {error:true, reason:a!=x?"bad_rand":"bad_status", url:u, xhr:r, status:r.status}; if (w.BX && w.BX.ready && b) { BX.ready(function() { setTimeout(function(){ BX.onCustomEvent("onFrameDataRequestFail", [f]); }, 0); }); } w.frameRequestFail = f; return; } if (b) { b.onFrameDataReceived(r.responseText); if (!w.frameUpdateInvoked) { b.update(false); } w.frameUpdateInvoked = true; } else { w.frameDataString = r.responseText; } }; r.send(); var p = w.performance; if (p && p.addEventListener && p.getEntries && p.setResourceTimingBufferSize) { var e = 'resourcetimingbufferfull'; var h = function() { if (w.BX && w.BX.frameCache && w.BX.frameCache.frameDataInserted) { p.removeEventListener(e, h); } else { p.setResourceTimingBufferSize(p.getEntries().length + 50); } }; p.addEventListener(e, h); } })(window, document); JS; $html = ""; if (self::isBannerEnabled()) { $html .= '<style type="text/css">'.str_replace(array("\n", "\t"), "", self::getInjectedCSS())."</style>\n"; } $html .= '<script type="text/javascript" data-skip-moving="true">'. str_replace(array("\n", "\t"), "", $inlineJS). "</script>"; return $html; } /** * Returns css string to be injected. * * @internal * @return string */ public static function getInjectedCSS() { /** @noinspection CssUnknownTarget */ /** @noinspection CssUnusedSymbol */ return <<<CSS .bx-composite-btn { background: url(/bitrix/images/main/composite/sprite-1x.png) no-repeat right 0 #e94524; border-radius: 15px; color: #fff !important; display: inline-block; line-height: 30px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important; font-size: 12px !important; font-weight: bold !important; height: 31px !important; padding: 0 42px 0 17px !important; vertical-align: middle !important; text-decoration: none !important; } @media screen and (min-device-width: 1200px) and (max-device-width: 1600px) and (-webkit-min-device-pixel-ratio: 2) and (min-resolution: 192dpi) { .bx-composite-btn { background-image: url(/bitrix/images/main/composite/sprite-2x.png); background-size: 42px 124px; } } .bx-composite-btn-fixed { position: absolute; top: -45px; right: 15px; z-index: 10; } .bx-btn-white { background-position: right 0; color: #fff !important; } .bx-btn-black { background-position: right -31px; color: #000 !important; } .bx-btn-red { background-position: right -62px; color: #555 !important; } .bx-btn-grey { background-position: right -93px; color: #657b89 !important; } .bx-btn-border { border: 1px solid #d4d4d4; height: 29px !important; line-height: 29px !important; } .bx-composite-loading { display: block; width: 40px; height: 40px; background: url(/bitrix/images/main/composite/loading.gif); } CSS; } /** * Checks whether HTML Cache should be enabled. * * @internal * @return void */ public static function shouldBeEnabled() { if (self::isSelfHostedPortal()) { return; } if (defined("USE_HTML_STATIC_CACHE") && USE_HTML_STATIC_CACHE === true) { if ( !defined("BX_SKIP_SESSION_EXPAND") && (!defined("ADMIN_SECTION") || (defined("ADMIN_SECTION") && ADMIN_SECTION != "Y")) ) { if (self::isInvalidationRequest()) { $cacheKey = Helper::convertUriToPath( Helper::getRequestUri(), Helper::getHttpHost(), Helper::getRealPrivateKey(Page::getPrivateKey()) ); if (!Locker::lock($cacheKey)) { die(Engine::getAjaxError("invalidation_request_locked")); } } self::setUseHTMLCache(); $options = Helper::getOptions(); if (isset($options["AUTO_UPDATE"]) && $options["AUTO_UPDATE"] === "N") { self::setAutoUpdate(false); } if (isset($options["AUTO_UPDATE_TTL"])) { self::setAutoUpdateTTL($options["AUTO_UPDATE_TTL"]); } define("BX_SKIP_SESSION_EXPAND", true); } } else if (Responder::getLastError() !== null && Logger::isOn()) { $result = Logger::log(array( "TYPE" => Responder::getLastError(), "MESSAGE" => Responder::getLastErrorMessage() )); //try to update page title on the end of a page execution if ($result && $result->getId()) { $recordId = $result->getId(); $eventManager = EventManager::getInstance(); $eventManager->addEventHandler("main", "OnEpilog", function() use($recordId) { if (is_object($GLOBALS["APPLICATION"])) { Debug\Model\LogTable::update($recordId, array( "TITLE" => $GLOBALS["APPLICATION"]->getTitle() )); } }); } } if ( (defined("ENABLE_HTML_STATIC_CACHE_JS") && ENABLE_HTML_STATIC_CACHE_JS === true) && (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true) ) { \CJSCore::init(array("fc")); //to warm up localStorage } } /** * Checks if admin panel will be shown or not. * Disables itself if panel will be show. * * @internal * @return void */ public static function checkAdminPanel() { if ( $GLOBALS["APPLICATION"]->showPanelWasInvoked === true && self::getUseHTMLCache() && !self::isAjaxRequest() && \CTopPanel::shouldShowPanel() ) { self::setEnable(false); Logger::log( array( "TYPE" => Logger::TYPE_ADMIN_PANEL, ) ); } } /** * Return true if it's a self-hosted portal. * @return bool */ public static function isSelfHostedPortal() { if (Configuration::getValue("force_enable_self_hosted_composite") === true) { return false; } else { return ModuleManager::isModuleInstalled("intranet") && !ModuleManager::isModuleInstalled("bitrix24"); } } public static function install($setDefaults = true) { $eventManager = EventManager::getInstance(); $eventManager->registerEventHandler("main", "OnEpilog", "main", "\\".__CLASS__, "onEpilog"); $eventManager->registerEventHandler("main", "OnLocalRedirect", "main", "\\".__CLASS__, "onEpilog"); $eventManager->registerEventHandler("main", "OnChangeFile", "main", "\\".__CLASS__, "onChangeFile"); //For very first run we have to fall into defaults if ($setDefaults === true) { Helper::setOptions(); } $file = new File(Helper::getEnabledFilePath()); if (!$file->isExists()) { $file->putContents(""); } } public static function uninstall() { $eventManager = EventManager::getInstance(); $eventManager->unRegisterEventHandler("main", "OnEpilog", "main", "\\".__CLASS__, "onEpilog"); $eventManager->unRegisterEventHandler("main", "OnLocalRedirect", "main", "\\".__CLASS__, "onEpilog"); $eventManager->unRegisterEventHandler("main", "OnChangeFile", "main", "\\".__CLASS__, "onChangeFile"); $file = new File(Helper::getEnabledFilePath()); $file->delete(); } /** * * Returns true if composite mode is turned on * @return bool */ public static function isOn() { return Helper::isOn(); } /** * @internal * OnEpilog Event Handler * @return void */ public static function onEpilog() { if (!self::isOn()) { return; } global $USER, $APPLICATION; if (is_object($USER) && $USER->IsAuthorized()) { if (self::isCurrentUserCC()) { if ($APPLICATION->get_cookie("CC") !== "Y" || $APPLICATION->get_cookie("NCC") === "Y") { self::setCC(); } } else { if ($APPLICATION->get_cookie("NCC") !== "Y" || $APPLICATION->get_cookie("CC") === "Y") { self::setNCC(); } } } else { if ($APPLICATION->get_cookie("NCC") === "Y" || $APPLICATION->get_cookie("CC") === "Y") { self::deleteCompositeCookies(); } } if (\Bitrix\Main\Data\Cache::shouldClearCache()) { $server = Context::getCurrent()->getServer(); $queryString = DeleteParam( array( "clear_cache", "clear_cache_session", "bitrix_include_areas", "back_url_admin", "show_page_exec_time", "show_include_exec_time", "show_sql_stat", "bitrix_show_mode", "show_link_stat", "login" ) ); $uri = new Uri($server->getRequestUri()); $refinedUri = $queryString != "" ? $uri->getPath()."?".$queryString : $uri->getPath(); $cacheStorage = new Page($refinedUri, Helper::getHttpHost()); $cacheStorage->delete(); } } /** * OnChangeFile Event Handler * * @internal * @param $path * @param $site */ public static function onChangeFile($path, $site) { $domains = Helper::getDomains(); $bytes = 0.0; foreach ($domains as $domain) { $cacheStorage = new Page($path, $domain); $cacheStorage->delete(); } Helper::updateQuota(-$bytes); } /** * OnUserLogin Event Handler */ public static function onUserLogin() { if (!self::isOn()) { return; } if (self::isCurrentUserCC()) { self::setCC(); } else { self::setNCC(); } } /** * OnUserLogout Event Handler */ public static function onUserLogout() { if (self::isOn()) { self::deleteCompositeCookies(); } } public static function isCurrentUserCC() { global $USER; $options = Helper::getOptions(); $groups = isset($options["GROUPS"]) && is_array($options["GROUPS"]) ? $options["GROUPS"] : array(); $groups[] = "2"; $diff = array_diff($USER->GetUserGroupArray(), $groups); return empty($diff); } /** * Sets NCC cookie */ public static function setNCC() { global $APPLICATION; $APPLICATION->set_cookie("NCC", "Y"); $APPLICATION->set_cookie("CC", "", 0); Helper::deleteUserPrivateKey(); } /** * Sets CC cookie */ public static function setCC() { global $APPLICATION; $APPLICATION->set_cookie("CC", "Y"); $APPLICATION->set_cookie("NCC", "", 0); $page = Page::getInstance(); $page->setUserPrivateKey(); } /** * Removes all composite cookies */ public static function deleteCompositeCookies() { global $APPLICATION; $APPLICATION->set_cookie("NCC", "", 0); $APPLICATION->set_cookie("CC", "", 0); Helper::deleteUserPrivateKey(); } //region Deprecated Methods /** * Sets useHTMLCache property. * * @param boolean $preventAutoUpdate property. * * @deprecated use setAutoUpdate * @return void */ public static function setPreventAutoUpdate($preventAutoUpdate = true) { self::$autoUpdate = !$preventAutoUpdate; } /** * Gets preventAutoUpdate property. * * @return boolean * @deprecated use getAutoUpdate */ public static function getPreventAutoUpdate() { return !self::$autoUpdate; } /** * Gets ids of the dynamic blocks. * * @deprecated * @return array */ public function getDynamicIDs() { return StaticArea::getDynamicIDs(); } /** * Returns the identifier of current dynamic area. * * @deprecated * @see \Bitrix\Main\Composite\StaticArea::getCurrentDynamicId * @return string|false */ public function getCurrentDynamicId() { return StaticArea::getCurrentDynamicId(); } /** * Adds dynamic data to be sent to the client. * * @deprecated * * @param string $id Unique identifier of the block. * @param string $content Dynamic part html. * @param string $stub Html to use as stub. * @param string $containerId Identifier of the html container. * @param boolean $useBrowserStorage Use browser storage for caching or not. * @param boolean $autoUpdate Automatically or manually update block contents. * @param boolean $useAnimation Animation flag. * * @return void */ public function addDynamicData( $id, $content, $stub = "", $containerId = null, $useBrowserStorage = false, $autoUpdate = true, $useAnimation = false ) { $area = new StaticArea($id); $area->setStub($stub); $area->setContainerId($containerId); $area->setBrowserStorage($useBrowserStorage); $area->setAutoUpdate($autoUpdate); $area->setAnimation($useAnimation); StaticArea::addDynamicArea($area); } /** * Marks start of a dynamic block. * * @deprecated * * @param integer $id Unique identifier of the block. * * @return boolean */ public function startDynamicWithID($id) { $dynamicArea = new StaticArea($id); return $dynamicArea->startDynamicArea(); } /** * Marks end of the dynamic block if it's the current dynamic block * and its start was being marked early. * * @deprecated * * @param string $id Unique identifier of the block. * @param string $stub Html to use as stub. * @param string $containerId Identifier of the html container. * @param boolean $useBrowserStorage Use browser storage for caching or not. * @param boolean $autoUpdate Automatically or manually update block contents. * @param boolean $useAnimation Animation flag. * * @return boolean */ public function finishDynamicWithID( $id, $stub = "", $containerId = null, $useBrowserStorage = false, $autoUpdate = true, $useAnimation = false) { $curDynamicArea = StaticArea::getCurrentDynamicArea(); if ($curDynamicArea === null || $curDynamicArea->getId() !== $id) { return false; } $curDynamicArea->setStub($stub); $curDynamicArea->setContainerId($containerId); $curDynamicArea->setBrowserStorage($useBrowserStorage); $curDynamicArea->setAutoUpdate($autoUpdate); $curDynamicArea->setAnimation($useAnimation); return $curDynamicArea->finishDynamicArea(); } //endregion } if (!class_exists("Bitrix\\Main\\Page\\Frame", false)) { class_alias("Bitrix\\Main\\Composite\\Engine", "Bitrix\\Main\\Page\\Frame"); }