diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 448b809ea..ac2d72a4e 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -141,15 +141,9 @@ class FrontEndFileHandler extends Handler * @param bool $forceMinify Whether this file should be minified * @return stdClass The file information */ - protected function getFileInfo($fileName, $targetIe = '', $media = 'all', $vars = array(), $forceMinify = false) + protected function getFileInfo($fileName, $targetIe = '', $media = 'all', $vars = array(), $isCommon = false) { static $existsInfo = array(); - - if(self::$minify === null) - { - self::$minify = config('view.minify_scripts') ?: 'common'; - } - if(isset($existsInfo[$existsKey])) { return $existsInfo[$existsKey]; @@ -162,7 +156,7 @@ class FrontEndFileHandler extends Handler $file->fileRealPath = FileHandler::getRealPath($pathInfo['dirname']); $file->fileFullPath = $file->fileRealPath . '/' . $pathInfo['basename']; $file->fileExtension = strtolower($pathInfo['extension']); - if(preg_match('/^(.+)\.min$/', $pathInfo['filename'], $matches)) + if (preg_match('/^(.+)\.min$/', $pathInfo['filename'], $matches)) { $file->fileNameNoExt = $matches[1]; $file->isMinified = true; @@ -174,8 +168,10 @@ class FrontEndFileHandler extends Handler } $file->isExternalURL = preg_match('@^(https?:)?//@i', $file->filePath) ? true : false; $file->isCachedScript = !$file->isExternalURL && strpos($file->filePath, 'files/cache/') !== false; + $file->isCommon = $isCommon; $file->keyName = $file->fileNameNoExt . '.' . $file->fileExtension; $file->cdnPath = $this->_normalizeFilePath($pathInfo['dirname']); + $file->vars = (array)$vars; // Fix incorrectly minified URL if($file->isMinified && !$file->isExternalURL && (!file_exists($file->fileFullPath) || is_link($file->fileFullPath) || @@ -188,36 +184,13 @@ class FrontEndFileHandler extends Handler $file->fileFullPath = $file->fileRealPath . '/' . $file->fileNameNoExt . '.' . $file->fileExtension; } } - - // Decide whether to minify this file - if ($file->isMinified || $file->isExternalURL || $file->isCachedScript || strpos($file->filePath, 'common/js/plugins') !== false || self::$minify === 'none') + + // Do not minify common JS plugins + if (strpos($file->filePath, 'common/js/plugins') !== false) { - $minify = false; - } - elseif (self::$minify === 'all') - { - $minify = true; - } - else - { - $minify = $forceMinify; + $file->isMinified = true; } - // Process according to file type - switch ($file->fileExtension) - { - case 'css': - case 'js': - $this->proc_CSS_JS($file, $minify); - break; - case 'less': - case 'scss': - $this->proc_LESS_SCSS($file, $minify, (array)$vars); - break; - default: - break; - } - // Process targetIe and media attributes $file->targetIe = $targetIe; if($file->fileExtension == 'css') @@ -279,10 +252,9 @@ class FrontEndFileHandler extends Handler * * @param object $file * @param bool $minify - * @param array $vars * @return void */ - protected function proc_LESS_SCSS($file, $minify, $vars = array()) + protected function proc_LESS_SCSS($file, $minify) { if (!file_exists($file->fileFullPath)) { @@ -296,7 +268,7 @@ class FrontEndFileHandler extends Handler if (!file_exists($compiledFilePath) || filemtime($compiledFilePath) < filemtime($file->fileFullPath)) { $method_name = 'compile' . $file->fileExtension; - $success = Rhymix\Framework\Formatter::$method_name($file->fileFullPath, $compiledFilePath, $vars, $minify); + $success = Rhymix\Framework\Formatter::$method_name($file->fileFullPath, $compiledFilePath, $file->vars, $minify); if ($success === false) { return; @@ -376,27 +348,81 @@ class FrontEndFileHandler extends Handler * * @return array Returns css file list. Array contains file, media, targetie. */ - function getCssFileList() + public function getCssFileList() { $map = &$this->cssMap; $mapIndex = &$this->cssMapIndex; - + $minify = self::$minify !== null ? self::$minify : (config('view.minify_scripts') ?: 'common'); + $concat = strpos(config('view.concat_scripts'), 'css') !== false; $this->_sortMap($map, $mapIndex); - - $result = array(); - foreach($map as $indexedMap) + + // Minify all scripts, and compile LESS/SCSS into CSS. + foreach ($map as $indexedMap) { - foreach($indexedMap as $file) + foreach ($indexedMap as $file) { - $fullFilePath = $file->filePath . '/' . $file->fileName; - if (!$file->isExternalURL && is_readable($file->fileFullPath)) + $minify_this_file = !$file->isMinified && !$file->isExternalURL && !$file->isCachedScript && (($file->isCommon && $minify !== 'none') || $minify === 'all'); + if ($file->fileExtension === 'css') { - $fullFilePath .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + $this->proc_CSS_JS($file, $minify_this_file); + } + else + { + $this->proc_LESS_SCSS($file, $minify_this_file); + } + } + } + + // Add all files to the final result. + $result = array(); + if ($concat && count($concat_list = $this->_concatMap($map))) + { + foreach ($concat_list as $concat_fileset) + { + if (count($concat_fileset) === 1) + { + $file = reset($concat_fileset); + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'media' => $file->media, 'targetie' => $file->targetIe); + } + else + { + $concat_files = array(); + $concat_max_timestamp = 0; + foreach ($concat_fileset as $file) + { + $concat_files[] = $file->media === 'all' ? $file->fileFullPath : array($file->fileFullPath, $concat_file->media); + $concat_max_timestamp = max($concat_max_timestamp, filemtime($concat_file->fileFullPath)); + } + $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.css'; + if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) + { + Rhymix\Framework\Storage::write(\RX_BASEDIR . $concat_filename, Rhymix\Framework\Formatter::concatCSS($concat_files, $concat_filename)); + } + $concat_filename .= '?' . date('YmdHis', filemtime(\RX_BASEDIR . $concat_filename)); + $result[] = array('file' => \RX_BASEURL . $concat_filename, 'media' => 'all', 'targetie' => ''); + } + } + } + else + { + foreach ($map as $indexedMap) + { + foreach ($indexedMap as $file) + { + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'media' => $file->media, 'targetie' => $file->targetIe); } - $result[] = array('file' => $fullFilePath, 'media' => $file->media, 'targetie' => $file->targetIe); } } - return $result; } @@ -406,7 +432,7 @@ class FrontEndFileHandler extends Handler * @param string $type Type of javascript. head, body * @return array Returns javascript file list. Array contains file, targetie. */ - function getJsFileList($type = 'head') + public function getJsFileList($type = 'head') { if($type == 'head') { @@ -418,26 +444,105 @@ class FrontEndFileHandler extends Handler $map = &$this->jsBodyMap; $mapIndex = &$this->jsBodyMapIndex; } - + + $minify = self::$minify !== null ? self::$minify : (config('view.minify_scripts') ?: 'common'); + $concat = strpos(config('view.concat_scripts'), 'js') !== false; $this->_sortMap($map, $mapIndex); - - $result = array(); - foreach($map as $indexedMap) + + // Minify all scripts. + foreach ($map as $indexedMap) { - foreach($indexedMap as $file) + foreach ($indexedMap as $file) { - $fullFilePath = $file->filePath . '/' . $file->fileName; - if (!$file->isExternalURL && is_readable($file->fileFullPath)) + if (!$file->isMinified && !$file->isExternalURL && !$file->isCachedScript && (($file->isCommon && $minify !== 'none') || $minify === 'all')) { - $fullFilePath .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + $this->proc_CSS_JS($file, true); + } + } + } + + // Add all files to the final result. + $result = array(); + if ($concat && count($concat_list = $this->_concatMap($map))) + { + foreach ($concat_list as $concat_fileset) + { + if (count($concat_fileset) === 1) + { + $file = reset($concat_fileset); + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'targetie' => $file->targetIe); + } + else + { + $concat_files = array(); + $concat_max_timestamp = 0; + foreach ($concat_fileset as $file) + { + $concat_files[] = $file->targetIe ? $file->fileFullPath : array($file->fileFullPath, $concat_file->targetIe); + $concat_max_timestamp = max($concat_max_timestamp, filemtime($concat_file->fileFullPath)); + } + $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.js'; + if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) + { + Rhymix\Framework\Storage::write(\RX_BASEDIR . $concat_filename, Rhymix\Framework\Formatter::concatJS($concat_files, $concat_filename)); + } + $concat_filename .= '?' . date('YmdHis', filemtime(\RX_BASEDIR . $concat_filename)); + $result[] = array('file' => \RX_BASEURL . $concat_filename, 'targetie' => ''); + } + } + } + else + { + foreach ($map as $indexedMap) + { + foreach ($indexedMap as $file) + { + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'targetie' => $file->targetIe); } - $result[] = array('file' => $fullFilePath, 'targetie' => $file->targetIe); } } - return $result; } - + + /** + * Create a concatenation map, skipping external URLs and unreadable scripts. + * + * @param array $map + * @return array + */ + protected function _concatMap(&$map) + { + $concat_list = array(); + $concat_key = 0; + foreach ($map as $indexedMap) + { + foreach ($indexedMap as $file) + { + if ($file->isExternalURL || ($file->fileExtension === 'css' && $file->targetIe) || !is_readable($file->fileFullPath)) + { + $concat_key++; + $concat_list[$concat_key][] = $file; + $concat_key++; + } + else + { + $concat_list[$concat_key][] = $file; + } + } + } + return $concat_list; + } + /** * Sort a map * @@ -517,7 +622,6 @@ class FrontEndFileHandler extends Handler $cssSortList = array('common' => -100000, 'layouts' => -90000, 'modules' => -80000, 'widgets' => -70000, 'addons' => -60000); $file->index = $cssSortList[$tmp[0]]; } - } /* End of file FrontEndFileHandler.class.php */ /* Location: ./classes/frontendfile/FrontEndFileHandler.class.php */ diff --git a/common/framework/formatter.php b/common/framework/formatter.php index 8bda5dbe0..38f3d5a44 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -375,7 +375,7 @@ class Formatter $content = 'if (' . self::convertIECondition($targetie) . ') {' . "\n\n" . $content . "\n\n" . '}'; } $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; - $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . "\n\n"; + $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . ";\n\n"; } return $result; diff --git a/modules/admin/admin.admin.controller.php b/modules/admin/admin.admin.controller.php index a0d287b68..f29cb7f40 100644 --- a/modules/admin/admin.admin.controller.php +++ b/modules/admin/admin.admin.controller.php @@ -699,6 +699,7 @@ class adminAdminController extends admin Rhymix\Framework\Config::set('session.delay', $vars->delay_session === 'Y'); Rhymix\Framework\Config::set('session.use_db', $vars->use_db_session === 'Y'); Rhymix\Framework\Config::set('view.minify_scripts', $vars->minify_scripts ?: 'common'); + Rhymix\Framework\Config::set('view.concat_scripts', $vars->concat_scripts ?: 'none'); Rhymix\Framework\Config::set('view.gzip', $vars->use_gzip === 'Y'); // Save diff --git a/modules/admin/admin.admin.view.php b/modules/admin/admin.admin.view.php index 7bc617e75..9542bfebc 100644 --- a/modules/admin/admin.admin.view.php +++ b/modules/admin/admin.admin.view.php @@ -530,6 +530,7 @@ class adminAdminView extends admin Context::set('delay_session', Rhymix\Framework\Config::get('session.delay')); Context::set('use_db_session', Rhymix\Framework\Config::get('session.use_db')); Context::set('minify_scripts', Rhymix\Framework\Config::get('view.minify_scripts')); + Context::set('concat_scripts', Rhymix\Framework\Config::get('view.concat_scripts')); Context::set('use_gzip', Rhymix\Framework\Config::get('view.gzip')); $this->setTemplateFile('config_advanced'); diff --git a/modules/admin/lang/en.php b/modules/admin/lang/en.php index 3e0edabf8..7a93fca6d 100644 --- a/modules/admin/lang/en.php +++ b/modules/admin/lang/en.php @@ -86,6 +86,12 @@ $lang->cmd_minify_all = 'All files'; $lang->cmd_minify_common = 'Common files only'; $lang->cmd_minify_none = 'None'; $lang->about_minify_scripts = 'Automatically minify all CSS and JS scripts in the Core and all modules.'; +$lang->concat_scripts = 'Concatenate scripts'; +$lang->cmd_concat_none = 'Do not concatenate'; +$lang->cmd_concat_css_only = 'Concatenate all CSS'; +$lang->cmd_concat_js_only = 'Concatenate all JS'; +$lang->cmd_concat_css_js = 'Concatenate both CSS and JS'; +$lang->about_concat_scripts = 'Automatically concatenate CSS and JS scripts into as few files as possible. External scripts are not concatenated.'; $lang->use_gzip = 'gzip Compression'; $lang->delay_session = 'Delay session start'; $lang->about_delay_session = 'To improve performance when using a caching proxy server such as Varnish, do not issue sessions to visitors until they log in.
Selecting this option may cause view counts and visitor counts to become inaccurate.'; diff --git a/modules/admin/lang/ko.php b/modules/admin/lang/ko.php index b115f00e9..4970668d2 100644 --- a/modules/admin/lang/ko.php +++ b/modules/admin/lang/ko.php @@ -85,7 +85,13 @@ $lang->minify_scripts = '스크립트 자동 압축'; $lang->cmd_minify_all = '모든 파일을 압축'; $lang->cmd_minify_common = '공통 파일만 압축'; $lang->cmd_minify_none = '압축하지 않음'; -$lang->about_minify_scripts = '코어와 모든 모듈에 포함된 CSS, JS 파일들을 자동으로 압축(minify)하여 전송합니다.'; +$lang->about_minify_scripts = 'CSS, JS 파일들을 자동으로 압축(minify)하여 전송합니다. 트래픽을 절약할 수 있습니다.'; +$lang->concat_scripts = '스크립트 합치기'; +$lang->cmd_concat_none = '합치지 않음'; +$lang->cmd_concat_css_only = 'CSS만 합침'; +$lang->cmd_concat_js_only = 'JS만 합침'; +$lang->cmd_concat_css_js = 'CSS와 JS를 모두 합침'; +$lang->about_concat_scripts = 'CSS, JS 파일들을 하나로 합쳐서 전송합니다. 외부에서 로딩하는 스크립트는 합쳐지지 않습니다.'; $lang->use_gzip = 'gzip 압축'; $lang->delay_session = '세션 시작 지연'; $lang->about_delay_session = 'Varnish 등의 프록시 캐싱 서버 사용시 성능 개선을 위해, 로그인하지 않은 사용자에게는 인증 세션을 부여하지 않습니다.
이 옵션을 선택할 경우 방문자 수 및 조회수 집계가 정확하게 이루어지지 않을 수 있습니다.'; diff --git a/modules/admin/tpl/config_advanced.html b/modules/admin/tpl/config_advanced.html index 2bc11806e..ee9ec8025 100644 --- a/modules/admin/tpl/config_advanced.html +++ b/modules/admin/tpl/config_advanced.html @@ -86,6 +86,17 @@

{$lang->about_minify_scripts}

+
+ +
+ + + + +
+

{$lang->about_concat_scripts}

+
+