<?php
/**
* PBMAssetManager class file.
* @author Chris Yates <chris.l.yates@gmail.com>
* @copyright Copyright © 2010 PBM Web Development
* @license http://phamlp.googlecode.com/files/license.txt
* @version $Id: PBMAssetManager.php 9 2010-03-21 12:54:48Z Chris $
*/
/**
* PBMAssetManager class.
* PBMAssetManager overrides CAssetManager::publish to provide parsing of assets
* when required.
*
* Configuration
* -------------
* Import the component.
* Yii::import('path.to.component.PBMAssetManager');
*
* Declare the use of this component as the asset manager component. This
* example declares a Sass {@link } parser; multiple parsers may be declared.
* <pre>
* // application components
* 'components'=>array(
* 'assetManager' => array(
* 'class' => 'PBMAssetManager',
* 'parsers' => array(
* 'sass' => array( // key == the type of file to parse
* 'class' => 'ext.haml.Sass', // path alias to the parser
* 'output' => 'css', // the file type it is parsed to
* 'options' => array(<Parser specific options>)
* ),
* )
* )
* )
* </pre>
*
* You can also declare the "force" parameter to be true. This forces assets to
* be published whether newer than the published asset or not; this is for
* development so that changes to deep files get published without having to
* flush the asset directory. Make sure this parameter is removed or declared
* false in production.
*
* Usage
* -----
* Usage is exactly the same as publishing an asset with CAssetManager, i.e.
*
* $publishedAsset = Yii::app()->getAssetMananger()->publish(Yii::getPathOfAlias('allias.to.asset.directory'). DIRECTORY_SEPARATOR . 'asset.sass');
*
* The only difference is that parsing of files will take place during the
* publish. Files that do not require parsing are handled exactly as before.
*/
class PBMAssetManager extends CAssetManager {
/**
* @var array asset parsers
*/
public $parsers;
/**
* @var boolean if true the asset will always be published
*/
public $force = false;
/**
* @var string base web accessible path for storing private files
*/
private $_basePath;
/**
* @var string base URL for accessing the publishing directory.
*/
private $_baseUrl;
/**
* @var array published assets
*/
private $_published=array();
/**
* Publishes a file or a directory.
* This method will copy the specified asset to a web accessible directory
* and return the URL for accessing the published asset.
* <ul>
* <li>If the asset is a file, its file modification time will be checked
* to avoid unnecessary file copying;</li>
* <li>If the asset is a directory, all files and subdirectories under it will
* be published recursively. Note, in this case the method only checks the
* existence of the target directory to avoid repetitive copying.</li>
* </ul>
* @param string the asset (file or directory) to be published
* @param boolean whether the published directory should be named as the hashed basename.
* If false, the name will be the hashed dirname of the path being published.
* Defaults to false. Set true if the path being published is shared among
* different suffixs.
* @param integer level of recursive copying when the asset is a directory.
* Level -1 means publishing all subdirectories and files;
* Level 0 means publishing only the files DIRECTLY under the directory;
* level N means copying those directories that are within N levels.
* @return string an absolute URL to the published asset
* @throws CException if the asset to be published does not exist.
*/
public function publish($path,$hashByName=false,$level=-1) {
if(isset($this->_published[$path])) {
return $this->_published[$path];
}
else if(($src=realpath($path))!==false) {
if(is_file($src)) {
$dir=$this->hash($hashByName ? basename($src) : dirname($src));
$fileName=basename($src);
$suffix=substr(strrchr($fileName, '.'), 1);
$dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir;
if (array_key_exists($suffix, $this->parsers)) {
$fileName=basename($src, $suffix);
$fileName=basename($src, $suffix).$this->parsers[$suffix]['output'];
}
$dstFile=$dstDir.DIRECTORY_SEPARATOR.$fileName;
if($this->force || @filemtime($dstFile)<@filemtime($src)) {
if(!is_dir($dstDir)) {
mkdir($dstDir);
@chmod($dstDir,0777);
}
if (array_key_exists($suffix, $this->parsers)) {
$parserClass = Yii::import($this->parsers[$suffix]['class']);
$parser = new $parserClass($this->parsers[$suffix]['options']);
file_put_contents($dstFile, $parser->parse($src));
}
else {
copy($src,$dstFile);
}
}
return $this->_published[$path]=$this->getBaseUrl()."/$dir/$fileName";
}
else if(is_dir($src)) {
$dir=$this->hash($hashByName ? basename($src) : $src);
$dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir;
if(!is_dir($dstDir)) {
CFileHelper::copyDirectory($src,$dstDir,array('exclude'=>array('.svn'),'level'=>$level));
}
return $this->_published[$path]=$this->getBaseUrl().'/'.$dir;
}
}
throw new CException(Yii::t('yii','The asset "{asset}" to be published does not exist.',
array('{asset}'=>$path)));
}
}