mocapapa
(Mocapapa)
May 27, 2009, 4:24am
34
Quote
I think I should copy old one to another place (will be yii-blogdemo-enhanced-debug) in order to debug ExtendedClientScript.
Yea, I can reproduce that error at the following site.
http://pugpug.agilit…enhanced-debug/
Quote
PHP Error
Description
filemtime() [<a href='function.filemtime'>function.filemtime</a>]: stat failed for //demos/yii-blogdemo-enhanced-debug/assets/ec4f9b44/jquery.js
Source File
Ismael
(Shalanga)
June 25, 2009, 3:27am
35
Read first here: http://www.yiiframew…script/reviews/
I have the same issue Boris did.
For me in line 221 does not make sense use DIRECTORY_SEPARATOR because this a URL separator and not a file separator.
You said: "It is also used in several other places in this extension".
This is just coincidence. In my Windows XP DIRECTORY_SEPARATOR is '' ; and in your tests maybe is LINUX. And linux maybe is '/' separator, just like URL separator which is '/'.
mocapapa
(Mocapapa)
June 25, 2009, 6:05am
36
I hope the author could read this…
samdark
(Alexander Makarov)
July 6, 2009, 7:13pm
37
Updated version with separated $combineJs and $combineCss. DIRECTORY_SEPARATOR issue fixed. Also added some checks for file existence instead of using @.
<?php
/**
* Compress and cache used JS and CSS files.
* Needs jsmin in helpers and csstidy in extensions.
*
* Ties into the 1.0.4 (or > SVN 813) Yii CClientScript functions.
*
* @author Maxximus <maxximus007@gmail.com>
* @author Alexander Makarov <sam@rmcreative.ru>
*
* @link http://www.yiiframework.com/
* @copyright Copyright © 2008-2009
* @license http://www.yiiframework.com/license/
* @version 0.5
*/
class ExtendedClientScript extends CClientScript {
/**
* Compress all Javascript files with JSMin. JSMin must be installed as an extension in dir jsmin.
* code.google.com/p/jsmin-php/
*/
public $compressJs = false;
/**
* Compress all CSS files with CssTidy. CssTidy must be installed as an extension in dir csstidy.
* Specific browserhacks will be removed, so don't add them in to be compressed CSS files
* csstidy.sourceforge.net
*/
public $compressCss = false;
/**
* Combine all JS files into one.
*
* @var boolean
*/
public $combineJs = false;
/**
* Combine all CSS files into one. Be careful with relative paths in CSS.
*
* @var boolean
*/
public $combineCss = false;
/**
* Exclude certain files from inclusion. array('/path/to/excluded/file') Useful for fixed base
* and incidental additional JS.
*/
public $excludeFiles = array();
/**
* Path where the combined/compressed file will be stored. Will use coreScriptUrl if not defined
*/
public $filePath;
/**
* If true, all files to be included will be checked if they are modified.
* To enhance speed (eg production) set to false.
*/
public $autoRefresh = true;
/**
* Relative Url where the combined/compressed file can be found
*/
public $fileUrl;
/**
* Path where files can be found
*/
public $basePath;
/**
* Used for garbage collection. If not accessed for that period: remove.
*/
public $ttlDays = 1;
/**
* prefix for the combined/compressed files
*/
public $prefix = 'c_';
/**
* CssTidy template. See CssTidy for more information
*/
public $cssTidyTemplate = "highest_compression";
/**
* CssTidy parameters. See CssTidy for more information
*/
public $cssTidyConfig = array(
'css_level' => 'CSS2.1',
'discard_invalid_properties' => FALSE,
'lowercase_s' => FALSE,
'sort_properties' => FALSE,
'sort_selectors' => FALSE,
'preserve_css' => FALSE,
'timestamp' => FALSE,
'remove_bslash' => TRUE,
'compress_colors' => TRUE,
'compress_font-weight' => TRUE,
'remove_last_,' => TRUE,
'optimise_shorthands' => 1,
'case_properties' => 1,
'merge_selectors' => 2,
);
private $_changesHash = '';
private $_renewFile;
/**
* Will combine/compress JS and CSS if wanted/needed, and will continue with original
* renderHead afterwards
*
* @param string $output
*/
public function renderHead(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_HEAD]) && count($this->scriptFiles[parent::POS_HEAD]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_HEAD];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js',$jsFiles,parent::POS_HEAD);
}
}
if($this->combineCss) {
if (count($this->cssFiles) !== 0) {
foreach ($this->cssFiles as $url => $media)
$cssFiles[$media][$url] = $url;
foreach ($cssFiles as $media => $url)
$this->combineAndCompress('css',$url, $media);
}
}
parent::renderHead($output);
}
/**
* Will combine/compress JS if wanted/needed, and will continue with original
* renderBodyEnd afterwards
*
* @param string $output
*/
public function renderBodyBegin(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_BEGIN]) && count($this->scriptFiles[parent::POS_BEGIN]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_BEGIN];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js',$jsFiles,parent::POS_BEGIN);
}
}
parent::renderBodyBegin($output);
}
/**
* Will combine/compress JS if wanted/needed, and will continue with original
* renderBodyEnd afterwards
*
* @param string $output
*/
public function renderBodyEnd(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_END]) && count($this->scriptFiles[parent::POS_END]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_END];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js',$jsFiles,parent::POS_END);
}
}
parent::renderBodyEnd($output);
}
/**
* Performs the actual combining and compressing
*
* @param string $type
* @param array $urls
* @param string $pos
*/
private function combineAndCompress($type, $urls, $pos) {
$this->fileUrl or $this->fileUrl = $this->getCoreScriptUrl();
$this->filePath or $this->filePath = realpath($_SERVER['DOCUMENT_ROOT'].$this->fileUrl);
$this->basePath or $this->basePath = $_SERVER['DOCUMENT_ROOT'];
if ($this->autoRefresh) {
$mtimes = array();
foreach ($urls as $file) {
$fileName = $this->basePath.'/'.trim($file,'/');
if(file_exists($fileName)) {
$mtimes[] = filemtime($fileName);
}
}
$this->_changesHash = md5(serialize($mtimes));
}
$combineHash = md5(implode('',$urls));
$optionsHash = ($type == 'js') ? md5($this->basePath.$this->compressJs.$this->ttlDays.$this->prefix):
md5($this->basePath.$this->compressCss.$this->ttlDays.$this->prefix.serialize($this->cssTidyConfig));
$fileName = $this->prefix.md5($combineHash.$optionsHash.$this->_changesHash).".$type";
$this->_renewFile = (file_exists($this->filePath.'/'.$fileName)) ? false : true;
if ($this->_renewFile) {
$this->garbageCollect($type);
$combinedFile = '';
foreach ($urls as $key => $file)
$combinedFile .= file_get_contents($this->basePath.'/'.$file);
if ($type == 'js' && $this->compressJs)
$combinedFile = $this->minifyJs($combinedFile);
if ($type == 'css' && $this->compressCss)
$combinedFile = $this->minifyCss($combinedFile);
file_put_contents($this->filePath.'/'.$fileName, $combinedFile);
}
foreach ($urls as $url)
$this->scriptMap[basename($url)] = $this->fileUrl.'/'.$fileName;
$this->remapScripts();
}
private function garbageCollect($type) {
$files = CFileHelper::findFiles($this->filePath, array('fileTypes' => array($type), 'level'=> 0));
foreach($files as $file) {
if (strpos($file, $this->prefix) !== false && $this->fileTTL($file)){
unlink($file);
}
}
}
/**
* See if file is ready for deletion
*
* @param string $file
*/
private function fileTTL($file) {
if(!file_exists($file)) return false;
$ttl = $this->ttlDays * 60 * 60 * 24;
return ((fileatime($file) + $ttl) < time()) ? true : false;
}
/**
* Minify javascript with JSMin
*
* @param string $js
*/
private function minifyJs($js) {
Yii::import('application.extensions.jsmin.*');
require_once('JSMin.php');
return JSMin::minify($js);
}
/**
* Yii-ified version of CSS.php of the Minify package with fixed options
*
* @param string $css
*/
private function minifyCss($css) {
Yii::import('application.extensions.csstidy.*');
require_once('class.csstidy.php');
$cssTidy = new csstidy();
$cssTidy->load_template($this->cssTidyTemplate);
foreach($this->cssTidyConfig as $k => $v)
$cssTidy->set_cfg($k, $v);
$cssTidy->parse($css);
return $cssTidy->print->plain();
}
}
mocapapa
(Mocapapa)
July 6, 2009, 9:53pm
38
Thank you samdark.
Still exists the error. You can find on the agility hoster free hosting service . On the other hand, this extension is working well on the byethost free hosting service .
mocapapa
(Mocapapa)
July 7, 2009, 2:34am
39
Yii itself is working on the agilityhoster well, because there is no expression using $_SERVER[‘DOCUMENT_ROOT’] in the framework. On the other hand, this extension uses this variable and there is the case that it does not work on some hosts, thus we should remove this.
samdark
(Alexander Makarov)
July 7, 2009, 9:03am
40
Replaced DOCUMENT_ROOT with Yii's getPathOfAlias(). Please try if it works now.
<?php
/**
* Compress and cache used JS and CSS files.
* Needs jsmin in helpers and csstidy in extensions.
*
* Ties into the 1.0.4 (or > SVN 813) Yii CClientScript functions.
*
* @author Maxximus <maxximus007@gmail.com>
* @author Alexander Makarov <sam@rmcreative.ru>
*
* @link http://www.yiiframework.com/
* @copyright Copyright © 2008-2009
* @license http://www.yiiframework.com/license/
* @version 0.6
*/
class ExtendedClientScript extends CClientScript {
/**
* Compress all Javascript files with JSMin. JSMin must be installed as an extension in dir jsmin.
* code.google.com/p/jsmin-php/
*/
public $compressJs = false;
/**
* Compress all CSS files with CssTidy. CssTidy must be installed as an extension in dir csstidy.
* Specific browserhacks will be removed, so don't add them in to be compressed CSS files
* csstidy.sourceforge.net
*/
public $compressCss = false;
/**
* Combine all JS files into one.
*
* @var boolean
*/
public $combineJs = false;
/**
* Combine all CSS files into one. Be careful with relative paths in CSS.
*
* @var boolean
*/
public $combineCss = false;
/**
* Exclude certain files from inclusion. array('/path/to/excluded/file') Useful for fixed base
* and incidental additional JS.
*/
public $excludeFiles = array();
/**
* Path where the combined/compressed file will be stored. Will use coreScriptUrl if not defined
*/
public $filePath;
/**
* If true, all files to be included will be checked if they are modified.
* To enhance speed (eg production) set to false.
*/
public $autoRefresh = true;
/**
* Relative Url where the combined/compressed file can be found
*/
public $fileUrl;
/**
* Path where files can be found
*/
public $basePath;
/**
* Used for garbage collection. If not accessed for that period: remove.
*/
public $ttlDays = 1;
/**
* prefix for the combined/compressed files
*/
public $prefix = 'c_';
/**
* CssTidy template. See CssTidy for more information
*/
public $cssTidyTemplate = "highest_compression";
/**
* CssTidy parameters. See CssTidy for more information
*/
public $cssTidyConfig = array(
'css_level' => 'CSS2.1',
'discard_invalid_properties' => FALSE,
'lowercase_s' => FALSE,
'sort_properties' => FALSE,
'sort_selectors' => FALSE,
'preserve_css' => FALSE,
'timestamp' => FALSE,
'remove_bslash' => TRUE,
'compress_colors' => TRUE,
'compress_font-weight' => TRUE,
'remove_last_,' => TRUE,
'optimise_shorthands' => 1,
'case_properties' => 1,
'merge_selectors' => 2,
);
private $_changesHash = '';
private $_renewFile;
/**
* Will combine/compress JS and CSS if wanted/needed, and will continue with original
* renderHead afterwards
*
* @param string $output
*/
public function renderHead(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_HEAD]) && count($this->scriptFiles[parent::POS_HEAD]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_HEAD];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js',$jsFiles,parent::POS_HEAD);
}
}
if($this->combineCss) {
if (count($this->cssFiles) !== 0) {
foreach ($this->cssFiles as $url => $media)
$cssFiles[$media][$url] = $url;
foreach ($cssFiles as $media => $url)
$this->combineAndCompress('css',$url, $media);
}
}
parent::renderHead($output);
}
/**
* Will combine/compress JS if wanted/needed, and will continue with original
* renderBodyEnd afterwards
*
* @param string $output
*/
public function renderBodyBegin(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_BEGIN]) && count($this->scriptFiles[parent::POS_BEGIN]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_BEGIN];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js',$jsFiles,parent::POS_BEGIN);
}
}
parent::renderBodyBegin($output);
}
/**
* Will combine/compress JS if wanted/needed, and will continue with original
* renderBodyEnd afterwards
*
* @param string $output
*/
public function renderBodyEnd(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_END]) && count($this->scriptFiles[parent::POS_END]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_END];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js',$jsFiles,parent::POS_END);
}
}
parent::renderBodyEnd($output);
}
/**
* Performs the actual combining and compressing
*
* @param string $type
* @param array $urls
* @param string $pos
*/
private function combineAndCompress($type, $urls, $pos) {
$this->fileUrl or $this->fileUrl = $this->getCoreScriptUrl();
$this->basePath or $this->basePath = Yii::getPathOfAlias('webroot');
$this->filePath or $this->filePath = Yii::getPathOfAlias('webroot').$this->fileUrl;
if ($this->autoRefresh) {
$mtimes = array();
foreach ($urls as $file) {
$fileName = $this->basePath.'/'.trim($file,'/');
if(file_exists($fileName)) {
$mtimes[] = filemtime($fileName);
}
}
$this->_changesHash = md5(serialize($mtimes));
}
$combineHash = md5(implode('',$urls));
$optionsHash = ($type == 'js') ? md5($this->basePath.$this->compressJs.$this->ttlDays.$this->prefix):
md5($this->basePath.$this->compressCss.$this->ttlDays.$this->prefix.serialize($this->cssTidyConfig));
$fileName = $this->prefix.md5($combineHash.$optionsHash.$this->_changesHash).".$type";
$this->_renewFile = (file_exists($this->filePath.'/'.$fileName)) ? false : true;
if ($this->_renewFile) {
$this->garbageCollect($type);
$combinedFile = '';
foreach ($urls as $key => $file)
$combinedFile .= file_get_contents($this->basePath.'/'.$file);
if ($type == 'js' && $this->compressJs)
$combinedFile = $this->minifyJs($combinedFile);
if ($type == 'css' && $this->compressCss)
$combinedFile = $this->minifyCss($combinedFile);
file_put_contents($this->filePath.'/'.$fileName, $combinedFile);
}
foreach ($urls as $url)
$this->scriptMap[basename($url)] = $this->fileUrl.'/'.$fileName;
$this->remapScripts();
}
private function garbageCollect($type) {
$files = CFileHelper::findFiles($this->filePath, array('fileTypes' => array($type), 'level'=> 0));
foreach($files as $file) {
if (strpos($file, $this->prefix) !== false && $this->fileTTL($file)){
unlink($file);
}
}
}
/**
* See if file is ready for deletion
*
* @param string $file
*/
private function fileTTL($file) {
if(!file_exists($file)) return false;
$ttl = $this->ttlDays * 60 * 60 * 24;
return ((fileatime($file) + $ttl) < time()) ? true : false;
}
/**
* Minify javascript with JSMin
*
* @param string $js
*/
private function minifyJs($js) {
Yii::import('application.extensions.jsmin.*');
require_once('JSMin.php');
return JSMin::minify($js);
}
/**
* Yii-ified version of CSS.php of the Minify package with fixed options
*
* @param string $css
*/
private function minifyCss($css) {
Yii::import('application.extensions.csstidy.*');
require_once('class.csstidy.php');
$cssTidy = new csstidy();
$cssTidy->load_template($this->cssTidyTemplate);
foreach($this->cssTidyConfig as $k => $v)
$cssTidy->set_cfg($k, $v);
$cssTidy->parse($css);
return $cssTidy->print->plain();
}
}
Maxximus
(Maxximus007)
July 7, 2009, 9:20am
41
Okay, nice one! Did not test it yet, but will update soon.
mocapapa
(Mocapapa)
July 7, 2009, 9:22am
42
It still does not work . If it only happens on this free host, it may be mis-configuration of this host
EDIT: NO. It does not work every host I have.
samdark
(Alexander Makarov)
July 7, 2009, 12:40pm
43
Insert
echo Yii::getPathOfAlias(‘webroot’); somewhere and post results here. It can be helpful.
mocapapa
(Mocapapa)
July 7, 2009, 2:34pm
44
DOCUMENT_ROOT may rather be Yii::getPathOfAlias('system')."/…/" though it does not work on the agility hoster, while it works on the other hosts.
samdark
(Alexander Makarov)
July 7, 2009, 10:05pm
45
Yii::getPathOfAlias('webroot'); should give webroot.
mocapapa
(Mocapapa)
July 8, 2009, 2:51am
46
In terms of webroot, I have posted the result as below.
Quote
It still does
not work . If it only happens on this free host, it may be mis-configuration of this host
EDIT: NO. It does NOT work EVERY host I have.
samdark
(Alexander Makarov)
July 8, 2009, 9:53pm
47
OK. It does not work… but what is the content of
echo Yii::getPathOfAlias('webroot');
?
mocapapa
(Mocapapa)
July 9, 2009, 8:02am
48
The code
echo Yii::getPathOfAlias('webroot');
produces
Quote
/home/www/pugpug.agilityhoster.com/demos/yii-blogdemo-enhanced
FYI, you can see the info as below. DOCUMENT_ROOT may be wrong.
http://pugpug.agilit…/demos/info.php
As I myself modified to produce the equivarent expression as DOCUMENT_ROOT using following code, it produced internal error that I cannot analyze.
$this->basePath or $this->basePath = substr($_SERVER['SCRIPT_FILENAME'], 0, strpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['PHP_SELF']));
$this->filePath or $this->filePath = realpath($this->basePath.$this->fileUrl);
samdark
(Alexander Makarov)
July 9, 2009, 11:34am
49
Quote
/home/www/pugpug.agilityhoster.com/demos/yii-blogdemo-enhanced
If your webroot is /demos/yii-blogdemo-enhanced it looks OK.
revo110
(Tjhaichristian)
July 10, 2009, 1:04am
50
Quote
Replaced DOCUMENT_ROOT with Yii's getPathOfAlias(). Please try if it works now.
HI samdark, thanks for this issue fix. But at my workstation, DOCUMENT_ROOT is working, while the Yii's getPathOfAlias() is not.
So I changed back to DOCUMENT_ROOT, and it works well.
$this->fileUrl or $this->fileUrl = $this->getCoreScriptUrl();
$this->basePath or $this->basePath = Yii::getPathOfAlias('webroot');
//$this->filePath or $this->filePath = Yii::getPathOfAlias('webroot').$this->fileUrl);
$this->filePath or $this->filePath = realpath($_SERVER['DOCUMENT_ROOT'].$this->fileUrl);
Thanks again!
PS: Run this on Linux and Windows.
drech
(Agitated)
August 20, 2009, 5:14pm
51
I’m trying to use Yii’s CStarRating widget with extendedclientscript.
It works with CSS and Javascript compression set to true, but when I set combineFiles to true, only little boxes outlined in red show up.
Any ideas?
drech
(Agitated)
August 20, 2009, 7:25pm
52
It turns out the widget CSS bundled with CStarRating couldn’t find it’s images in the same directory (I’m guessing because of something done during extendedclientscript’s combine and/or JSMin compression).
I had to override the CSS file in CStarRating.php to a duplicate in /css and it worked.
The little red boxes are a result of a 1px border in that same CSS file. I removed that and now it all looks normal.
drech
(Agitated)
August 20, 2009, 7:29pm
53
and THEN I learned I could override the CSS file in the call to the widget itself…lol, I’m doing good work over here