]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Web/TAssetManager.php
272d4400e706e4b619f30b96fbca6b842a9c3f7e
[bacula/bacula] / gui / baculum / framework / Web / TAssetManager.php
1 <?php
2 /**
3  * TAssetManager class
4  *
5  * @author Qiang Xue <qiang.xue@gmail.com>
6  * @link http://www.pradosoft.com/
7  * @copyright Copyright &copy; 2005-2014 PradoSoft
8  * @license http://www.pradosoft.com/license/
9  * @package System.Web
10  */
11
12 /**
13  * TAssetManager class
14  *
15  * TAssetManager provides a scheme to allow web clients visiting
16  * private files that are normally web-inaccessible.
17  *
18  * TAssetManager will copy the file to be published into a web-accessible
19  * directory. The default base directory for storing the file is "assets", which
20  * should be under the application directory. This can be changed by setting
21  * the {@link setBasePath BasePath} property together with the
22  * {@link setBaseUrl BaseUrl} property that refers to the URL for accessing the base path.
23  *
24  * By default, TAssetManager will not publish a file or directory if it already
25  * exists in the publishing directory and has an older modification time.
26  * If the application mode is set as 'Performance', the modification time check
27  * will be skipped. You can explicitly require a modification time check
28  * with the function {@link publishFilePath}. This is usually
29  * very useful during development.
30  *
31  * TAssetManager may be configured in application configuration file as follows,
32  * <code>
33  * <module id="asset" BasePath="Application.assets" BaseUrl="/assets" />
34  * </code>
35  * where {@link getBasePath BasePath} and {@link getBaseUrl BaseUrl} are
36  * configurable properties of TAssetManager. Make sure that BasePath is a namespace
37  * pointing to a valid directory writable by the Web server process.
38  *
39  * @author Qiang Xue <qiang.xue@gmail.com>
40  * @package System.Web
41  * @since 3.0
42  */
43 class TAssetManager extends TModule
44 {
45         /**
46          * Default web accessible base path for storing private files
47          */
48         const DEFAULT_BASEPATH='assets';
49         /**
50          * @var string base web accessible path for storing private files
51          */
52         private $_basePath=null;
53         /**
54          * @var string base URL for accessing the publishing directory.
55          */
56         private $_baseUrl=null;
57         /**
58          * @var boolean whether to use timestamp checking to ensure files are published with up-to-date versions.
59          */
60         private $_checkTimestamp=false;
61         /**
62          * @var TApplication application instance
63          */
64         private $_application;
65         /**
66          * @var array published assets
67          */
68         private $_published=array();
69         /**
70          * @var boolean whether the module is initialized
71          */
72         private $_initialized=false;
73
74         /**
75          * Initializes the module.
76          * This method is required by IModule and is invoked by application.
77          * @param TXmlElement module configuration
78          */
79         public function init($config)
80         {
81                 $application=$this->getApplication();
82                 if($this->_basePath===null)
83                         $this->_basePath=dirname($application->getRequest()->getApplicationFilePath()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH;
84                 if(!is_writable($this->_basePath) || !is_dir($this->_basePath))
85                         throw new TConfigurationException('assetmanager_basepath_invalid',$this->_basePath);
86                 if($this->_baseUrl===null)
87                         $this->_baseUrl=rtrim(dirname($application->getRequest()->getApplicationUrl()),'/\\').'/'.self::DEFAULT_BASEPATH;
88                 $application->setAssetManager($this);
89                 $this->_initialized=true;
90         }
91
92         /**
93          * @return string the root directory storing published asset files
94          */
95         public function getBasePath()
96         {
97                 return $this->_basePath;
98         }
99
100         /**
101          * Sets the root directory storing published asset files.
102          * The directory must be in namespace format.
103          * @param string the root directory storing published asset files
104          * @throws TInvalidOperationException if the module is initialized already
105          */
106         public function setBasePath($value)
107         {
108                 if($this->_initialized)
109                         throw new TInvalidOperationException('assetmanager_basepath_unchangeable');
110                 else
111                 {
112                         $this->_basePath=Prado::getPathOfNamespace($value);
113                         if($this->_basePath===null || !is_dir($this->_basePath) || !is_writable($this->_basePath))
114                                 throw new TInvalidDataValueException('assetmanager_basepath_invalid',$value);
115                 }
116         }
117
118         /**
119          * @return string the base url that the published asset files can be accessed
120          */
121         public function getBaseUrl()
122         {
123                 return $this->_baseUrl;
124         }
125
126         /**
127          * @param string the base url that the published asset files can be accessed
128          * @throws TInvalidOperationException if the module is initialized already
129          */
130         public function setBaseUrl($value)
131         {
132                 if($this->_initialized)
133                         throw new TInvalidOperationException('assetmanager_baseurl_unchangeable');
134                 else
135                         $this->_baseUrl=rtrim($value,'/');
136         }
137
138         /**
139          * Publishes a file or a directory (recursively).
140          * This method will copy the content in a directory (recursively) to
141          * a web accessible directory and returns the URL for the directory.
142          * If the application is not in performance mode, the file modification
143          * time will be used to make sure the published file is latest or not.
144          * If not, a file copy will be performed.
145          * @param string the path to be published
146          * @param boolean If true, file modification time will be checked even if the application
147          * is in performance mode.
148          * @return string an absolute URL to the published directory
149          * @throws TInvalidDataValueException if the file path to be published is
150          * invalid
151          */
152         public function publishFilePath($path,$checkTimestamp=false)
153         {
154                 if(isset($this->_published[$path]))
155                         return $this->_published[$path];
156                 else if(empty($path) || ($fullpath=realpath($path))===false)
157                         throw new TInvalidDataValueException('assetmanager_filepath_invalid',$path);
158                 else if(is_file($fullpath))
159                 {
160                         $dir=$this->hash(dirname($fullpath));
161                         $fileName=basename($fullpath);
162                         $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir;
163                         if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance)
164                                 $this->copyFile($fullpath,$dst);
165                         return $this->_published[$path]=$this->_baseUrl.'/'.$dir.'/'.$fileName;
166                 }
167                 else
168                 {
169                         $dir=$this->hash($fullpath);
170                         if(!is_dir($this->_basePath.DIRECTORY_SEPARATOR.$dir) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance)
171                         {
172                                 Prado::trace("Publishing directory $fullpath",'System.Web.UI.TAssetManager');
173                                 $this->copyDirectory($fullpath,$this->_basePath.DIRECTORY_SEPARATOR.$dir);
174                         }
175                         return $this->_published[$path]=$this->_baseUrl.'/'.$dir;
176                 }
177         }
178
179         /**
180          * @return array List of published assets
181          * @since 3.1.6
182          */
183         public function getPublished()
184         {
185                 return $this->_published;
186         }
187
188         /**
189          * @param $values List of published assets
190          * @since 3.1.6
191          */
192         protected function setPublished($values=array())
193         {
194                 $this->_published = $values;
195         }
196
197         /**
198          * Returns the published path of a file path.
199          * This method does not perform any publishing. It merely tells you
200          * if the file path is published, where it will go.
201          * @param string directory or file path being published
202          * @return string the published file path
203          */
204         public function getPublishedPath($path)
205         {
206                 $path=realpath($path);
207                 if(is_file($path))
208                         return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash(dirname($path)).DIRECTORY_SEPARATOR.basename($path);
209                 else
210                         return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash($path);
211         }
212
213         /**
214          * Returns the URL of a published file path.
215          * This method does not perform any publishing. It merely tells you
216          * if the file path is published, what the URL will be to access it.
217          * @param string directory or file path being published
218          * @return string the published URL for the file path
219          */
220         public function getPublishedUrl($path)
221         {
222                 $path=realpath($path);
223                 if(is_file($path))
224                         return $this->_baseUrl.'/'.$this->hash(dirname($path)).'/'.basename($path);
225                 else
226                         return $this->_baseUrl.'/'.$this->hash($path);
227         }
228
229         /**
230          * Generate a CRC32 hash for the directory path. Collisions are higher
231          * than MD5 but generates a much smaller hash string.
232          * @param string string to be hashed.
233          * @return string hashed string.
234          */
235         protected function hash($dir)
236         {
237                 return sprintf('%x',crc32($dir.Prado::getVersion()));
238         }
239
240         /**
241          * Copies a file to a directory.
242          * Copying is done only when the destination file does not exist
243          * or has an older file modification time.
244          * @param string source file path
245          * @param string destination directory (if not exists, it will be created)
246          */
247         protected function copyFile($src,$dst)
248         {
249                 if(!is_dir($dst))
250                 {
251                         @mkdir($dst);
252                         @chmod($dst, PRADO_CHMOD);
253                 }
254                 $dstFile=$dst.DIRECTORY_SEPARATOR.basename($src);
255                 if(@filemtime($dstFile)<@filemtime($src))
256                 {
257                         Prado::trace("Publishing file $src to $dstFile",'System.Web.TAssetManager');
258                         @copy($src,$dstFile);
259                 }
260         }
261
262         /**
263          * Copies a directory recursively as another.
264          * If the destination directory does not exist, it will be created.
265          * File modification time is used to ensure the copied files are latest.
266          * @param string the source directory
267          * @param string the destination directory
268          * @todo a generic solution to ignore certain directories and files
269          */
270         public function copyDirectory($src,$dst)
271         {
272                 if(!is_dir($dst))
273                 {
274                         @mkdir($dst);
275                         @chmod($dst, PRADO_CHMOD);
276                 }
277                 if($folder=@opendir($src))
278                 {
279                         while($file=@readdir($folder))
280                         {
281                                 if($file==='.' || $file==='..' || $file==='.svn')
282                                         continue;
283                                 else if(is_file($src.DIRECTORY_SEPARATOR.$file))
284                                 {
285                                         if(@filemtime($dst.DIRECTORY_SEPARATOR.$file)<@filemtime($src.DIRECTORY_SEPARATOR.$file))
286                                         {
287                                                 @copy($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file);
288                                                 @chmod($dst.DIRECTORY_SEPARATOR.$file, PRADO_CHMOD);
289                                         }
290                                 }
291                                 else
292                                         $this->copyDirectory($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file);
293                         }
294                         closedir($folder);
295                 } else {
296                         throw new TInvalidDataValueException('assetmanager_source_directory_invalid', $src);
297                 }
298         }
299
300         /**
301          * Publish a tar file by extracting its contents to the assets directory.
302          * Each tar file must be accomplished with its own MD5 check sum file.
303          * The MD5 file is published when the tar contents are successfully
304          * extracted to the assets directory. The presence of the MD5 file
305          * as published asset assumes that the tar file has already been extracted.
306          * @param string tar filename
307          * @param string MD5 checksum for the corresponding tar file.
308          * @param boolean Wether or not to check the time stamp of the file for publishing. Defaults to false.
309          * @return string URL path to the directory where the tar file was extracted.
310          */
311         public function publishTarFile($tarfile, $md5sum, $checkTimestamp=false)
312         {
313                 if(isset($this->_published[$md5sum]))
314                         return $this->_published[$md5sum];
315                 else if(($fullpath=realpath($md5sum))===false || !is_file($fullpath))
316                         throw new TInvalidDataValueException('assetmanager_tarchecksum_invalid',$md5sum);
317                 else
318                 {
319                         $dir=$this->hash(dirname($fullpath));
320                         $fileName=basename($fullpath);
321                         $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir;
322                         if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance)
323                         {
324                                 if(@filemtime($dst.DIRECTORY_SEPARATOR.$fileName)<@filemtime($fullpath))
325                                 {
326                                         $this->copyFile($fullpath,$dst);
327                                         $this->deployTarFile($tarfile,$dst);
328                                 }
329                         }
330                         return $this->_published[$md5sum]=$this->_baseUrl.'/'.$dir;
331                 }
332         }
333
334         /**
335          * Extracts the tar file to the destination directory.
336          * N.B Tar file must not be compressed.
337          * @param string tar file
338          * @param string path where the contents of tar file are to be extracted
339          * @return boolean true if extract successful, false otherwise.
340          */
341         protected function deployTarFile($path,$destination)
342         {
343                 if(($fullpath=realpath($path))===false || !is_file($fullpath))
344                         throw new TIOException('assetmanager_tarfile_invalid',$path);
345                 else
346                 {
347                         Prado::using('System.IO.TTarFileExtractor');
348                         $tar = new TTarFileExtractor($fullpath);
349                         return $tar->extract($destination);
350                 }
351         }
352
353 }
354