Qiang Xue
13 years ago
18 changed files with 4352 additions and 38 deletions
@ -0,0 +1,115 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Theme class file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* Theme represents an application theme. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Theme extends ApplicationComponent |
||||||
|
{ |
||||||
|
private $_name; |
||||||
|
private $_basePath; |
||||||
|
private $_baseUrl; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param string $name name of the theme |
||||||
|
* @param string $basePath base theme path |
||||||
|
* @param string $baseUrl base theme URL |
||||||
|
*/ |
||||||
|
public function __construct($name, $basePath, $baseUrl) |
||||||
|
{ |
||||||
|
$this->_name = $name; |
||||||
|
$this->_baseUrl = $baseUrl; |
||||||
|
$this->_basePath = $basePath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string theme name |
||||||
|
*/ |
||||||
|
public function getName() |
||||||
|
{ |
||||||
|
return $this->_name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the relative URL to the theme folder (without ending slash) |
||||||
|
*/ |
||||||
|
public function getBaseUrl() |
||||||
|
{ |
||||||
|
return $this->_baseUrl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the file path to the theme folder |
||||||
|
*/ |
||||||
|
public function getBasePath() |
||||||
|
{ |
||||||
|
return $this->_basePath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the path for controller views. Defaults to 'ThemeRoot/views'. |
||||||
|
*/ |
||||||
|
public function getViewPath() |
||||||
|
{ |
||||||
|
return $this->_basePath . DIRECTORY_SEPARATOR . 'views'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finds the view file for the specified controller's view. |
||||||
|
* @param CController $controller the controller |
||||||
|
* @param string $viewName the view name |
||||||
|
* @return string the view file path. False if the file does not exist. |
||||||
|
*/ |
||||||
|
public function getViewFile($controller, $viewName) |
||||||
|
{ |
||||||
|
$moduleViewPath = $this->getViewPath(); |
||||||
|
if (($module = $controller->getModule()) !== null) |
||||||
|
{ |
||||||
|
$moduleViewPath .= '/' . $module->getId(); |
||||||
|
} |
||||||
|
return $controller->resolveViewFile($viewName, $this->getViewPath() . '/' . $controller->getUniqueId(), $this->getViewPath(), $moduleViewPath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finds the layout file for the specified controller's layout. |
||||||
|
* @param CController $controller the controller |
||||||
|
* @param string $layoutName the layout name |
||||||
|
* @return string the layout file path. False if the file does not exist. |
||||||
|
*/ |
||||||
|
public function getLayoutFile($controller, $layoutName) |
||||||
|
{ |
||||||
|
$moduleViewPath = $basePath = $this->getViewPath(); |
||||||
|
$module = $controller->getModule(); |
||||||
|
if (empty($layoutName)) { |
||||||
|
while ($module !== null) { |
||||||
|
if ($module->layout === false) |
||||||
|
return false; |
||||||
|
if (!empty($module->layout)) |
||||||
|
break; |
||||||
|
$module = $module->getParentModule(); |
||||||
|
} |
||||||
|
if ($module === null) |
||||||
|
$layoutName = Yii::app()->layout; |
||||||
|
else { |
||||||
|
$layoutName = $module->layout; |
||||||
|
$moduleViewPath .= '/' . $module->getId(); |
||||||
|
} |
||||||
|
} |
||||||
|
else if ($module !== null) |
||||||
|
$moduleViewPath .= '/' . $module->getId(); |
||||||
|
|
||||||
|
return $controller->resolveViewFile($layoutName, $moduleViewPath . '/layouts', $basePath, $moduleViewPath); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* ThemeManager class file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* ThemeManager manages the themes for the Web application. |
||||||
|
* |
||||||
|
* A theme is a collection of view/layout files and resource files |
||||||
|
* (e.g. css, image, js files). When a theme is active, {@link CController} |
||||||
|
* will look for the specified view/layout under the theme folder first. |
||||||
|
* The corresponding view/layout files will be used if the theme provides them. |
||||||
|
* Otherwise, the default view/layout files will be used. |
||||||
|
* |
||||||
|
* By default, each theme is organized as a directory whose name is the theme name. |
||||||
|
* All themes are located under the "WebRootPath/themes" directory. |
||||||
|
* |
||||||
|
* To activate a theme, set the {@link CWebApplication::setTheme theme} property |
||||||
|
* to be the name of that theme. |
||||||
|
* |
||||||
|
* Since a self-contained theme often contains resource files that are made |
||||||
|
* Web accessible, please make sure the view/layout files are protected from Web access. |
||||||
|
* |
||||||
|
* @property array $themeNames List of available theme names. |
||||||
|
* @property string $basePath The base path for all themes. Defaults to "WebRootPath/themes". |
||||||
|
* @property string $baseUrl The base URL for all themes. Defaults to "/WebRoot/themes". |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class ThemeManager extends ApplicationComponent |
||||||
|
{ |
||||||
|
/** |
||||||
|
* default themes base path |
||||||
|
*/ |
||||||
|
const DEFAULT_BASEPATH = 'themes'; |
||||||
|
|
||||||
|
/** |
||||||
|
* @var string the name of the theme class for representing a theme. |
||||||
|
* Defaults to {@link Theme}. This can also be a class name in dot syntax. |
||||||
|
*/ |
||||||
|
public $themeClass = 'Theme'; |
||||||
|
/** |
||||||
|
* @var string the base path containing all themes. Defaults to '@entry/themes'. |
||||||
|
*/ |
||||||
|
public $basePath = '@entry/themes'; |
||||||
|
/** |
||||||
|
* @var string the base URL for all themes. Defaults to "@www/themes". |
||||||
|
*/ |
||||||
|
public $baseUrl = '@www/themes'; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $name name of the theme to be retrieved |
||||||
|
* @return Theme the theme retrieved. Null if the theme does not exist. |
||||||
|
*/ |
||||||
|
public function getTheme($name) |
||||||
|
{ |
||||||
|
$themePath = $this->getBasePath() . DIRECTORY_SEPARATOR . $name; |
||||||
|
if (is_dir($themePath)) { |
||||||
|
$class = Yii::import($this->themeClass, true); |
||||||
|
return new $class($name, $themePath, $this->getBaseUrl() . '/' . $name); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return array list of available theme names |
||||||
|
*/ |
||||||
|
public function getThemeNames() |
||||||
|
{ |
||||||
|
static $themes; |
||||||
|
if ($themes === null) { |
||||||
|
$themes = array(); |
||||||
|
$basePath = $this->getBasePath(); |
||||||
|
$folder = @opendir($basePath); |
||||||
|
while (($file = @readdir($folder)) !== false) { |
||||||
|
if ($file !== '.' && $file !== '..' && $file !== '.svn' && $file !== '.gitignore' && is_dir($basePath . DIRECTORY_SEPARATOR . $file)) { |
||||||
|
$themes[] = $file; |
||||||
|
} |
||||||
|
} |
||||||
|
closedir($folder); |
||||||
|
sort($themes); |
||||||
|
} |
||||||
|
return $themes; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Widget class file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* Widget is the base class for widgets. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Widget extends Component implements Initable |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var mixed the name of the skin to be used by this widget. Defaults to 'default'. |
||||||
|
* If this is set as false, no skin will be applied to this widget. |
||||||
|
*/ |
||||||
|
public $skin = 'default'; |
||||||
|
/** |
||||||
|
* @var Widget|Controller the owner/creator of this widget. It could be either a widget or a controller. |
||||||
|
*/ |
||||||
|
public $owner; |
||||||
|
/** |
||||||
|
* @var string id of the widget. |
||||||
|
*/ |
||||||
|
private $_id; |
||||||
|
/** |
||||||
|
* @var integer a counter used to generate IDs for widgets. |
||||||
|
*/ |
||||||
|
private static $_counter = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param Widget|Controller $owner owner/creator of this widget. |
||||||
|
*/ |
||||||
|
public function __construct($owner) |
||||||
|
{ |
||||||
|
$this->owner = $owner; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the ID of the widget. |
||||||
|
* @param boolean $autoGenerate whether to generate an ID if it is not set previously |
||||||
|
* @return string ID of the widget. |
||||||
|
*/ |
||||||
|
public function getId($autoGenerate = true) |
||||||
|
{ |
||||||
|
if ($autoGenerate && $this->_id === null) { |
||||||
|
$this->_id = 'yw' . self::$_counter++; |
||||||
|
} |
||||||
|
return $this->_id; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the ID of the widget. |
||||||
|
* @param string $value id of the widget. |
||||||
|
*/ |
||||||
|
public function setId($value) |
||||||
|
{ |
||||||
|
$this->_id = $value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes the widget. |
||||||
|
*/ |
||||||
|
public function init() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Executes the widget. |
||||||
|
* @return string the rendering result of the widget |
||||||
|
*/ |
||||||
|
public function run() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders a view. |
||||||
|
* |
||||||
|
* The method first finds the actual view file corresponding to the specified view. |
||||||
|
* It then calls [[renderFile()]] to render the view file. The rendering result is returned |
||||||
|
* as a string. If the view file does not exist, an exception will be thrown. |
||||||
|
* |
||||||
|
* To determine which view file should be rendered, the method calls [[findViewFile()]] which |
||||||
|
* will search in the directories as specified by [[basePath]]. |
||||||
|
* |
||||||
|
* View name can be a path alias representing an absolute file path (e.g. `@app/views/layout/index`), |
||||||
|
* or a path relative to [[basePath]]. The file suffix is optional and defaults to `.php` if not given |
||||||
|
* in the view name. |
||||||
|
* |
||||||
|
* @param string $view the view to be rendered. This can be either a path alias or a path relative to [[basePath]]. |
||||||
|
* @param array $params the parameters that should be made available in the view. The PHP function `extract()` |
||||||
|
* will be called on this variable to extract the variables from this parameter. |
||||||
|
* @return string the rendering result |
||||||
|
* @throws Exception if the view file cannot be found |
||||||
|
*/ |
||||||
|
public function render($view, $params = array()) |
||||||
|
{ |
||||||
|
return $this->createView()->render($view, $params); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return View |
||||||
|
*/ |
||||||
|
public function createView() |
||||||
|
{ |
||||||
|
$view = new View; |
||||||
|
if (($theme = \Yii::$application->getTheme()) !== null) { |
||||||
|
$view->basePath[] = $theme->getViewPath() . DIRECTORY_SEPARATOR . str_replace('\\', '_', get_class($this)); |
||||||
|
} |
||||||
|
$class = new \ReflectionClass($this); |
||||||
|
$view->basePath[] = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views'; |
||||||
|
return $view; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Application class file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\web; |
||||||
|
|
||||||
|
/** |
||||||
|
* Application is the base class for all application classes. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Application extends \yii\base\Application |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Processes the request. |
||||||
|
* @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal) |
||||||
|
*/ |
||||||
|
public function processRequest() |
||||||
|
{ |
||||||
|
$route = $this->resolveRequest(); |
||||||
|
return $this->runController($route, null); |
||||||
|
} |
||||||
|
|
||||||
|
protected function resolveRequest() |
||||||
|
{ |
||||||
|
return array(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,304 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CAssetManager class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* CAssetManager is a Web application component that manages private files (called assets) and makes them accessible by Web clients. |
||||||
|
* |
||||||
|
* It achieves this goal by copying assets to a Web-accessible directory |
||||||
|
* and returns the corresponding URL for accessing them. |
||||||
|
* |
||||||
|
* To publish an asset, simply call {@link publish()}. |
||||||
|
* |
||||||
|
* The Web-accessible directory holding the published files is specified |
||||||
|
* by {@link setBasePath basePath}, which defaults to the "assets" directory |
||||||
|
* under the directory containing the application entry script file. |
||||||
|
* The property {@link setBaseUrl baseUrl} refers to the URL for accessing |
||||||
|
* the {@link setBasePath basePath}. |
||||||
|
* |
||||||
|
* @property string $basePath The root directory storing the published asset files. Defaults to 'WebRoot/assets'. |
||||||
|
* @property string $baseUrl The base url that the published asset files can be accessed. |
||||||
|
* Note, the ending slashes are stripped off. Defaults to '/AppBaseUrl/assets'. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CAssetManager extends CApplicationComponent |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Default web accessible base path for storing private files |
||||||
|
*/ |
||||||
|
const DEFAULT_BASEPATH='assets'; |
||||||
|
/** |
||||||
|
* @var boolean whether to use symbolic link to publish asset files. Defaults to false, meaning |
||||||
|
* asset files are copied to public folders. Using symbolic links has the benefit that the published |
||||||
|
* assets will always be consistent with the source assets. This is especially useful during development. |
||||||
|
* |
||||||
|
* However, there are special requirements for hosting environments in order to use symbolic links. |
||||||
|
* In particular, symbolic links are supported only on Linux/Unix, and Windows Vista/2008 or greater. |
||||||
|
* The latter requires PHP 5.3 or greater. |
||||||
|
* |
||||||
|
* Moreover, some Web servers need to be properly configured so that the linked assets are accessible |
||||||
|
* to Web users. For example, for Apache Web server, the following configuration directive should be added |
||||||
|
* for the Web folder: |
||||||
|
* <pre> |
||||||
|
* Options FollowSymLinks |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @since 1.1.5 |
||||||
|
*/ |
||||||
|
public $linkAssets=false; |
||||||
|
/** |
||||||
|
* @var array list of directories and files which should be excluded from the publishing process. |
||||||
|
* Defaults to exclude '.svn' and '.gitignore' files only. This option has no effect if {@link linkAssets} is enabled. |
||||||
|
* @since 1.1.6 |
||||||
|
**/ |
||||||
|
public $excludeFiles=array('.svn','.gitignore'); |
||||||
|
/** |
||||||
|
* @var integer the permission to be set for newly generated asset files. |
||||||
|
* This value will be used by PHP chmod function. |
||||||
|
* Defaults to 0666, meaning the file is read-writable by all users. |
||||||
|
* @since 1.1.8 |
||||||
|
*/ |
||||||
|
public $newFileMode=0666; |
||||||
|
/** |
||||||
|
* @var integer the permission to be set for newly generated asset directories. |
||||||
|
* This value will be used by PHP chmod function. |
||||||
|
* Defaults to 0777, meaning the directory can be read, written and executed by all users. |
||||||
|
* @since 1.1.8 |
||||||
|
*/ |
||||||
|
public $newDirMode=0777; |
||||||
|
/** |
||||||
|
* @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(); |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the root directory storing the published asset files. Defaults to 'WebRoot/assets'. |
||||||
|
*/ |
||||||
|
public function getBasePath() |
||||||
|
{ |
||||||
|
if($this->_basePath===null) |
||||||
|
{ |
||||||
|
$request=Yii::app()->getRequest(); |
||||||
|
$this->setBasePath(dirname($request->getScriptFile()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH); |
||||||
|
} |
||||||
|
return $this->_basePath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the root directory storing published asset files. |
||||||
|
* @param string $value the root directory storing published asset files |
||||||
|
* @throws CException if the base path is invalid |
||||||
|
*/ |
||||||
|
public function setBasePath($value) |
||||||
|
{ |
||||||
|
if(($basePath=realpath($value))!==false && is_dir($basePath) && is_writable($basePath)) |
||||||
|
$this->_basePath=$basePath; |
||||||
|
else |
||||||
|
throw new CException(Yii::t('yii','CAssetManager.basePath "{path}" is invalid. Please make sure the directory exists and is writable by the Web server process.', |
||||||
|
array('{path}'=>$value))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the base url that the published asset files can be accessed. |
||||||
|
* Note, the ending slashes are stripped off. Defaults to '/AppBaseUrl/assets'. |
||||||
|
*/ |
||||||
|
public function getBaseUrl() |
||||||
|
{ |
||||||
|
if($this->_baseUrl===null) |
||||||
|
{ |
||||||
|
$request=Yii::app()->getRequest(); |
||||||
|
$this->setBaseUrl($request->getBaseUrl().'/'.self::DEFAULT_BASEPATH); |
||||||
|
} |
||||||
|
return $this->_baseUrl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $value the base url that the published asset files can be accessed |
||||||
|
*/ |
||||||
|
public function setBaseUrl($value) |
||||||
|
{ |
||||||
|
$this->_baseUrl=rtrim($value,'/'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 case $forceCopy is false the method only checks the |
||||||
|
* existence of the target directory to avoid repetitive copying.</li> |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* Note: On rare scenario, a race condition can develop that will lead to a |
||||||
|
* one-time-manifestation of a non-critical problem in the creation of the directory |
||||||
|
* that holds the published assets. This problem can be avoided altogether by 'requesting' |
||||||
|
* in advance all the resources that are supposed to trigger a 'publish()' call, and doing |
||||||
|
* that in the application deployment phase, before system goes live. See more in the following |
||||||
|
* discussion: http://code.google.com/p/yii/issues/detail?id=2579 |
||||||
|
* |
||||||
|
* @param string $path the asset (file or directory) to be published |
||||||
|
* @param boolean $hashByName whether the published directory should be named as the hashed basename. |
||||||
|
* If false, the name will be the hash taken from dirname of the path being published and path mtime. |
||||||
|
* Defaults to false. Set true if the path being published is shared among |
||||||
|
* different extensions. |
||||||
|
* @param integer $level 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. |
||||||
|
* @param boolean $forceCopy whether we should copy the asset file or directory even if it is already published before. |
||||||
|
* This parameter is set true mainly during development stage when the original |
||||||
|
* assets are being constantly changed. The consequence is that the performance |
||||||
|
* is degraded, which is not a concern during development, however. |
||||||
|
* This parameter has been available since version 1.1.2. |
||||||
|
* @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,$forceCopy=false) |
||||||
|
{ |
||||||
|
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).filemtime($src)); |
||||||
|
$fileName=basename($src); |
||||||
|
$dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir; |
||||||
|
$dstFile=$dstDir.DIRECTORY_SEPARATOR.$fileName; |
||||||
|
|
||||||
|
if($this->linkAssets) |
||||||
|
{ |
||||||
|
if(!is_file($dstFile)) |
||||||
|
{ |
||||||
|
if(!is_dir($dstDir)) |
||||||
|
{ |
||||||
|
mkdir($dstDir); |
||||||
|
@chmod($dstDir, $this->newDirMode); |
||||||
|
} |
||||||
|
symlink($src,$dstFile); |
||||||
|
} |
||||||
|
} |
||||||
|
else if(@filemtime($dstFile)<@filemtime($src)) |
||||||
|
{ |
||||||
|
if(!is_dir($dstDir)) |
||||||
|
{ |
||||||
|
mkdir($dstDir); |
||||||
|
@chmod($dstDir, $this->newDirMode); |
||||||
|
} |
||||||
|
copy($src,$dstFile); |
||||||
|
@chmod($dstFile, $this->newFileMode); |
||||||
|
} |
||||||
|
|
||||||
|
return $this->_published[$path]=$this->getBaseUrl()."/$dir/$fileName"; |
||||||
|
} |
||||||
|
else if(is_dir($src)) |
||||||
|
{ |
||||||
|
$dir=$this->hash($hashByName ? basename($src) : $src.filemtime($src)); |
||||||
|
$dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir; |
||||||
|
|
||||||
|
if($this->linkAssets) |
||||||
|
{ |
||||||
|
if(!is_dir($dstDir)) |
||||||
|
symlink($src,$dstDir); |
||||||
|
} |
||||||
|
else if(!is_dir($dstDir) || $forceCopy) |
||||||
|
{ |
||||||
|
CFileHelper::copyDirectory($src,$dstDir,array( |
||||||
|
'exclude'=>$this->excludeFiles, |
||||||
|
'level'=>$level, |
||||||
|
'newDirMode'=>$this->newDirMode, |
||||||
|
'newFileMode'=>$this->newFileMode, |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
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))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the published path of a file path. |
||||||
|
* This method does not perform any publishing. It merely tells you |
||||||
|
* if the file or directory is published, where it will go. |
||||||
|
* @param string $path directory or file path being published |
||||||
|
* @param boolean $hashByName whether the published directory should be named as the hashed basename. |
||||||
|
* If false, the name will be the hash taken from dirname of the path being published and path mtime. |
||||||
|
* Defaults to false. Set true if the path being published is shared among |
||||||
|
* different extensions. |
||||||
|
* @return string the published file path. False if the file or directory does not exist |
||||||
|
*/ |
||||||
|
public function getPublishedPath($path,$hashByName=false) |
||||||
|
{ |
||||||
|
if(($path=realpath($path))!==false) |
||||||
|
{ |
||||||
|
$base=$this->getBasePath().DIRECTORY_SEPARATOR; |
||||||
|
if(is_file($path)) |
||||||
|
return $base . $this->hash($hashByName ? basename($path) : dirname($path).filemtime($path)) . DIRECTORY_SEPARATOR . basename($path); |
||||||
|
else |
||||||
|
return $base . $this->hash($hashByName ? basename($path) : $path.filemtime($path)); |
||||||
|
} |
||||||
|
else |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the URL of a published file path. |
||||||
|
* This method does not perform any publishing. It merely tells you |
||||||
|
* if the file path is published, what the URL will be to access it. |
||||||
|
* @param string $path directory or file path being published |
||||||
|
* @param boolean $hashByName whether the published directory should be named as the hashed basename. |
||||||
|
* If false, the name will be the hash taken from dirname of the path being published and path mtime. |
||||||
|
* Defaults to false. Set true if the path being published is shared among |
||||||
|
* different extensions. |
||||||
|
* @return string the published URL for the file or directory. False if the file or directory does not exist. |
||||||
|
*/ |
||||||
|
public function getPublishedUrl($path,$hashByName=false) |
||||||
|
{ |
||||||
|
if(isset($this->_published[$path])) |
||||||
|
return $this->_published[$path]; |
||||||
|
if(($path=realpath($path))!==false) |
||||||
|
{ |
||||||
|
if(is_file($path)) |
||||||
|
return $this->getBaseUrl().'/'.$this->hash($hashByName ? basename($path) : dirname($path).filemtime($path)).'/'.basename($path); |
||||||
|
else |
||||||
|
return $this->getBaseUrl().'/'.$this->hash($hashByName ? basename($path) : $path.filemtime($path)); |
||||||
|
} |
||||||
|
else |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generate a CRC32 hash for the directory path. Collisions are higher |
||||||
|
* than MD5 but generates a much smaller hash string. |
||||||
|
* @param string $path string to be hashed. |
||||||
|
* @return string hashed string. |
||||||
|
*/ |
||||||
|
protected function hash($path) |
||||||
|
{ |
||||||
|
return sprintf('%x',crc32($path.Yii::getVersion())); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Controller class file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\web; |
||||||
|
|
||||||
|
use yii\base\Action; |
||||||
|
use yii\base\Exception; |
||||||
|
use yii\base\HttpException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Controller is the base class of Web controllers. |
||||||
|
* |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Controller extends \yii\base\Controller |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Returns the request parameters that will be used for action parameter binding. |
||||||
|
* Default implementation simply returns an empty array. |
||||||
|
* Child classes may override this method to customize the parameters to be provided |
||||||
|
* for action parameter binding (e.g. `$_GET`). |
||||||
|
* @return array the request parameters (name-value pairs) to be used for action parameter binding |
||||||
|
*/ |
||||||
|
public function getActionParams() |
||||||
|
{ |
||||||
|
return $_GET; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is invoked when the request parameters do not satisfy the requirement of the specified action. |
||||||
|
* The default implementation will throw an exception. |
||||||
|
* @param Action $action the action being executed |
||||||
|
* @param Exception $exception the exception about the invalid parameters |
||||||
|
* @throws HttpException $exception a 400 HTTP exception |
||||||
|
*/ |
||||||
|
public function invalidActionParams($action, $exception) |
||||||
|
{ |
||||||
|
throw new HttpException(400, \Yii::t('yii', 'Your request is invalid.')); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CHttpCookie class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* A CHttpCookie instance stores a single cookie, including the cookie name, value, domain, path, expire, and secure. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CHttpCookie extends CComponent |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var string name of the cookie |
||||||
|
*/ |
||||||
|
public $name; |
||||||
|
/** |
||||||
|
* @var string value of the cookie |
||||||
|
*/ |
||||||
|
public $value=''; |
||||||
|
/** |
||||||
|
* @var string domain of the cookie |
||||||
|
*/ |
||||||
|
public $domain=''; |
||||||
|
/** |
||||||
|
* @var integer the timestamp at which the cookie expires. This is the server timestamp. Defaults to 0, meaning "until the browser is closed". |
||||||
|
*/ |
||||||
|
public $expire=0; |
||||||
|
/** |
||||||
|
* @var string the path on the server in which the cookie will be available on. The default is '/'. |
||||||
|
*/ |
||||||
|
public $path='/'; |
||||||
|
/** |
||||||
|
* @var boolean whether cookie should be sent via secure connection |
||||||
|
*/ |
||||||
|
public $secure=false; |
||||||
|
/** |
||||||
|
* @var boolean whether the cookie should be accessible only through the HTTP protocol. |
||||||
|
* By setting this property to true, the cookie will not be accessible by scripting languages, |
||||||
|
* such as JavaScript, which can effectly help to reduce identity theft through XSS attacks. |
||||||
|
* Note, this property is only effective for PHP 5.2.0 or above. |
||||||
|
*/ |
||||||
|
public $httpOnly=false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param string $name name of this cookie |
||||||
|
* @param string $value value of this cookie |
||||||
|
*/ |
||||||
|
public function __construct($name,$value) |
||||||
|
{ |
||||||
|
$this->name=$name; |
||||||
|
$this->value=$value; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,241 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CPagination class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* CPagination represents information relevant to pagination. |
||||||
|
* |
||||||
|
* When data needs to be rendered in multiple pages, we can use CPagination to |
||||||
|
* represent information such as {@link getItemCount total item count}, |
||||||
|
* {@link getPageSize page size}, {@link getCurrentPage current page}, etc. |
||||||
|
* These information can be passed to {@link CBasePager pagers} to render |
||||||
|
* pagination buttons or links. |
||||||
|
* |
||||||
|
* Example: |
||||||
|
* |
||||||
|
* Controller action: |
||||||
|
* <pre> |
||||||
|
* function actionIndex(){ |
||||||
|
* $criteria=new CDbCriteria(); |
||||||
|
* $count=Article::model()->count($criteria); |
||||||
|
* $pages=new CPagination($count); |
||||||
|
* |
||||||
|
* // results per page |
||||||
|
* $pages->pageSize=10; |
||||||
|
* $pages->applyLimit($criteria); |
||||||
|
* $models=Article::model()->findAll($criteria); |
||||||
|
* |
||||||
|
* $this->render('index', array( |
||||||
|
* 'models' => $models, |
||||||
|
* 'pages' => $pages |
||||||
|
* )); |
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* View: |
||||||
|
* <pre> |
||||||
|
* <?php foreach($models as $model): ?> |
||||||
|
* // display a model |
||||||
|
* <?php endforeach; ?> |
||||||
|
* |
||||||
|
* // display pagination |
||||||
|
* <?php $this->widget('CLinkPager', array( |
||||||
|
* 'pages' => $pages, |
||||||
|
* )) ?> |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @property integer $pageSize Number of items in each page. Defaults to 10. |
||||||
|
* @property integer $itemCount Total number of items. Defaults to 0. |
||||||
|
* @property integer $pageCount Number of pages. |
||||||
|
* @property integer $currentPage The zero-based index of the current page. Defaults to 0. |
||||||
|
* @property integer $offset The offset of the data. This may be used to set the |
||||||
|
* OFFSET value for a SQL statement for fetching the current page of data. |
||||||
|
* @property integer $limit The limit of the data. This may be used to set the |
||||||
|
* LIMIT value for a SQL statement for fetching the current page of data. |
||||||
|
* This returns the same value as {@link pageSize}. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CPagination extends CComponent |
||||||
|
{ |
||||||
|
/** |
||||||
|
* The default page size. |
||||||
|
*/ |
||||||
|
const DEFAULT_PAGE_SIZE=10; |
||||||
|
/** |
||||||
|
* @var string name of the GET variable storing the current page index. Defaults to 'page'. |
||||||
|
*/ |
||||||
|
public $pageVar='page'; |
||||||
|
/** |
||||||
|
* @var string the route (controller ID and action ID) for displaying the paged contents. |
||||||
|
* Defaults to empty string, meaning using the current route. |
||||||
|
*/ |
||||||
|
public $route=''; |
||||||
|
/** |
||||||
|
* @var array of parameters (name=>value) that should be used instead of GET when generating pagination URLs. |
||||||
|
* Defaults to null, meaning using the currently available GET parameters. |
||||||
|
*/ |
||||||
|
public $params; |
||||||
|
/** |
||||||
|
* @var boolean whether to ensure {@link currentPage} is returning a valid page number. |
||||||
|
* When this property is true, the value returned by {@link currentPage} will always be between |
||||||
|
* 0 and ({@link pageCount}-1). Because {@link pageCount} relies on the correct value of {@link itemCount}, |
||||||
|
* it means you must have knowledge about the total number of data items when you want to access {@link currentPage}. |
||||||
|
* This is fine for SQL-based queries, but may not be feasible for other kinds of queries (e.g. MongoDB). |
||||||
|
* In those cases, you may set this property to be false to skip the validation (you may need to validate yourself then). |
||||||
|
* Defaults to true. |
||||||
|
* @since 1.1.4 |
||||||
|
*/ |
||||||
|
public $validateCurrentPage=true; |
||||||
|
|
||||||
|
private $_pageSize=self::DEFAULT_PAGE_SIZE; |
||||||
|
private $_itemCount=0; |
||||||
|
private $_currentPage; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param integer $itemCount total number of items. |
||||||
|
*/ |
||||||
|
public function __construct($itemCount=0) |
||||||
|
{ |
||||||
|
$this->setItemCount($itemCount); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return integer number of items in each page. Defaults to 10. |
||||||
|
*/ |
||||||
|
public function getPageSize() |
||||||
|
{ |
||||||
|
return $this->_pageSize; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param integer $value number of items in each page |
||||||
|
*/ |
||||||
|
public function setPageSize($value) |
||||||
|
{ |
||||||
|
if(($this->_pageSize=$value)<=0) |
||||||
|
$this->_pageSize=self::DEFAULT_PAGE_SIZE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return integer total number of items. Defaults to 0. |
||||||
|
*/ |
||||||
|
public function getItemCount() |
||||||
|
{ |
||||||
|
return $this->_itemCount; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param integer $value total number of items. |
||||||
|
*/ |
||||||
|
public function setItemCount($value) |
||||||
|
{ |
||||||
|
if(($this->_itemCount=$value)<0) |
||||||
|
$this->_itemCount=0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return integer number of pages |
||||||
|
*/ |
||||||
|
public function getPageCount() |
||||||
|
{ |
||||||
|
return (int)(($this->_itemCount+$this->_pageSize-1)/$this->_pageSize); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param boolean $recalculate whether to recalculate the current page based on the page size and item count. |
||||||
|
* @return integer the zero-based index of the current page. Defaults to 0. |
||||||
|
*/ |
||||||
|
public function getCurrentPage($recalculate=true) |
||||||
|
{ |
||||||
|
if($this->_currentPage===null || $recalculate) |
||||||
|
{ |
||||||
|
if(isset($_GET[$this->pageVar])) |
||||||
|
{ |
||||||
|
$this->_currentPage=(int)$_GET[$this->pageVar]-1; |
||||||
|
if($this->validateCurrentPage) |
||||||
|
{ |
||||||
|
$pageCount=$this->getPageCount(); |
||||||
|
if($this->_currentPage>=$pageCount) |
||||||
|
$this->_currentPage=$pageCount-1; |
||||||
|
} |
||||||
|
if($this->_currentPage<0) |
||||||
|
$this->_currentPage=0; |
||||||
|
} |
||||||
|
else |
||||||
|
$this->_currentPage=0; |
||||||
|
} |
||||||
|
return $this->_currentPage; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param integer $value the zero-based index of the current page. |
||||||
|
*/ |
||||||
|
public function setCurrentPage($value) |
||||||
|
{ |
||||||
|
$this->_currentPage=$value; |
||||||
|
$_GET[$this->pageVar]=$value+1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the URL suitable for pagination. |
||||||
|
* This method is mainly called by pagers when creating URLs used to |
||||||
|
* perform pagination. The default implementation is to call |
||||||
|
* the controller's createUrl method with the page information. |
||||||
|
* You may override this method if your URL scheme is not the same as |
||||||
|
* the one supported by the controller's createUrl method. |
||||||
|
* @param CController $controller the controller that will create the actual URL |
||||||
|
* @param integer $page the page that the URL should point to. This is a zero-based index. |
||||||
|
* @return string the created URL |
||||||
|
*/ |
||||||
|
public function createPageUrl($controller,$page) |
||||||
|
{ |
||||||
|
$params=$this->params===null ? $_GET : $this->params; |
||||||
|
if($page>0) // page 0 is the default |
||||||
|
$params[$this->pageVar]=$page+1; |
||||||
|
else |
||||||
|
unset($params[$this->pageVar]); |
||||||
|
return $controller->createUrl($this->route,$params); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Applies LIMIT and OFFSET to the specified query criteria. |
||||||
|
* @param CDbCriteria $criteria the query criteria that should be applied with the limit |
||||||
|
*/ |
||||||
|
public function applyLimit($criteria) |
||||||
|
{ |
||||||
|
$criteria->limit=$this->getLimit(); |
||||||
|
$criteria->offset=$this->getOffset(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return integer the offset of the data. This may be used to set the |
||||||
|
* OFFSET value for a SQL statement for fetching the current page of data. |
||||||
|
* @since 1.1.0 |
||||||
|
*/ |
||||||
|
public function getOffset() |
||||||
|
{ |
||||||
|
return $this->getCurrentPage()*$this->getPageSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return integer the limit of the data. This may be used to set the |
||||||
|
* LIMIT value for a SQL statement for fetching the current page of data. |
||||||
|
* This returns the same value as {@link pageSize}. |
||||||
|
* @since 1.1.0 |
||||||
|
*/ |
||||||
|
public function getLimit() |
||||||
|
{ |
||||||
|
return $this->getPageSize(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,572 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CHttpSession class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* CHttpSession provides session-level data management and the related configurations. |
||||||
|
* |
||||||
|
* To start the session, call {@link open()}; To complete and send out session data, call {@link close()}; |
||||||
|
* To destroy the session, call {@link destroy()}. |
||||||
|
* |
||||||
|
* If {@link autoStart} is set true, the session will be started automatically |
||||||
|
* when the application component is initialized by the application. |
||||||
|
* |
||||||
|
* CHttpSession can be used like an array to set and get session data. For example, |
||||||
|
* <pre> |
||||||
|
* $session=new CHttpSession; |
||||||
|
* $session->open(); |
||||||
|
* $value1=$session['name1']; // get session variable 'name1' |
||||||
|
* $value2=$session['name2']; // get session variable 'name2' |
||||||
|
* foreach($session as $name=>$value) // traverse all session variables |
||||||
|
* $session['name3']=$value3; // set session variable 'name3' |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* The following configurations are available for session: |
||||||
|
* <ul> |
||||||
|
* <li>{@link setSessionID sessionID};</li> |
||||||
|
* <li>{@link setSessionName sessionName};</li> |
||||||
|
* <li>{@link autoStart};</li> |
||||||
|
* <li>{@link setSavePath savePath};</li> |
||||||
|
* <li>{@link setCookieParams cookieParams};</li> |
||||||
|
* <li>{@link setGCProbability gcProbability};</li> |
||||||
|
* <li>{@link setCookieMode cookieMode};</li> |
||||||
|
* <li>{@link setUseTransparentSessionID useTransparentSessionID};</li> |
||||||
|
* <li>{@link setTimeout timeout}.</li> |
||||||
|
* </ul> |
||||||
|
* See the corresponding setter and getter documentation for more information. |
||||||
|
* Note, these properties must be set before the session is started. |
||||||
|
* |
||||||
|
* CHttpSession can be extended to support customized session storage. |
||||||
|
* Override {@link openSession}, {@link closeSession}, {@link readSession}, |
||||||
|
* {@link writeSession}, {@link destroySession} and {@link gcSession} |
||||||
|
* and set {@link useCustomStorage} to true. |
||||||
|
* Then, the session data will be stored and retrieved using the above methods. |
||||||
|
* |
||||||
|
* CHttpSession is a Web application component that can be accessed via |
||||||
|
* {@link CWebApplication::getSession()}. |
||||||
|
* |
||||||
|
* @property boolean $useCustomStorage Whether to use custom storage. |
||||||
|
* @property boolean $isStarted Whether the session has started. |
||||||
|
* @property string $sessionID The current session ID. |
||||||
|
* @property string $sessionName The current session name. |
||||||
|
* @property string $savePath The current session save path, defaults to '/tmp'. |
||||||
|
* @property array $cookieParams The session cookie parameters. |
||||||
|
* @property string $cookieMode How to use cookie to store session ID. Defaults to 'Allow'. |
||||||
|
* @property integer $gCProbability The probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. |
||||||
|
* @property boolean $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to false. |
||||||
|
* @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds. |
||||||
|
* @property CHttpSessionIterator $iterator An iterator for traversing the session variables. |
||||||
|
* @property integer $count The number of session variables. |
||||||
|
* @property array $keys The list of session variable names. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CHttpSession extends CApplicationComponent implements IteratorAggregate,ArrayAccess,Countable |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var boolean whether the session should be automatically started when the session application component is initialized, defaults to true. |
||||||
|
*/ |
||||||
|
public $autoStart=true; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes the application component. |
||||||
|
* This method is required by IApplicationComponent and is invoked by application. |
||||||
|
*/ |
||||||
|
public function init() |
||||||
|
{ |
||||||
|
parent::init(); |
||||||
|
if($this->autoStart) |
||||||
|
$this->open(); |
||||||
|
register_shutdown_function(array($this,'close')); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a value indicating whether to use custom session storage. |
||||||
|
* This method should be overriden to return true if custom session storage handler should be used. |
||||||
|
* If returning true, make sure the methods {@link openSession}, {@link closeSession}, {@link readSession}, |
||||||
|
* {@link writeSession}, {@link destroySession}, and {@link gcSession} are overridden in child |
||||||
|
* class, because they will be used as the callback handlers. |
||||||
|
* The default implementation always return false. |
||||||
|
* @return boolean whether to use custom storage. |
||||||
|
*/ |
||||||
|
public function getUseCustomStorage() |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Starts the session if it has not started yet. |
||||||
|
*/ |
||||||
|
public function open() |
||||||
|
{ |
||||||
|
if($this->getUseCustomStorage()) |
||||||
|
@session_set_save_handler(array($this,'openSession'),array($this,'closeSession'),array($this,'readSession'),array($this,'writeSession'),array($this,'destroySession'),array($this,'gcSession')); |
||||||
|
|
||||||
|
@session_start(); |
||||||
|
if(YII_DEBUG && session_id()=='') |
||||||
|
{ |
||||||
|
$message=Yii::t('yii','Failed to start session.'); |
||||||
|
if(function_exists('error_get_last')) |
||||||
|
{ |
||||||
|
$error=error_get_last(); |
||||||
|
if(isset($error['message'])) |
||||||
|
$message=$error['message']; |
||||||
|
} |
||||||
|
Yii::log($message, CLogger::LEVEL_WARNING, 'system.web.CHttpSession'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Ends the current session and store session data. |
||||||
|
*/ |
||||||
|
public function close() |
||||||
|
{ |
||||||
|
if(session_id()!=='') |
||||||
|
@session_write_close(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Frees all session variables and destroys all data registered to a session. |
||||||
|
*/ |
||||||
|
public function destroy() |
||||||
|
{ |
||||||
|
if(session_id()!=='') |
||||||
|
{ |
||||||
|
@session_unset(); |
||||||
|
@session_destroy(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return boolean whether the session has started |
||||||
|
*/ |
||||||
|
public function getIsStarted() |
||||||
|
{ |
||||||
|
return session_id()!==''; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the current session ID |
||||||
|
*/ |
||||||
|
public function getSessionID() |
||||||
|
{ |
||||||
|
return session_id(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $value the session ID for the current session |
||||||
|
*/ |
||||||
|
public function setSessionID($value) |
||||||
|
{ |
||||||
|
session_id($value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the current session id with a newly generated one . |
||||||
|
* Please refer to {@link http://php.net/session_regenerate_id} for more details. |
||||||
|
* @param boolean $deleteOldSession Whether to delete the old associated session file or not. |
||||||
|
* @since 1.1.8 |
||||||
|
*/ |
||||||
|
public function regenerateID($deleteOldSession=false) |
||||||
|
{ |
||||||
|
session_regenerate_id($deleteOldSession); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the current session name |
||||||
|
*/ |
||||||
|
public function getSessionName() |
||||||
|
{ |
||||||
|
return session_name(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $value the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID |
||||||
|
*/ |
||||||
|
public function setSessionName($value) |
||||||
|
{ |
||||||
|
session_name($value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the current session save path, defaults to '/tmp'. |
||||||
|
*/ |
||||||
|
public function getSavePath() |
||||||
|
{ |
||||||
|
return session_save_path(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $value the current session save path |
||||||
|
* @throws CException if the path is not a valid directory |
||||||
|
*/ |
||||||
|
public function setSavePath($value) |
||||||
|
{ |
||||||
|
if(is_dir($value)) |
||||||
|
session_save_path($value); |
||||||
|
else |
||||||
|
throw new CException(Yii::t('yii','CHttpSession.savePath "{path}" is not a valid directory.', |
||||||
|
array('{path}'=>$value))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return array the session cookie parameters. |
||||||
|
* @see http://us2.php.net/manual/en/function.session-get-cookie-params.php |
||||||
|
*/ |
||||||
|
public function getCookieParams() |
||||||
|
{ |
||||||
|
return session_get_cookie_params(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the session cookie parameters. |
||||||
|
* The effect of this method only lasts for the duration of the script. |
||||||
|
* Call this method before the session starts. |
||||||
|
* @param array $value cookie parameters, valid keys include: lifetime, path, domain, secure. |
||||||
|
* @see http://us2.php.net/manual/en/function.session-set-cookie-params.php |
||||||
|
*/ |
||||||
|
public function setCookieParams($value) |
||||||
|
{ |
||||||
|
$data=session_get_cookie_params(); |
||||||
|
extract($data); |
||||||
|
extract($value); |
||||||
|
if(isset($httponly)) |
||||||
|
session_set_cookie_params($lifetime,$path,$domain,$secure,$httponly); |
||||||
|
else |
||||||
|
session_set_cookie_params($lifetime,$path,$domain,$secure); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string how to use cookie to store session ID. Defaults to 'Allow'. |
||||||
|
*/ |
||||||
|
public function getCookieMode() |
||||||
|
{ |
||||||
|
if(ini_get('session.use_cookies')==='0') |
||||||
|
return 'none'; |
||||||
|
else if(ini_get('session.use_only_cookies')==='0') |
||||||
|
return 'allow'; |
||||||
|
else |
||||||
|
return 'only'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $value how to use cookie to store session ID. Valid values include 'none', 'allow' and 'only'. |
||||||
|
*/ |
||||||
|
public function setCookieMode($value) |
||||||
|
{ |
||||||
|
if($value==='none') |
||||||
|
{ |
||||||
|
ini_set('session.use_cookies','0'); |
||||||
|
ini_set('session.use_only_cookies','0'); |
||||||
|
} |
||||||
|
else if($value==='allow') |
||||||
|
{ |
||||||
|
ini_set('session.use_cookies','1'); |
||||||
|
ini_set('session.use_only_cookies','0'); |
||||||
|
} |
||||||
|
else if($value==='only') |
||||||
|
{ |
||||||
|
ini_set('session.use_cookies','1'); |
||||||
|
ini_set('session.use_only_cookies','1'); |
||||||
|
} |
||||||
|
else |
||||||
|
throw new CException(Yii::t('yii','CHttpSession.cookieMode can only be "none", "allow" or "only".')); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. |
||||||
|
*/ |
||||||
|
public function getGCProbability() |
||||||
|
{ |
||||||
|
return (int)ini_get('session.gc_probability'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param integer $value the probability (percentage) that the gc (garbage collection) process is started on every session initialization. |
||||||
|
* @throws CException if the value is beyond [0,100] |
||||||
|
*/ |
||||||
|
public function setGCProbability($value) |
||||||
|
{ |
||||||
|
$value=(int)$value; |
||||||
|
if($value>=0 && $value<=100) |
||||||
|
{ |
||||||
|
ini_set('session.gc_probability',$value); |
||||||
|
ini_set('session.gc_divisor','100'); |
||||||
|
} |
||||||
|
else |
||||||
|
throw new CException(Yii::t('yii','CHttpSession.gcProbability "{value}" is invalid. It must be an integer between 0 and 100.', |
||||||
|
array('{value}'=>$value))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return boolean whether transparent sid support is enabled or not, defaults to false. |
||||||
|
*/ |
||||||
|
public function getUseTransparentSessionID() |
||||||
|
{ |
||||||
|
return ini_get('session.use_trans_sid')==1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param boolean $value whether transparent sid support is enabled or not. |
||||||
|
*/ |
||||||
|
public function setUseTransparentSessionID($value) |
||||||
|
{ |
||||||
|
ini_set('session.use_trans_sid',$value?'1':'0'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds. |
||||||
|
*/ |
||||||
|
public function getTimeout() |
||||||
|
{ |
||||||
|
return (int)ini_get('session.gc_maxlifetime'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param integer $value the number of seconds after which data will be seen as 'garbage' and cleaned up |
||||||
|
*/ |
||||||
|
public function setTimeout($value) |
||||||
|
{ |
||||||
|
ini_set('session.gc_maxlifetime',$value); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Session open handler. |
||||||
|
* This method should be overridden if {@link useCustomStorage} is set true. |
||||||
|
* Do not call this method directly. |
||||||
|
* @param string $savePath session save path |
||||||
|
* @param string $sessionName session name |
||||||
|
* @return boolean whether session is opened successfully |
||||||
|
*/ |
||||||
|
public function openSession($savePath,$sessionName) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Session close handler. |
||||||
|
* This method should be overridden if {@link useCustomStorage} is set true. |
||||||
|
* Do not call this method directly. |
||||||
|
* @return boolean whether session is closed successfully |
||||||
|
*/ |
||||||
|
public function closeSession() |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Session read handler. |
||||||
|
* This method should be overridden if {@link useCustomStorage} is set true. |
||||||
|
* Do not call this method directly. |
||||||
|
* @param string $id session ID |
||||||
|
* @return string the session data |
||||||
|
*/ |
||||||
|
public function readSession($id) |
||||||
|
{ |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Session write handler. |
||||||
|
* This method should be overridden if {@link useCustomStorage} is set true. |
||||||
|
* Do not call this method directly. |
||||||
|
* @param string $id session ID |
||||||
|
* @param string $data session data |
||||||
|
* @return boolean whether session write is successful |
||||||
|
*/ |
||||||
|
public function writeSession($id,$data) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Session destroy handler. |
||||||
|
* This method should be overridden if {@link useCustomStorage} is set true. |
||||||
|
* Do not call this method directly. |
||||||
|
* @param string $id session ID |
||||||
|
* @return boolean whether session is destroyed successfully |
||||||
|
*/ |
||||||
|
public function destroySession($id) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Session GC (garbage collection) handler. |
||||||
|
* This method should be overridden if {@link useCustomStorage} is set true. |
||||||
|
* Do not call this method directly. |
||||||
|
* @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. |
||||||
|
* @return boolean whether session is GCed successfully |
||||||
|
*/ |
||||||
|
public function gcSession($maxLifetime) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
//------ The following methods enable CHttpSession to be CMap-like ----- |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an iterator for traversing the session variables. |
||||||
|
* This method is required by the interface IteratorAggregate. |
||||||
|
* @return CHttpSessionIterator an iterator for traversing the session variables. |
||||||
|
*/ |
||||||
|
public function getIterator() |
||||||
|
{ |
||||||
|
return new CHttpSessionIterator; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of items in the session. |
||||||
|
* @return integer the number of session variables |
||||||
|
*/ |
||||||
|
public function getCount() |
||||||
|
{ |
||||||
|
return count($_SESSION); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of items in the session. |
||||||
|
* This method is required by Countable interface. |
||||||
|
* @return integer number of items in the session. |
||||||
|
*/ |
||||||
|
public function count() |
||||||
|
{ |
||||||
|
return $this->getCount(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return array the list of session variable names |
||||||
|
*/ |
||||||
|
public function getKeys() |
||||||
|
{ |
||||||
|
return array_keys($_SESSION); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the session variable value with the session variable name. |
||||||
|
* This method is very similar to {@link itemAt} and {@link offsetGet}, |
||||||
|
* except that it will return $defaultValue if the session variable does not exist. |
||||||
|
* @param mixed $key the session variable name |
||||||
|
* @param mixed $defaultValue the default value to be returned when the session variable does not exist. |
||||||
|
* @return mixed the session variable value, or $defaultValue if the session variable does not exist. |
||||||
|
* @since 1.1.2 |
||||||
|
*/ |
||||||
|
public function get($key,$defaultValue=null) |
||||||
|
{ |
||||||
|
return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the session variable value with the session variable name. |
||||||
|
* This method is exactly the same as {@link offsetGet}. |
||||||
|
* @param mixed $key the session variable name |
||||||
|
* @return mixed the session variable value, null if no such variable exists |
||||||
|
*/ |
||||||
|
public function itemAt($key) |
||||||
|
{ |
||||||
|
return isset($_SESSION[$key]) ? $_SESSION[$key] : null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a session variable. |
||||||
|
* Note, if the specified name already exists, the old value will be removed first. |
||||||
|
* @param mixed $key session variable name |
||||||
|
* @param mixed $value session variable value |
||||||
|
*/ |
||||||
|
public function add($key,$value) |
||||||
|
{ |
||||||
|
$_SESSION[$key]=$value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes a session variable. |
||||||
|
* @param mixed $key the name of the session variable to be removed |
||||||
|
* @return mixed the removed value, null if no such session variable. |
||||||
|
*/ |
||||||
|
public function remove($key) |
||||||
|
{ |
||||||
|
if(isset($_SESSION[$key])) |
||||||
|
{ |
||||||
|
$value=$_SESSION[$key]; |
||||||
|
unset($_SESSION[$key]); |
||||||
|
return $value; |
||||||
|
} |
||||||
|
else |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes all session variables |
||||||
|
*/ |
||||||
|
public function clear() |
||||||
|
{ |
||||||
|
foreach(array_keys($_SESSION) as $key) |
||||||
|
unset($_SESSION[$key]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param mixed $key session variable name |
||||||
|
* @return boolean whether there is the named session variable |
||||||
|
*/ |
||||||
|
public function contains($key) |
||||||
|
{ |
||||||
|
return isset($_SESSION[$key]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return array the list of all session variables in array |
||||||
|
*/ |
||||||
|
public function toArray() |
||||||
|
{ |
||||||
|
return $_SESSION; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is required by the interface ArrayAccess. |
||||||
|
* @param mixed $offset the offset to check on |
||||||
|
* @return boolean |
||||||
|
*/ |
||||||
|
public function offsetExists($offset) |
||||||
|
{ |
||||||
|
return isset($_SESSION[$offset]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is required by the interface ArrayAccess. |
||||||
|
* @param integer $offset the offset to retrieve element. |
||||||
|
* @return mixed the element at the offset, null if no element is found at the offset |
||||||
|
*/ |
||||||
|
public function offsetGet($offset) |
||||||
|
{ |
||||||
|
return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is required by the interface ArrayAccess. |
||||||
|
* @param integer $offset the offset to set element |
||||||
|
* @param mixed $item the element value |
||||||
|
*/ |
||||||
|
public function offsetSet($offset,$item) |
||||||
|
{ |
||||||
|
$_SESSION[$offset]=$item; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is required by the interface ArrayAccess. |
||||||
|
* @param mixed $offset the offset to unset element |
||||||
|
*/ |
||||||
|
public function offsetUnset($offset) |
||||||
|
{ |
||||||
|
unset($_SESSION[$offset]); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,459 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CSort class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* CSort represents information relevant to sorting. |
||||||
|
* |
||||||
|
* When data needs to be sorted according to one or several attributes, |
||||||
|
* we can use CSort to represent the sorting information and generate |
||||||
|
* appropriate hyperlinks that can lead to sort actions. |
||||||
|
* |
||||||
|
* CSort is designed to be used together with {@link CActiveRecord}. |
||||||
|
* When creating a CSort instance, you need to specify {@link modelClass}. |
||||||
|
* You can use CSort to generate hyperlinks by calling {@link link}. |
||||||
|
* You can also use CSort to modify a {@link CDbCriteria} instance by calling {@link applyOrder} so that |
||||||
|
* it can cause the query results to be sorted according to the specified |
||||||
|
* attributes. |
||||||
|
* |
||||||
|
* In order to prevent SQL injection attacks, CSort ensures that only valid model attributes |
||||||
|
* can be sorted. This is determined based on {@link modelClass} and {@link attributes}. |
||||||
|
* When {@link attributes} is not set, all attributes belonging to {@link modelClass} |
||||||
|
* can be sorted. When {@link attributes} is set, only those attributes declared in the property |
||||||
|
* can be sorted. |
||||||
|
* |
||||||
|
* By configuring {@link attributes}, one can perform more complex sorts that may |
||||||
|
* consist of things like compound attributes (e.g. sort based on the combination of |
||||||
|
* first name and last name of users). |
||||||
|
* |
||||||
|
* The property {@link attributes} should be an array of key-value pairs, where the keys |
||||||
|
* represent the attribute names, while the values represent the virtual attribute definitions. |
||||||
|
* For more details, please check the documentation about {@link attributes}. |
||||||
|
* |
||||||
|
* @property string $orderBy The order-by columns represented by this sort object. |
||||||
|
* This can be put in the ORDER BY clause of a SQL statement. |
||||||
|
* @property array $directions Sort directions indexed by attribute names. |
||||||
|
* The sort direction. Can be either CSort::SORT_ASC for ascending order or |
||||||
|
* CSort::SORT_DESC for descending order. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
*/ |
||||||
|
class CSort extends CComponent |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Sort ascending |
||||||
|
* @since 1.1.10 |
||||||
|
*/ |
||||||
|
const SORT_ASC = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Sort descending |
||||||
|
* @since 1.1.10 |
||||||
|
*/ |
||||||
|
const SORT_DESC = true; |
||||||
|
|
||||||
|
/** |
||||||
|
* @var boolean whether the sorting can be applied to multiple attributes simultaneously. |
||||||
|
* Defaults to false, which means each time the data can only be sorted by one attribute. |
||||||
|
*/ |
||||||
|
public $multiSort=false; |
||||||
|
/** |
||||||
|
* @var string the name of the model class whose attributes can be sorted. |
||||||
|
* The model class must be a child class of {@link CActiveRecord}. |
||||||
|
*/ |
||||||
|
public $modelClass; |
||||||
|
/** |
||||||
|
* @var array list of attributes that are allowed to be sorted. |
||||||
|
* For example, array('user_id','create_time') would specify that only 'user_id' |
||||||
|
* and 'create_time' of the model {@link modelClass} can be sorted. |
||||||
|
* By default, this property is an empty array, which means all attributes in |
||||||
|
* {@link modelClass} are allowed to be sorted. |
||||||
|
* |
||||||
|
* This property can also be used to specify complex sorting. To do so, |
||||||
|
* a virtual attribute can be declared in terms of a key-value pair in the array. |
||||||
|
* The key refers to the name of the virtual attribute that may appear in the sort request, |
||||||
|
* while the value specifies the definition of the virtual attribute. |
||||||
|
* |
||||||
|
* In the simple case, a key-value pair can be like <code>'user'=>'user_id'</code> |
||||||
|
* where 'user' is the name of the virtual attribute while 'user_id' means the virtual |
||||||
|
* attribute is the 'user_id' attribute in the {@link modelClass}. |
||||||
|
* |
||||||
|
* A more flexible way is to specify the key-value pair as |
||||||
|
* <pre> |
||||||
|
* 'user'=>array( |
||||||
|
* 'asc'=>'first_name, last_name', |
||||||
|
* 'desc'=>'first_name DESC, last_name DESC', |
||||||
|
* 'label'=>'Name' |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* where 'user' is the name of the virtual attribute that specifies the full name of user |
||||||
|
* (a compound attribute consisting of first name and last name of user). In this case, |
||||||
|
* we have to use an array to define the virtual attribute with three elements: 'asc', |
||||||
|
* 'desc' and 'label'. |
||||||
|
* |
||||||
|
* The above approach can also be used to declare virtual attributes that consist of relational |
||||||
|
* attributes. For example, |
||||||
|
* <pre> |
||||||
|
* 'price'=>array( |
||||||
|
* 'asc'=>'item.price', |
||||||
|
* 'desc'=>'item.price DESC', |
||||||
|
* 'label'=>'Item Price' |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* Note, the attribute name should not contain '-' or '.' characters because |
||||||
|
* they are used as {@link separators}. |
||||||
|
* |
||||||
|
* Starting from version 1.1.3, an additional option named 'default' can be used in the virtual attribute |
||||||
|
* declaration. This option specifies whether an attribute should be sorted in ascending or descending |
||||||
|
* order upon user clicking the corresponding sort hyperlink if it is not currently sorted. The valid |
||||||
|
* option values include 'asc' (default) and 'desc'. For example, |
||||||
|
* <pre> |
||||||
|
* 'price'=>array( |
||||||
|
* 'asc'=>'item.price', |
||||||
|
* 'desc'=>'item.price DESC', |
||||||
|
* 'label'=>'Item Price', |
||||||
|
* 'default'=>'desc', |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* Also starting from version 1.1.3, you can include a star ('*') element in this property so that |
||||||
|
* all model attributes are available for sorting, in addition to those virtual attributes. For example, |
||||||
|
* <pre> |
||||||
|
* 'attributes'=>array( |
||||||
|
* 'price'=>array( |
||||||
|
* 'asc'=>'item.price', |
||||||
|
* 'desc'=>'item.price DESC', |
||||||
|
* 'label'=>'Item Price', |
||||||
|
* 'default'=>'desc', |
||||||
|
* ), |
||||||
|
* '*', |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* Note that when a name appears as both a model attribute and a virtual attribute, the position of |
||||||
|
* the star element in the array determines which one takes precedence. In particular, if the star |
||||||
|
* element is the first element in the array, the model attribute takes precedence; and if the star |
||||||
|
* element is the last one, the virtual attribute takes precedence. |
||||||
|
*/ |
||||||
|
public $attributes=array(); |
||||||
|
/** |
||||||
|
* @var string the name of the GET parameter that specifies which attributes to be sorted |
||||||
|
* in which direction. Defaults to 'sort'. |
||||||
|
*/ |
||||||
|
public $sortVar='sort'; |
||||||
|
/** |
||||||
|
* @var string the tag appeared in the GET parameter that indicates the attribute should be sorted |
||||||
|
* in descending order. Defaults to 'desc'. |
||||||
|
*/ |
||||||
|
public $descTag='desc'; |
||||||
|
/** |
||||||
|
* @var mixed the default order that should be applied to the query criteria when |
||||||
|
* the current request does not specify any sort. For example, 'name, create_time DESC' or |
||||||
|
* 'UPPER(name)'. |
||||||
|
* |
||||||
|
* Starting from version 1.1.3, you can also specify the default order using an array. |
||||||
|
* The array keys could be attribute names or virtual attribute names as declared in {@link attributes}, |
||||||
|
* and the array values indicate whether the sorting of the corresponding attributes should |
||||||
|
* be in descending order. For example, |
||||||
|
* <pre> |
||||||
|
* 'defaultOrder'=>array( |
||||||
|
* 'price'=>CSort::SORT_DESC, |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* `SORT_DESC` and `SORT_ASC` are available since 1.1.10. In earlier Yii versions you should use |
||||||
|
* `true` and `false` respectively. |
||||||
|
* |
||||||
|
* Please note when using array to specify the default order, the corresponding attributes |
||||||
|
* will be put into {@link directions} and thus affect how the sort links are rendered |
||||||
|
* (e.g. an arrow may be displayed next to the currently active sort link). |
||||||
|
*/ |
||||||
|
public $defaultOrder; |
||||||
|
/** |
||||||
|
* @var string the route (controller ID and action ID) for generating the sorted contents. |
||||||
|
* Defaults to empty string, meaning using the currently requested route. |
||||||
|
*/ |
||||||
|
public $route=''; |
||||||
|
/** |
||||||
|
* @var array separators used in the generated URL. This must be an array consisting of |
||||||
|
* two elements. The first element specifies the character separating different |
||||||
|
* attributes, while the second element specifies the character separating attribute name |
||||||
|
* and the corresponding sort direction. Defaults to array('-','.'). |
||||||
|
*/ |
||||||
|
public $separators=array('-','.'); |
||||||
|
/** |
||||||
|
* @var array the additional GET parameters (name=>value) that should be used when generating sort URLs. |
||||||
|
* Defaults to null, meaning using the currently available GET parameters. |
||||||
|
*/ |
||||||
|
public $params; |
||||||
|
|
||||||
|
private $_directions; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param string $modelClass the class name of data models that need to be sorted. |
||||||
|
* This should be a child class of {@link CActiveRecord}. |
||||||
|
*/ |
||||||
|
public function __construct($modelClass=null) |
||||||
|
{ |
||||||
|
$this->modelClass=$modelClass; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Modifies the query criteria by changing its {@link CDbCriteria::order} property. |
||||||
|
* This method will use {@link directions} to determine which columns need to be sorted. |
||||||
|
* They will be put in the ORDER BY clause. If the criteria already has non-empty {@link CDbCriteria::order} value, |
||||||
|
* the new value will be appended to it. |
||||||
|
* @param CDbCriteria $criteria the query criteria |
||||||
|
*/ |
||||||
|
public function applyOrder($criteria) |
||||||
|
{ |
||||||
|
$order=$this->getOrderBy(); |
||||||
|
if(!empty($order)) |
||||||
|
{ |
||||||
|
if(!empty($criteria->order)) |
||||||
|
$criteria->order.=', '; |
||||||
|
$criteria->order.=$order; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the order-by columns represented by this sort object. |
||||||
|
* This can be put in the ORDER BY clause of a SQL statement. |
||||||
|
* @since 1.1.0 |
||||||
|
*/ |
||||||
|
public function getOrderBy() |
||||||
|
{ |
||||||
|
$directions=$this->getDirections(); |
||||||
|
if(empty($directions)) |
||||||
|
return is_string($this->defaultOrder) ? $this->defaultOrder : ''; |
||||||
|
else |
||||||
|
{ |
||||||
|
if($this->modelClass!==null) |
||||||
|
$schema=CActiveRecord::model($this->modelClass)->getDbConnection()->getSchema(); |
||||||
|
$orders=array(); |
||||||
|
foreach($directions as $attribute=>$descending) |
||||||
|
{ |
||||||
|
$definition=$this->resolveAttribute($attribute); |
||||||
|
if(is_array($definition)) |
||||||
|
{ |
||||||
|
if($descending) |
||||||
|
$orders[]=isset($definition['desc']) ? $definition['desc'] : $attribute.' DESC'; |
||||||
|
else |
||||||
|
$orders[]=isset($definition['asc']) ? $definition['asc'] : $attribute; |
||||||
|
} |
||||||
|
else if($definition!==false) |
||||||
|
{ |
||||||
|
$attribute=$definition; |
||||||
|
if(isset($schema)) |
||||||
|
{ |
||||||
|
if(($pos=strpos($attribute,'.'))!==false) |
||||||
|
$attribute=$schema->quoteTableName(substr($attribute,0,$pos)).'.'.$schema->quoteColumnName(substr($attribute,$pos+1)); |
||||||
|
else |
||||||
|
$attribute=CActiveRecord::model($this->modelClass)->getTableAlias(true).'.'.$schema->quoteColumnName($attribute); |
||||||
|
} |
||||||
|
$orders[]=$descending?$attribute.' DESC':$attribute; |
||||||
|
} |
||||||
|
} |
||||||
|
return implode(', ',$orders); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a hyperlink that can be clicked to cause sorting. |
||||||
|
* @param string $attribute the attribute name. This must be the actual attribute name, not alias. |
||||||
|
* If it is an attribute of a related AR object, the name should be prefixed with |
||||||
|
* the relation name (e.g. 'author.name', where 'author' is the relation name). |
||||||
|
* @param string $label the link label. If null, the label will be determined according |
||||||
|
* to the attribute (see {@link resolveLabel}). |
||||||
|
* @param array $htmlOptions additional HTML attributes for the hyperlink tag |
||||||
|
* @return string the generated hyperlink |
||||||
|
*/ |
||||||
|
public function link($attribute,$label=null,$htmlOptions=array()) |
||||||
|
{ |
||||||
|
if($label===null) |
||||||
|
$label=$this->resolveLabel($attribute); |
||||||
|
if(($definition=$this->resolveAttribute($attribute))===false) |
||||||
|
return $label; |
||||||
|
$directions=$this->getDirections(); |
||||||
|
if(isset($directions[$attribute])) |
||||||
|
{ |
||||||
|
$class=$directions[$attribute] ? 'desc' : 'asc'; |
||||||
|
if(isset($htmlOptions['class'])) |
||||||
|
$htmlOptions['class'].=' '.$class; |
||||||
|
else |
||||||
|
$htmlOptions['class']=$class; |
||||||
|
$descending=!$directions[$attribute]; |
||||||
|
unset($directions[$attribute]); |
||||||
|
} |
||||||
|
else if(is_array($definition) && isset($definition['default'])) |
||||||
|
$descending=$definition['default']==='desc'; |
||||||
|
else |
||||||
|
$descending=false; |
||||||
|
|
||||||
|
if($this->multiSort) |
||||||
|
$directions=array_merge(array($attribute=>$descending),$directions); |
||||||
|
else |
||||||
|
$directions=array($attribute=>$descending); |
||||||
|
|
||||||
|
$url=$this->createUrl(Yii::app()->getController(),$directions); |
||||||
|
|
||||||
|
return $this->createLink($attribute,$label,$url,$htmlOptions); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resolves the attribute label for the specified attribute. |
||||||
|
* This will invoke {@link CActiveRecord::getAttributeLabel} to determine what label to use. |
||||||
|
* If the attribute refers to a virtual attribute declared in {@link attributes}, |
||||||
|
* then the label given in the {@link attributes} will be returned instead. |
||||||
|
* @param string $attribute the attribute name. |
||||||
|
* @return string the attribute label |
||||||
|
*/ |
||||||
|
public function resolveLabel($attribute) |
||||||
|
{ |
||||||
|
$definition=$this->resolveAttribute($attribute); |
||||||
|
if(is_array($definition)) |
||||||
|
{ |
||||||
|
if(isset($definition['label'])) |
||||||
|
return $definition['label']; |
||||||
|
} |
||||||
|
else if(is_string($definition)) |
||||||
|
$attribute=$definition; |
||||||
|
if($this->modelClass!==null) |
||||||
|
return CActiveRecord::model($this->modelClass)->getAttributeLabel($attribute); |
||||||
|
else |
||||||
|
return $attribute; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the currently requested sort information. |
||||||
|
* @return array sort directions indexed by attribute names. |
||||||
|
* Sort direction can be either CSort::SORT_ASC for ascending order or |
||||||
|
* CSort::SORT_DESC for descending order. |
||||||
|
*/ |
||||||
|
public function getDirections() |
||||||
|
{ |
||||||
|
if($this->_directions===null) |
||||||
|
{ |
||||||
|
$this->_directions=array(); |
||||||
|
if(isset($_GET[$this->sortVar]) && is_string($_GET[$this->sortVar])) |
||||||
|
{ |
||||||
|
$attributes=explode($this->separators[0],$_GET[$this->sortVar]); |
||||||
|
foreach($attributes as $attribute) |
||||||
|
{ |
||||||
|
if(($pos=strrpos($attribute,$this->separators[1]))!==false) |
||||||
|
{ |
||||||
|
$descending=substr($attribute,$pos+1)===$this->descTag; |
||||||
|
if($descending) |
||||||
|
$attribute=substr($attribute,0,$pos); |
||||||
|
} |
||||||
|
else |
||||||
|
$descending=false; |
||||||
|
|
||||||
|
if(($this->resolveAttribute($attribute))!==false) |
||||||
|
{ |
||||||
|
$this->_directions[$attribute]=$descending; |
||||||
|
if(!$this->multiSort) |
||||||
|
return $this->_directions; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if($this->_directions===array() && is_array($this->defaultOrder)) |
||||||
|
$this->_directions=$this->defaultOrder; |
||||||
|
} |
||||||
|
return $this->_directions; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the sort direction of the specified attribute in the current request. |
||||||
|
* @param string $attribute the attribute name |
||||||
|
* @return mixed Sort direction of the attribute. Can be either CSort::SORT_ASC |
||||||
|
* for ascending order or CSort::SORT_DESC for descending order. Value is null |
||||||
|
* if the attribute doesn't need to be sorted. |
||||||
|
*/ |
||||||
|
public function getDirection($attribute) |
||||||
|
{ |
||||||
|
$this->getDirections(); |
||||||
|
return isset($this->_directions[$attribute]) ? $this->_directions[$attribute] : null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a URL that can lead to generating sorted data. |
||||||
|
* @param CController $controller the controller that will be used to create the URL. |
||||||
|
* @param array $directions the sort directions indexed by attribute names. |
||||||
|
* The sort direction can be either CSort::SORT_ASC for ascending order or |
||||||
|
* CSort::SORT_DESC for descending order. |
||||||
|
* @return string the URL for sorting |
||||||
|
*/ |
||||||
|
public function createUrl($controller,$directions) |
||||||
|
{ |
||||||
|
$sorts=array(); |
||||||
|
foreach($directions as $attribute=>$descending) |
||||||
|
$sorts[]=$descending ? $attribute.$this->separators[1].$this->descTag : $attribute; |
||||||
|
$params=$this->params===null ? $_GET : $this->params; |
||||||
|
$params[$this->sortVar]=implode($this->separators[0],$sorts); |
||||||
|
return $controller->createUrl($this->route,$params); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the real definition of an attribute given its name. |
||||||
|
* |
||||||
|
* The resolution is based on {@link attributes} and {@link CActiveRecord::attributeNames}. |
||||||
|
* <ul> |
||||||
|
* <li>When {@link attributes} is an empty array, if the name refers to an attribute of {@link modelClass}, |
||||||
|
* then the name is returned back.</li> |
||||||
|
* <li>When {@link attributes} is not empty, if the name refers to an attribute declared in {@link attributes}, |
||||||
|
* then the corresponding virtual attribute definition is returned. Starting from version 1.1.3, if {@link attributes} |
||||||
|
* contains a star ('*') element, the name will also be used to match against all model attributes.</li> |
||||||
|
* <li>In all other cases, false is returned, meaning the name does not refer to a valid attribute.</li> |
||||||
|
* </ul> |
||||||
|
* @param string $attribute the attribute name that the user requests to sort on |
||||||
|
* @return mixed the attribute name or the virtual attribute definition. False if the attribute cannot be sorted. |
||||||
|
*/ |
||||||
|
public function resolveAttribute($attribute) |
||||||
|
{ |
||||||
|
if($this->attributes!==array()) |
||||||
|
$attributes=$this->attributes; |
||||||
|
else if($this->modelClass!==null) |
||||||
|
$attributes=CActiveRecord::model($this->modelClass)->attributeNames(); |
||||||
|
else |
||||||
|
return false; |
||||||
|
foreach($attributes as $name=>$definition) |
||||||
|
{ |
||||||
|
if(is_string($name)) |
||||||
|
{ |
||||||
|
if($name===$attribute) |
||||||
|
return $definition; |
||||||
|
} |
||||||
|
else if($definition==='*') |
||||||
|
{ |
||||||
|
if($this->modelClass!==null && CActiveRecord::model($this->modelClass)->hasAttribute($attribute)) |
||||||
|
return $attribute; |
||||||
|
} |
||||||
|
else if($definition===$attribute) |
||||||
|
return $attribute; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a hyperlink based on the given label and URL. |
||||||
|
* You may override this method to customize the link generation. |
||||||
|
* @param string $attribute the name of the attribute that this link is for |
||||||
|
* @param string $label the label of the hyperlink |
||||||
|
* @param string $url the URL |
||||||
|
* @param array $htmlOptions additional HTML options |
||||||
|
* @return string the generated hyperlink |
||||||
|
*/ |
||||||
|
protected function createLink($attribute,$label,$url,$htmlOptions) |
||||||
|
{ |
||||||
|
return CHtml::link($label,$url,$htmlOptions); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CTheme class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* CTheme represents an application theme. |
||||||
|
* |
||||||
|
* @property string $name Theme name. |
||||||
|
* @property string $baseUrl The relative URL to the theme folder (without ending slash). |
||||||
|
* @property string $basePath The file path to the theme folder. |
||||||
|
* @property string $viewPath The path for controller views. Defaults to 'ThemeRoot/views'. |
||||||
|
* @property string $systemViewPath The path for system views. Defaults to 'ThemeRoot/views/system'. |
||||||
|
* @property string $skinPath The path for widget skins. Defaults to 'ThemeRoot/views/skins'. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CTheme extends CComponent |
||||||
|
{ |
||||||
|
private $_name; |
||||||
|
private $_basePath; |
||||||
|
private $_baseUrl; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param string $name name of the theme |
||||||
|
* @param string $basePath base theme path |
||||||
|
* @param string $baseUrl base theme URL |
||||||
|
*/ |
||||||
|
public function __construct($name,$basePath,$baseUrl) |
||||||
|
{ |
||||||
|
$this->_name=$name; |
||||||
|
$this->_baseUrl=$baseUrl; |
||||||
|
$this->_basePath=$basePath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string theme name |
||||||
|
*/ |
||||||
|
public function getName() |
||||||
|
{ |
||||||
|
return $this->_name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the relative URL to the theme folder (without ending slash) |
||||||
|
*/ |
||||||
|
public function getBaseUrl() |
||||||
|
{ |
||||||
|
return $this->_baseUrl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the file path to the theme folder |
||||||
|
*/ |
||||||
|
public function getBasePath() |
||||||
|
{ |
||||||
|
return $this->_basePath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the path for controller views. Defaults to 'ThemeRoot/views'. |
||||||
|
*/ |
||||||
|
public function getViewPath() |
||||||
|
{ |
||||||
|
return $this->_basePath.DIRECTORY_SEPARATOR.'views'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the path for system views. Defaults to 'ThemeRoot/views/system'. |
||||||
|
*/ |
||||||
|
public function getSystemViewPath() |
||||||
|
{ |
||||||
|
return $this->getViewPath().DIRECTORY_SEPARATOR.'system'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the path for widget skins. Defaults to 'ThemeRoot/views/skins'. |
||||||
|
* @since 1.1 |
||||||
|
*/ |
||||||
|
public function getSkinPath() |
||||||
|
{ |
||||||
|
return $this->getViewPath().DIRECTORY_SEPARATOR.'skins'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finds the view file for the specified controller's view. |
||||||
|
* @param CController $controller the controller |
||||||
|
* @param string $viewName the view name |
||||||
|
* @return string the view file path. False if the file does not exist. |
||||||
|
*/ |
||||||
|
public function getViewFile($controller,$viewName) |
||||||
|
{ |
||||||
|
$moduleViewPath=$this->getViewPath(); |
||||||
|
if(($module=$controller->getModule())!==null) |
||||||
|
$moduleViewPath.='/'.$module->getId(); |
||||||
|
return $controller->resolveViewFile($viewName,$this->getViewPath().'/'.$controller->getUniqueId(),$this->getViewPath(),$moduleViewPath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finds the layout file for the specified controller's layout. |
||||||
|
* @param CController $controller the controller |
||||||
|
* @param string $layoutName the layout name |
||||||
|
* @return string the layout file path. False if the file does not exist. |
||||||
|
*/ |
||||||
|
public function getLayoutFile($controller,$layoutName) |
||||||
|
{ |
||||||
|
$moduleViewPath=$basePath=$this->getViewPath(); |
||||||
|
$module=$controller->getModule(); |
||||||
|
if(empty($layoutName)) |
||||||
|
{ |
||||||
|
while($module!==null) |
||||||
|
{ |
||||||
|
if($module->layout===false) |
||||||
|
return false; |
||||||
|
if(!empty($module->layout)) |
||||||
|
break; |
||||||
|
$module=$module->getParentModule(); |
||||||
|
} |
||||||
|
if($module===null) |
||||||
|
$layoutName=Yii::app()->layout; |
||||||
|
else |
||||||
|
{ |
||||||
|
$layoutName=$module->layout; |
||||||
|
$moduleViewPath.='/'.$module->getId(); |
||||||
|
} |
||||||
|
} |
||||||
|
else if($module!==null) |
||||||
|
$moduleViewPath.='/'.$module->getId(); |
||||||
|
|
||||||
|
return $controller->resolveViewFile($layoutName,$moduleViewPath.'/layouts',$basePath,$moduleViewPath); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CThemeManager class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* CThemeManager manages the themes for the Web application. |
||||||
|
* |
||||||
|
* A theme is a collection of view/layout files and resource files |
||||||
|
* (e.g. css, image, js files). When a theme is active, {@link CController} |
||||||
|
* will look for the specified view/layout under the theme folder first. |
||||||
|
* The corresponding view/layout files will be used if the theme provides them. |
||||||
|
* Otherwise, the default view/layout files will be used. |
||||||
|
* |
||||||
|
* By default, each theme is organized as a directory whose name is the theme name. |
||||||
|
* All themes are located under the "WebRootPath/themes" directory. |
||||||
|
* |
||||||
|
* To activate a theme, set the {@link CWebApplication::setTheme theme} property |
||||||
|
* to be the name of that theme. |
||||||
|
* |
||||||
|
* Since a self-contained theme often contains resource files that are made |
||||||
|
* Web accessible, please make sure the view/layout files are protected from Web access. |
||||||
|
* |
||||||
|
* @property array $themeNames List of available theme names. |
||||||
|
* @property string $basePath The base path for all themes. Defaults to "WebRootPath/themes". |
||||||
|
* @property string $baseUrl The base URL for all themes. Defaults to "/WebRoot/themes". |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CThemeManager extends CApplicationComponent |
||||||
|
{ |
||||||
|
/** |
||||||
|
* default themes base path |
||||||
|
*/ |
||||||
|
const DEFAULT_BASEPATH='themes'; |
||||||
|
|
||||||
|
/** |
||||||
|
* @var string the name of the theme class for representing a theme. |
||||||
|
* Defaults to {@link CTheme}. This can also be a class name in dot syntax. |
||||||
|
*/ |
||||||
|
public $themeClass='CTheme'; |
||||||
|
|
||||||
|
private $_basePath=null; |
||||||
|
private $_baseUrl=null; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $name name of the theme to be retrieved |
||||||
|
* @return CTheme the theme retrieved. Null if the theme does not exist. |
||||||
|
*/ |
||||||
|
public function getTheme($name) |
||||||
|
{ |
||||||
|
$themePath=$this->getBasePath().DIRECTORY_SEPARATOR.$name; |
||||||
|
if(is_dir($themePath)) |
||||||
|
{ |
||||||
|
$class=Yii::import($this->themeClass, true); |
||||||
|
return new $class($name,$themePath,$this->getBaseUrl().'/'.$name); |
||||||
|
} |
||||||
|
else |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return array list of available theme names |
||||||
|
*/ |
||||||
|
public function getThemeNames() |
||||||
|
{ |
||||||
|
static $themes; |
||||||
|
if($themes===null) |
||||||
|
{ |
||||||
|
$themes=array(); |
||||||
|
$basePath=$this->getBasePath(); |
||||||
|
$folder=@opendir($basePath); |
||||||
|
while(($file=@readdir($folder))!==false) |
||||||
|
{ |
||||||
|
if($file!=='.' && $file!=='..' && $file!=='.svn' && $file!=='.gitignore' && is_dir($basePath.DIRECTORY_SEPARATOR.$file)) |
||||||
|
$themes[]=$file; |
||||||
|
} |
||||||
|
closedir($folder); |
||||||
|
sort($themes); |
||||||
|
} |
||||||
|
return $themes; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the base path for all themes. Defaults to "WebRootPath/themes". |
||||||
|
*/ |
||||||
|
public function getBasePath() |
||||||
|
{ |
||||||
|
if($this->_basePath===null) |
||||||
|
$this->setBasePath(dirname(Yii::app()->getRequest()->getScriptFile()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH); |
||||||
|
return $this->_basePath; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $value the base path for all themes. |
||||||
|
* @throws CException if the base path does not exist |
||||||
|
*/ |
||||||
|
public function setBasePath($value) |
||||||
|
{ |
||||||
|
$this->_basePath=realpath($value); |
||||||
|
if($this->_basePath===false || !is_dir($this->_basePath)) |
||||||
|
throw new CException(Yii::t('yii','Theme directory "{directory}" does not exist.',array('{directory}'=>$value))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string the base URL for all themes. Defaults to "/WebRoot/themes". |
||||||
|
*/ |
||||||
|
public function getBaseUrl() |
||||||
|
{ |
||||||
|
if($this->_baseUrl===null) |
||||||
|
$this->_baseUrl=Yii::app()->getBaseUrl().'/'.self::DEFAULT_BASEPATH; |
||||||
|
return $this->_baseUrl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $value the base URL for all themes. |
||||||
|
*/ |
||||||
|
public function setBaseUrl($value) |
||||||
|
{ |
||||||
|
$this->_baseUrl=rtrim($value,'/'); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,849 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* CUrlManager class file |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* CUrlManager manages the URLs of Yii Web applications. |
||||||
|
* |
||||||
|
* It provides URL construction ({@link createUrl()}) as well as parsing ({@link parseUrl()}) functionality. |
||||||
|
* |
||||||
|
* URLs managed via CUrlManager can be in one of the following two formats, |
||||||
|
* by setting {@link setUrlFormat urlFormat} property: |
||||||
|
* <ul> |
||||||
|
* <li>'path' format: /path/to/EntryScript.php/name1/value1/name2/value2...</li> |
||||||
|
* <li>'get' format: /path/to/EntryScript.php?name1=value1&name2=value2...</li> |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* When using 'path' format, CUrlManager uses a set of {@link setRules rules} to: |
||||||
|
* <ul> |
||||||
|
* <li>parse the requested URL into a route ('ControllerID/ActionID') and GET parameters;</li> |
||||||
|
* <li>create URLs based on the given route and GET parameters.</li> |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* A rule consists of a route and a pattern. The latter is used by CUrlManager to determine |
||||||
|
* which rule is used for parsing/creating URLs. A pattern is meant to match the path info |
||||||
|
* part of a URL. It may contain named parameters using the syntax '<ParamName:RegExp>'. |
||||||
|
* |
||||||
|
* When parsing a URL, a matching rule will extract the named parameters from the path info |
||||||
|
* and put them into the $_GET variable; when creating a URL, a matching rule will extract |
||||||
|
* the named parameters from $_GET and put them into the path info part of the created URL. |
||||||
|
* |
||||||
|
* If a pattern ends with '/*', it means additional GET parameters may be appended to the path |
||||||
|
* info part of the URL; otherwise, the GET parameters can only appear in the query string part. |
||||||
|
* |
||||||
|
* To specify URL rules, set the {@link setRules rules} property as an array of rules (pattern=>route). |
||||||
|
* For example, |
||||||
|
* <pre> |
||||||
|
* array( |
||||||
|
* 'articles'=>'article/list', |
||||||
|
* 'article/<id:\d+>/*'=>'article/read', |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* Two rules are specified in the above: |
||||||
|
* <ul> |
||||||
|
* <li>The first rule says that if the user requests the URL '/path/to/index.php/articles', |
||||||
|
* it should be treated as '/path/to/index.php/article/list'; and vice versa applies |
||||||
|
* when constructing such a URL.</li> |
||||||
|
* <li>The second rule contains a named parameter 'id' which is specified using |
||||||
|
* the <ParamName:RegExp> syntax. It says that if the user requests the URL |
||||||
|
* '/path/to/index.php/article/13', it should be treated as '/path/to/index.php/article/read?id=13'; |
||||||
|
* and vice versa applies when constructing such a URL.</li> |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* The route part may contain references to named parameters defined in the pattern part. |
||||||
|
* This allows a rule to be applied to different routes based on matching criteria. |
||||||
|
* For example, |
||||||
|
* <pre> |
||||||
|
* array( |
||||||
|
* '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>'=>'<_c>/<_a>', |
||||||
|
* '<_c:(post|comment)>/<id:\d+>'=>'<_c>/view', |
||||||
|
* '<_c:(post|comment)>s/*'=>'<_c>/list', |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* In the above, we use two named parameters '<_c>' and '<_a>' in the route part. The '<_c>' |
||||||
|
* parameter matches either 'post' or 'comment', while the '<_a>' parameter matches an action ID. |
||||||
|
* |
||||||
|
* Like normal rules, these rules can be used for both parsing and creating URLs. |
||||||
|
* For example, using the rules above, the URL '/index.php/post/123/create' |
||||||
|
* would be parsed as the route 'post/create' with GET parameter 'id' being 123. |
||||||
|
* And given the route 'post/list' and GET parameter 'page' being 2, we should get a URL |
||||||
|
* '/index.php/posts/page/2'. |
||||||
|
* |
||||||
|
* It is also possible to include hostname into the rules for parsing and creating URLs. |
||||||
|
* One may extract part of the hostname to be a GET parameter. |
||||||
|
* For example, the URL <code>http://admin.example.com/en/profile</code> may be parsed into GET parameters |
||||||
|
* <code>user=admin</code> and <code>lang=en</code>. On the other hand, rules with hostname may also be used to |
||||||
|
* create URLs with parameterized hostnames. |
||||||
|
* |
||||||
|
* In order to use parameterized hostnames, simply declare URL rules with host info, e.g.: |
||||||
|
* <pre> |
||||||
|
* array( |
||||||
|
* 'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile', |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* Starting from version 1.1.8, one can write custom URL rule classes and use them for one or several URL rules. |
||||||
|
* For example, |
||||||
|
* <pre> |
||||||
|
* array( |
||||||
|
* // a standard rule |
||||||
|
* '<action:(login|logout)>' => 'site/<action>', |
||||||
|
* // a custom rule using data in DB |
||||||
|
* array( |
||||||
|
* 'class' => 'application.components.MyUrlRule', |
||||||
|
* 'connectionID' => 'db', |
||||||
|
* ), |
||||||
|
* ) |
||||||
|
* </pre> |
||||||
|
* Please note that the custom URL rule class should extend from {@link CBaseUrlRule} and |
||||||
|
* implement the following two methods, |
||||||
|
* <ul> |
||||||
|
* <li>{@link CBaseUrlRule::createUrl()}</li> |
||||||
|
* <li>{@link CBaseUrlRule::parseUrl()}</li> |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* CUrlManager is a default application component that may be accessed via |
||||||
|
* {@link CWebApplication::getUrlManager()}. |
||||||
|
* |
||||||
|
* @property string $baseUrl The base URL of the application (the part after host name and before query string). |
||||||
|
* If {@link showScriptName} is true, it will include the script name part. |
||||||
|
* Otherwise, it will not, and the ending slashes are stripped off. |
||||||
|
* @property string $urlFormat The URL format. Defaults to 'path'. Valid values include 'path' and 'get'. |
||||||
|
* Please refer to the guide for more details about the difference between these two formats. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CUrlManager extends CApplicationComponent |
||||||
|
{ |
||||||
|
const CACHE_KEY='Yii.CUrlManager.rules'; |
||||||
|
const GET_FORMAT='get'; |
||||||
|
const PATH_FORMAT='path'; |
||||||
|
|
||||||
|
/** |
||||||
|
* @var array the URL rules (pattern=>route). |
||||||
|
*/ |
||||||
|
public $rules=array(); |
||||||
|
/** |
||||||
|
* @var string the URL suffix used when in 'path' format. |
||||||
|
* For example, ".html" can be used so that the URL looks like pointing to a static HTML page. Defaults to empty. |
||||||
|
*/ |
||||||
|
public $urlSuffix=''; |
||||||
|
/** |
||||||
|
* @var boolean whether to show entry script name in the constructed URL. Defaults to true. |
||||||
|
*/ |
||||||
|
public $showScriptName=true; |
||||||
|
/** |
||||||
|
* @var boolean whether to append GET parameters to the path info part. Defaults to true. |
||||||
|
* This property is only effective when {@link urlFormat} is 'path' and is mainly used when |
||||||
|
* creating URLs. When it is true, GET parameters will be appended to the path info and |
||||||
|
* separate from each other using slashes. If this is false, GET parameters will be in query part. |
||||||
|
*/ |
||||||
|
public $appendParams=true; |
||||||
|
/** |
||||||
|
* @var string the GET variable name for route. Defaults to 'r'. |
||||||
|
*/ |
||||||
|
public $routeVar='r'; |
||||||
|
/** |
||||||
|
* @var boolean whether routes are case-sensitive. Defaults to true. By setting this to false, |
||||||
|
* the route in the incoming request will be turned to lower case first before further processing. |
||||||
|
* As a result, you should follow the convention that you use lower case when specifying |
||||||
|
* controller mapping ({@link CWebApplication::controllerMap}) and action mapping |
||||||
|
* ({@link CController::actions}). Also, the directory names for organizing controllers should |
||||||
|
* be in lower case. |
||||||
|
*/ |
||||||
|
public $caseSensitive=true; |
||||||
|
/** |
||||||
|
* @var boolean whether the GET parameter values should match the corresponding |
||||||
|
* sub-patterns in a rule before using it to create a URL. Defaults to false, meaning |
||||||
|
* a rule will be used for creating a URL only if its route and parameter names match the given ones. |
||||||
|
* If this property is set true, then the given parameter values must also match the corresponding |
||||||
|
* parameter sub-patterns. Note that setting this property to true will degrade performance. |
||||||
|
* @since 1.1.0 |
||||||
|
*/ |
||||||
|
public $matchValue=false; |
||||||
|
/** |
||||||
|
* @var string the ID of the cache application component that is used to cache the parsed URL rules. |
||||||
|
* Defaults to 'cache' which refers to the primary cache application component. |
||||||
|
* Set this property to false if you want to disable caching URL rules. |
||||||
|
*/ |
||||||
|
public $cacheID='cache'; |
||||||
|
/** |
||||||
|
* @var boolean whether to enable strict URL parsing. |
||||||
|
* This property is only effective when {@link urlFormat} is 'path'. |
||||||
|
* If it is set true, then an incoming URL must match one of the {@link rules URL rules}. |
||||||
|
* Otherwise, it will be treated as an invalid request and trigger a 404 HTTP exception. |
||||||
|
* Defaults to false. |
||||||
|
*/ |
||||||
|
public $useStrictParsing=false; |
||||||
|
/** |
||||||
|
* @var string the class name or path alias for the URL rule instances. Defaults to 'CUrlRule'. |
||||||
|
* If you change this to something else, please make sure that the new class must extend from |
||||||
|
* {@link CBaseUrlRule} and have the same constructor signature as {@link CUrlRule}. |
||||||
|
* It must also be serializable and autoloadable. |
||||||
|
* @since 1.1.8 |
||||||
|
*/ |
||||||
|
public $urlRuleClass='CUrlRule'; |
||||||
|
|
||||||
|
private $_urlFormat=self::GET_FORMAT; |
||||||
|
private $_rules=array(); |
||||||
|
private $_baseUrl; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes the application component. |
||||||
|
*/ |
||||||
|
public function init() |
||||||
|
{ |
||||||
|
parent::init(); |
||||||
|
$this->processRules(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Processes the URL rules. |
||||||
|
*/ |
||||||
|
protected function processRules() |
||||||
|
{ |
||||||
|
if(empty($this->rules) || $this->getUrlFormat()===self::GET_FORMAT) |
||||||
|
return; |
||||||
|
if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null) |
||||||
|
{ |
||||||
|
$hash=md5(serialize($this->rules)); |
||||||
|
if(($data=$cache->get(self::CACHE_KEY))!==false && isset($data[1]) && $data[1]===$hash) |
||||||
|
{ |
||||||
|
$this->_rules=$data[0]; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
foreach($this->rules as $pattern=>$route) |
||||||
|
$this->_rules[]=$this->createUrlRule($route,$pattern); |
||||||
|
if(isset($cache)) |
||||||
|
$cache->set(self::CACHE_KEY,array($this->_rules,$hash)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds new URL rules. |
||||||
|
* In order to make the new rules effective, this method must be called BEFORE |
||||||
|
* {@link CWebApplication::processRequest}. |
||||||
|
* @param array $rules new URL rules (pattern=>route). |
||||||
|
* @param boolean $append whether the new URL rules should be appended to the existing ones. If false, |
||||||
|
* they will be inserted at the beginning. |
||||||
|
* @since 1.1.4 |
||||||
|
*/ |
||||||
|
public function addRules($rules, $append=true) |
||||||
|
{ |
||||||
|
if ($append) |
||||||
|
{ |
||||||
|
foreach($rules as $pattern=>$route) |
||||||
|
$this->_rules[]=$this->createUrlRule($route,$pattern); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
foreach($rules as $pattern=>$route) |
||||||
|
array_unshift($this->_rules, $this->createUrlRule($route,$pattern)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a URL rule instance. |
||||||
|
* The default implementation returns a CUrlRule object. |
||||||
|
* @param mixed $route the route part of the rule. This could be a string or an array |
||||||
|
* @param string $pattern the pattern part of the rule |
||||||
|
* @return CUrlRule the URL rule instance |
||||||
|
* @since 1.1.0 |
||||||
|
*/ |
||||||
|
protected function createUrlRule($route,$pattern) |
||||||
|
{ |
||||||
|
if(is_array($route) && isset($route['class'])) |
||||||
|
return $route; |
||||||
|
else |
||||||
|
return new $this->urlRuleClass($route,$pattern); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a URL. |
||||||
|
* @param string $route the controller and the action (e.g. article/read) |
||||||
|
* @param array $params list of GET parameters (name=>value). Both the name and value will be URL-encoded. |
||||||
|
* If the name is '#', the corresponding value will be treated as an anchor |
||||||
|
* and will be appended at the end of the URL. |
||||||
|
* @param string $ampersand the token separating name-value pairs in the URL. Defaults to '&'. |
||||||
|
* @return string the constructed URL |
||||||
|
*/ |
||||||
|
public function createUrl($route,$params=array(),$ampersand='&') |
||||||
|
{ |
||||||
|
unset($params[$this->routeVar]); |
||||||
|
foreach($params as $i=>$param) |
||||||
|
if($param===null) |
||||||
|
$params[$i]=''; |
||||||
|
|
||||||
|
if(isset($params['#'])) |
||||||
|
{ |
||||||
|
$anchor='#'.$params['#']; |
||||||
|
unset($params['#']); |
||||||
|
} |
||||||
|
else |
||||||
|
$anchor=''; |
||||||
|
$route=trim($route,'/'); |
||||||
|
foreach($this->_rules as $i=>$rule) |
||||||
|
{ |
||||||
|
if(is_array($rule)) |
||||||
|
$this->_rules[$i]=$rule=Yii::createComponent($rule); |
||||||
|
if(($url=$rule->createUrl($this,$route,$params,$ampersand))!==false) |
||||||
|
{ |
||||||
|
if($rule->hasHostInfo) |
||||||
|
return $url==='' ? '/'.$anchor : $url.$anchor; |
||||||
|
else |
||||||
|
return $this->getBaseUrl().'/'.$url.$anchor; |
||||||
|
} |
||||||
|
} |
||||||
|
return $this->createUrlDefault($route,$params,$ampersand).$anchor; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a URL based on default settings. |
||||||
|
* @param string $route the controller and the action (e.g. article/read) |
||||||
|
* @param array $params list of GET parameters |
||||||
|
* @param string $ampersand the token separating name-value pairs in the URL. |
||||||
|
* @return string the constructed URL |
||||||
|
*/ |
||||||
|
protected function createUrlDefault($route,$params,$ampersand) |
||||||
|
{ |
||||||
|
if($this->getUrlFormat()===self::PATH_FORMAT) |
||||||
|
{ |
||||||
|
$url=rtrim($this->getBaseUrl().'/'.$route,'/'); |
||||||
|
if($this->appendParams) |
||||||
|
{ |
||||||
|
$url=rtrim($url.'/'.$this->createPathInfo($params,'/','/'),'/'); |
||||||
|
return $route==='' ? $url : $url.$this->urlSuffix; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if($route!=='') |
||||||
|
$url.=$this->urlSuffix; |
||||||
|
$query=$this->createPathInfo($params,'=',$ampersand); |
||||||
|
return $query==='' ? $url : $url.'?'.$query; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
$url=$this->getBaseUrl(); |
||||||
|
if(!$this->showScriptName) |
||||||
|
$url.='/'; |
||||||
|
if($route!=='') |
||||||
|
{ |
||||||
|
$url.='?'.$this->routeVar.'='.$route; |
||||||
|
if(($query=$this->createPathInfo($params,'=',$ampersand))!=='') |
||||||
|
$url.=$ampersand.$query; |
||||||
|
} |
||||||
|
else if(($query=$this->createPathInfo($params,'=',$ampersand))!=='') |
||||||
|
$url.='?'.$query; |
||||||
|
return $url; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parses the user request. |
||||||
|
* @param CHttpRequest $request the request application component |
||||||
|
* @return string the route (controllerID/actionID) and perhaps GET parameters in path format. |
||||||
|
*/ |
||||||
|
public function parseUrl($request) |
||||||
|
{ |
||||||
|
if($this->getUrlFormat()===self::PATH_FORMAT) |
||||||
|
{ |
||||||
|
$rawPathInfo=$request->getPathInfo(); |
||||||
|
$pathInfo=$this->removeUrlSuffix($rawPathInfo,$this->urlSuffix); |
||||||
|
foreach($this->_rules as $i=>$rule) |
||||||
|
{ |
||||||
|
if(is_array($rule)) |
||||||
|
$this->_rules[$i]=$rule=Yii::createComponent($rule); |
||||||
|
if(($r=$rule->parseUrl($this,$request,$pathInfo,$rawPathInfo))!==false) |
||||||
|
return isset($_GET[$this->routeVar]) ? $_GET[$this->routeVar] : $r; |
||||||
|
} |
||||||
|
if($this->useStrictParsing) |
||||||
|
throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', |
||||||
|
array('{route}'=>$pathInfo))); |
||||||
|
else |
||||||
|
return $pathInfo; |
||||||
|
} |
||||||
|
else if(isset($_GET[$this->routeVar])) |
||||||
|
return $_GET[$this->routeVar]; |
||||||
|
else if(isset($_POST[$this->routeVar])) |
||||||
|
return $_POST[$this->routeVar]; |
||||||
|
else |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parses a path info into URL segments and saves them to $_GET and $_REQUEST. |
||||||
|
* @param string $pathInfo path info |
||||||
|
*/ |
||||||
|
public function parsePathInfo($pathInfo) |
||||||
|
{ |
||||||
|
if($pathInfo==='') |
||||||
|
return; |
||||||
|
$segs=explode('/',$pathInfo.'/'); |
||||||
|
$n=count($segs); |
||||||
|
for($i=0;$i<$n-1;$i+=2) |
||||||
|
{ |
||||||
|
$key=$segs[$i]; |
||||||
|
if($key==='') continue; |
||||||
|
$value=$segs[$i+1]; |
||||||
|
if(($pos=strpos($key,'['))!==false && ($m=preg_match_all('/\[(.*?)\]/',$key,$matches))>0) |
||||||
|
{ |
||||||
|
$name=substr($key,0,$pos); |
||||||
|
for($j=$m-1;$j>=0;--$j) |
||||||
|
{ |
||||||
|
if($matches[1][$j]==='') |
||||||
|
$value=array($value); |
||||||
|
else |
||||||
|
$value=array($matches[1][$j]=>$value); |
||||||
|
} |
||||||
|
if(isset($_GET[$name]) && is_array($_GET[$name])) |
||||||
|
$value=CMap::mergeArray($_GET[$name],$value); |
||||||
|
$_REQUEST[$name]=$_GET[$name]=$value; |
||||||
|
} |
||||||
|
else |
||||||
|
$_REQUEST[$key]=$_GET[$key]=$value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a path info based on the given parameters. |
||||||
|
* @param array $params list of GET parameters |
||||||
|
* @param string $equal the separator between name and value |
||||||
|
* @param string $ampersand the separator between name-value pairs |
||||||
|
* @param string $key this is used internally. |
||||||
|
* @return string the created path info |
||||||
|
*/ |
||||||
|
public function createPathInfo($params,$equal,$ampersand, $key=null) |
||||||
|
{ |
||||||
|
$pairs = array(); |
||||||
|
foreach($params as $k => $v) |
||||||
|
{ |
||||||
|
if ($key!==null) |
||||||
|
$k = $key.'['.$k.']'; |
||||||
|
|
||||||
|
if (is_array($v)) |
||||||
|
$pairs[]=$this->createPathInfo($v,$equal,$ampersand, $k); |
||||||
|
else |
||||||
|
$pairs[]=urlencode($k).$equal.urlencode($v); |
||||||
|
} |
||||||
|
return implode($ampersand,$pairs); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes the URL suffix from path info. |
||||||
|
* @param string $pathInfo path info part in the URL |
||||||
|
* @param string $urlSuffix the URL suffix to be removed |
||||||
|
* @return string path info with URL suffix removed. |
||||||
|
*/ |
||||||
|
public function removeUrlSuffix($pathInfo,$urlSuffix) |
||||||
|
{ |
||||||
|
if($urlSuffix!=='' && substr($pathInfo,-strlen($urlSuffix))===$urlSuffix) |
||||||
|
return substr($pathInfo,0,-strlen($urlSuffix)); |
||||||
|
else |
||||||
|
return $pathInfo; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the base URL of the application. |
||||||
|
* @return string the base URL of the application (the part after host name and before query string). |
||||||
|
* If {@link showScriptName} is true, it will include the script name part. |
||||||
|
* Otherwise, it will not, and the ending slashes are stripped off. |
||||||
|
*/ |
||||||
|
public function getBaseUrl() |
||||||
|
{ |
||||||
|
if($this->_baseUrl!==null) |
||||||
|
return $this->_baseUrl; |
||||||
|
else |
||||||
|
{ |
||||||
|
if($this->showScriptName) |
||||||
|
$this->_baseUrl=Yii::app()->getRequest()->getScriptUrl(); |
||||||
|
else |
||||||
|
$this->_baseUrl=Yii::app()->getRequest()->getBaseUrl(); |
||||||
|
return $this->_baseUrl; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the base URL of the application (the part after host name and before query string). |
||||||
|
* This method is provided in case the {@link baseUrl} cannot be determined automatically. |
||||||
|
* The ending slashes should be stripped off. And you are also responsible to remove the script name |
||||||
|
* if you set {@link showScriptName} to be false. |
||||||
|
* @param string $value the base URL of the application |
||||||
|
* @since 1.1.1 |
||||||
|
*/ |
||||||
|
public function setBaseUrl($value) |
||||||
|
{ |
||||||
|
$this->_baseUrl=$value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the URL format. |
||||||
|
* @return string the URL format. Defaults to 'path'. Valid values include 'path' and 'get'. |
||||||
|
* Please refer to the guide for more details about the difference between these two formats. |
||||||
|
*/ |
||||||
|
public function getUrlFormat() |
||||||
|
{ |
||||||
|
return $this->_urlFormat; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the URL format. |
||||||
|
* @param string $value the URL format. It must be either 'path' or 'get'. |
||||||
|
*/ |
||||||
|
public function setUrlFormat($value) |
||||||
|
{ |
||||||
|
if($value===self::PATH_FORMAT || $value===self::GET_FORMAT) |
||||||
|
$this->_urlFormat=$value; |
||||||
|
else |
||||||
|
throw new CException(Yii::t('yii','CUrlManager.UrlFormat must be either "path" or "get".')); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* CBaseUrlRule is the base class for a URL rule class. |
||||||
|
* |
||||||
|
* Custom URL rule classes should extend from this class and implement two methods: |
||||||
|
* {@link createUrl} and {@link parseUrl}. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.1.8 |
||||||
|
*/ |
||||||
|
abstract class CBaseUrlRule extends CComponent |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var boolean whether this rule will also parse the host info part. Defaults to false. |
||||||
|
*/ |
||||||
|
public $hasHostInfo=false; |
||||||
|
/** |
||||||
|
* Creates a URL based on this rule. |
||||||
|
* @param CUrlManager $manager the manager |
||||||
|
* @param string $route the route |
||||||
|
* @param array $params list of parameters (name=>value) associated with the route |
||||||
|
* @param string $ampersand the token separating name-value pairs in the URL. |
||||||
|
* @return mixed the constructed URL. False if this rule does not apply. |
||||||
|
*/ |
||||||
|
abstract public function createUrl($manager,$route,$params,$ampersand); |
||||||
|
/** |
||||||
|
* Parses a URL based on this rule. |
||||||
|
* @param CUrlManager $manager the URL manager |
||||||
|
* @param CHttpRequest $request the request object |
||||||
|
* @param string $pathInfo path info part of the URL (URL suffix is already removed based on {@link CUrlManager::urlSuffix}) |
||||||
|
* @param string $rawPathInfo path info that contains the potential URL suffix |
||||||
|
* @return mixed the route that consists of the controller ID and action ID. False if this rule does not apply. |
||||||
|
*/ |
||||||
|
abstract public function parseUrl($manager,$request,$pathInfo,$rawPathInfo); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* CUrlRule represents a URL formatting/parsing rule. |
||||||
|
* |
||||||
|
* It mainly consists of two parts: route and pattern. The former classifies |
||||||
|
* the rule so that it only applies to specific controller-action route. |
||||||
|
* The latter performs the actual formatting and parsing role. The pattern |
||||||
|
* may have a set of named parameters. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id$ |
||||||
|
* @package system.web |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class CUrlRule extends CBaseUrlRule |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var string the URL suffix used for this rule. |
||||||
|
* For example, ".html" can be used so that the URL looks like pointing to a static HTML page. |
||||||
|
* Defaults to null, meaning using the value of {@link CUrlManager::urlSuffix}. |
||||||
|
*/ |
||||||
|
public $urlSuffix; |
||||||
|
/** |
||||||
|
* @var boolean whether the rule is case sensitive. Defaults to null, meaning |
||||||
|
* using the value of {@link CUrlManager::caseSensitive}. |
||||||
|
*/ |
||||||
|
public $caseSensitive; |
||||||
|
/** |
||||||
|
* @var array the default GET parameters (name=>value) that this rule provides. |
||||||
|
* When this rule is used to parse the incoming request, the values declared in this property |
||||||
|
* will be injected into $_GET. |
||||||
|
*/ |
||||||
|
public $defaultParams=array(); |
||||||
|
/** |
||||||
|
* @var boolean whether the GET parameter values should match the corresponding |
||||||
|
* sub-patterns in the rule when creating a URL. Defaults to null, meaning using the value |
||||||
|
* of {@link CUrlManager::matchValue}. When this property is false, it means |
||||||
|
* a rule will be used for creating a URL if its route and parameter names match the given ones. |
||||||
|
* If this property is set true, then the given parameter values must also match the corresponding |
||||||
|
* parameter sub-patterns. Note that setting this property to true will degrade performance. |
||||||
|
* @since 1.1.0 |
||||||
|
*/ |
||||||
|
public $matchValue; |
||||||
|
/** |
||||||
|
* @var string the HTTP verb (e.g. GET, POST, DELETE) that this rule should match. |
||||||
|
* If this rule can match multiple verbs, please separate them with commas. |
||||||
|
* If this property is not set, the rule can match any verb. |
||||||
|
* Note that this property is only used when parsing a request. It is ignored for URL creation. |
||||||
|
* @since 1.1.7 |
||||||
|
*/ |
||||||
|
public $verb; |
||||||
|
/** |
||||||
|
* @var boolean whether this rule is only used for request parsing. |
||||||
|
* Defaults to false, meaning the rule is used for both URL parsing and creation. |
||||||
|
* @since 1.1.7 |
||||||
|
*/ |
||||||
|
public $parsingOnly=false; |
||||||
|
/** |
||||||
|
* @var string the controller/action pair |
||||||
|
*/ |
||||||
|
public $route; |
||||||
|
/** |
||||||
|
* @var array the mapping from route param name to token name (e.g. _r1=><1>) |
||||||
|
*/ |
||||||
|
public $references=array(); |
||||||
|
/** |
||||||
|
* @var string the pattern used to match route |
||||||
|
*/ |
||||||
|
public $routePattern; |
||||||
|
/** |
||||||
|
* @var string regular expression used to parse a URL |
||||||
|
*/ |
||||||
|
public $pattern; |
||||||
|
/** |
||||||
|
* @var string template used to construct a URL |
||||||
|
*/ |
||||||
|
public $template; |
||||||
|
/** |
||||||
|
* @var array list of parameters (name=>regular expression) |
||||||
|
*/ |
||||||
|
public $params=array(); |
||||||
|
/** |
||||||
|
* @var boolean whether the URL allows additional parameters at the end of the path info. |
||||||
|
*/ |
||||||
|
public $append; |
||||||
|
/** |
||||||
|
* @var boolean whether host info should be considered for this rule |
||||||
|
*/ |
||||||
|
public $hasHostInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param string $route the route of the URL (controller/action) |
||||||
|
* @param string $pattern the pattern for matching the URL |
||||||
|
*/ |
||||||
|
public function __construct($route,$pattern) |
||||||
|
{ |
||||||
|
if(is_array($route)) |
||||||
|
{ |
||||||
|
foreach(array('urlSuffix', 'caseSensitive', 'defaultParams', 'matchValue', 'verb', 'parsingOnly') as $name) |
||||||
|
{ |
||||||
|
if(isset($route[$name])) |
||||||
|
$this->$name=$route[$name]; |
||||||
|
} |
||||||
|
if(isset($route['pattern'])) |
||||||
|
$pattern=$route['pattern']; |
||||||
|
$route=$route[0]; |
||||||
|
} |
||||||
|
$this->route=trim($route,'/'); |
||||||
|
|
||||||
|
$tr2['/']=$tr['/']='\\/'; |
||||||
|
|
||||||
|
if(strpos($route,'<')!==false && preg_match_all('/<(\w+)>/',$route,$matches2)) |
||||||
|
{ |
||||||
|
foreach($matches2[1] as $name) |
||||||
|
$this->references[$name]="<$name>"; |
||||||
|
} |
||||||
|
|
||||||
|
$this->hasHostInfo=!strncasecmp($pattern,'http://',7) || !strncasecmp($pattern,'https://',8); |
||||||
|
|
||||||
|
if($this->verb!==null) |
||||||
|
$this->verb=preg_split('/[\s,]+/',strtoupper($this->verb),-1,PREG_SPLIT_NO_EMPTY); |
||||||
|
|
||||||
|
if(preg_match_all('/<(\w+):?(.*?)?>/',$pattern,$matches)) |
||||||
|
{ |
||||||
|
$tokens=array_combine($matches[1],$matches[2]); |
||||||
|
foreach($tokens as $name=>$value) |
||||||
|
{ |
||||||
|
if($value==='') |
||||||
|
$value='[^\/]+'; |
||||||
|
$tr["<$name>"]="(?P<$name>$value)"; |
||||||
|
if(isset($this->references[$name])) |
||||||
|
$tr2["<$name>"]=$tr["<$name>"]; |
||||||
|
else |
||||||
|
$this->params[$name]=$value; |
||||||
|
} |
||||||
|
} |
||||||
|
$p=rtrim($pattern,'*'); |
||||||
|
$this->append=$p!==$pattern; |
||||||
|
$p=trim($p,'/'); |
||||||
|
$this->template=preg_replace('/<(\w+):?.*?>/','<$1>',$p); |
||||||
|
$this->pattern='/^'.strtr($this->template,$tr).'\/'; |
||||||
|
if($this->append) |
||||||
|
$this->pattern.='/u'; |
||||||
|
else |
||||||
|
$this->pattern.='$/u'; |
||||||
|
|
||||||
|
if($this->references!==array()) |
||||||
|
$this->routePattern='/^'.strtr($this->route,$tr2).'$/u'; |
||||||
|
|
||||||
|
if(YII_DEBUG && @preg_match($this->pattern,'test')===false) |
||||||
|
throw new CException(Yii::t('yii','The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.', |
||||||
|
array('{route}'=>$route,'{pattern}'=>$pattern))); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a URL based on this rule. |
||||||
|
* @param CUrlManager $manager the manager |
||||||
|
* @param string $route the route |
||||||
|
* @param array $params list of parameters |
||||||
|
* @param string $ampersand the token separating name-value pairs in the URL. |
||||||
|
* @return mixed the constructed URL or false on error |
||||||
|
*/ |
||||||
|
public function createUrl($manager,$route,$params,$ampersand) |
||||||
|
{ |
||||||
|
if($this->parsingOnly) |
||||||
|
return false; |
||||||
|
|
||||||
|
if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive) |
||||||
|
$case=''; |
||||||
|
else |
||||||
|
$case='i'; |
||||||
|
|
||||||
|
$tr=array(); |
||||||
|
if($route!==$this->route) |
||||||
|
{ |
||||||
|
if($this->routePattern!==null && preg_match($this->routePattern.$case,$route,$matches)) |
||||||
|
{ |
||||||
|
foreach($this->references as $key=>$name) |
||||||
|
$tr[$name]=$matches[$key]; |
||||||
|
} |
||||||
|
else |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
foreach($this->defaultParams as $key=>$value) |
||||||
|
{ |
||||||
|
if(isset($params[$key])) |
||||||
|
{ |
||||||
|
if($params[$key]==$value) |
||||||
|
unset($params[$key]); |
||||||
|
else |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach($this->params as $key=>$value) |
||||||
|
if(!isset($params[$key])) |
||||||
|
return false; |
||||||
|
|
||||||
|
if($manager->matchValue && $this->matchValue===null || $this->matchValue) |
||||||
|
{ |
||||||
|
foreach($this->params as $key=>$value) |
||||||
|
{ |
||||||
|
if(!preg_match('/\A'.$value.'\z/u'.$case,$params[$key])) |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach($this->params as $key=>$value) |
||||||
|
{ |
||||||
|
$tr["<$key>"]=urlencode($params[$key]); |
||||||
|
unset($params[$key]); |
||||||
|
} |
||||||
|
|
||||||
|
$suffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix; |
||||||
|
|
||||||
|
$url=strtr($this->template,$tr); |
||||||
|
|
||||||
|
if($this->hasHostInfo) |
||||||
|
{ |
||||||
|
$hostInfo=Yii::app()->getRequest()->getHostInfo(); |
||||||
|
if(stripos($url,$hostInfo)===0) |
||||||
|
$url=substr($url,strlen($hostInfo)); |
||||||
|
} |
||||||
|
|
||||||
|
if(empty($params)) |
||||||
|
return $url!=='' ? $url.$suffix : $url; |
||||||
|
|
||||||
|
if($this->append) |
||||||
|
$url.='/'.$manager->createPathInfo($params,'/','/').$suffix; |
||||||
|
else |
||||||
|
{ |
||||||
|
if($url!=='') |
||||||
|
$url.=$suffix; |
||||||
|
$url.='?'.$manager->createPathInfo($params,'=',$ampersand); |
||||||
|
} |
||||||
|
|
||||||
|
return $url; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parses a URL based on this rule. |
||||||
|
* @param CUrlManager $manager the URL manager |
||||||
|
* @param CHttpRequest $request the request object |
||||||
|
* @param string $pathInfo path info part of the URL |
||||||
|
* @param string $rawPathInfo path info that contains the potential URL suffix |
||||||
|
* @return mixed the route that consists of the controller ID and action ID or false on error |
||||||
|
*/ |
||||||
|
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo) |
||||||
|
{ |
||||||
|
if($this->verb!==null && !in_array($request->getRequestType(), $this->verb, true)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive) |
||||||
|
$case=''; |
||||||
|
else |
||||||
|
$case='i'; |
||||||
|
|
||||||
|
if($this->urlSuffix!==null) |
||||||
|
$pathInfo=$manager->removeUrlSuffix($rawPathInfo,$this->urlSuffix); |
||||||
|
|
||||||
|
// URL suffix required, but not found in the requested URL |
||||||
|
if($manager->useStrictParsing && $pathInfo===$rawPathInfo) |
||||||
|
{ |
||||||
|
$urlSuffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix; |
||||||
|
if($urlSuffix!='' && $urlSuffix!=='/') |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if($this->hasHostInfo) |
||||||
|
$pathInfo=strtolower($request->getHostInfo()).rtrim('/'.$pathInfo,'/'); |
||||||
|
|
||||||
|
$pathInfo.='/'; |
||||||
|
|
||||||
|
if(preg_match($this->pattern.$case,$pathInfo,$matches)) |
||||||
|
{ |
||||||
|
foreach($this->defaultParams as $name=>$value) |
||||||
|
{ |
||||||
|
if(!isset($_GET[$name])) |
||||||
|
$_REQUEST[$name]=$_GET[$name]=$value; |
||||||
|
} |
||||||
|
$tr=array(); |
||||||
|
foreach($matches as $key=>$value) |
||||||
|
{ |
||||||
|
if(isset($this->references[$key])) |
||||||
|
$tr[$this->references[$key]]=$value; |
||||||
|
else if(isset($this->params[$key])) |
||||||
|
$_REQUEST[$key]=$_GET[$key]=$value; |
||||||
|
} |
||||||
|
if($pathInfo!==$matches[0]) // there're additional GET params |
||||||
|
$manager->parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/')); |
||||||
|
if($this->routePattern!==null) |
||||||
|
return strtr($this->route,$tr); |
||||||
|
else |
||||||
|
return $this->route; |
||||||
|
} |
||||||
|
else |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue