Skip to content

Commit

Permalink
initial implementation of skin feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
qiang.xue committed Sep 13, 2009
1 parent 4b31cdd commit 768d676
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 9 deletions.
1 change: 1 addition & 0 deletions build/commands/AutoloadCommand.php
Expand Up @@ -56,6 +56,7 @@ public function run($args)
'/i18n/data',
'/utils/mimeTypes.php',
'/test',
'/zii',
),
);
$files=CFileHelper::findFiles(YII_PATH,$options);
Expand Down
1 change: 1 addition & 0 deletions framework/YiiBase.php
Expand Up @@ -628,6 +628,7 @@ public static function t($category,$message,$params=array(),$source=null,$langua
'CUrlManager' => '/web/CUrlManager.php',
'CWebApplication' => '/web/CWebApplication.php',
'CWebModule' => '/web/CWebModule.php',
'CWidgetFactory' => '/web/CWidgetFactory.php',
'CAction' => '/web/actions/CAction.php',
'CInlineAction' => '/web/actions/CInlineAction.php',
'CViewAction' => '/web/actions/CViewAction.php',
Expand Down
23 changes: 23 additions & 0 deletions framework/base/interfaces.php
Expand Up @@ -533,3 +533,26 @@ public function getEnabled();
*/
public function setEnabled($value);
}

/**
* IWidgetFactory is the interface that must be implemented by a widget factory class.
*
* When calling {@link CBaseController::createWidget}, if a widget factory is available,
* it will be used for creating the requested widget.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.1
*/
interface IWidgetFactory
{
/**
* Creates a new widget based on the given class name and initial properties.
* @param CBaseController the owner of the new widget
* @param string the class name of the widget. This can also be a path alias (e.g. system.web.widgets.COutputCache)
* @param array the initial property values (name=>value) of the widget.
* @return CWidget the newly created widget whose properties have been initialized with the given values.
*/
public function createWidget($owner,$className,$properties=array());
}
15 changes: 11 additions & 4 deletions framework/web/CBaseController.php
Expand Up @@ -128,16 +128,23 @@ public function renderInternal($_viewFile_,$_data_=null,$_return_=false)
* This method first creates the specified widget instance.
* It then configures the widget's properties with the given initial values.
* At the end it calls {@link CWidget::init} to initialize the widget.
* Starting from version 1.1, if a {@link CWidgetFactory widget factory} is enabled,
* this method will use the factory to create the widget, instead.
* @param string class name (can be in path alias format)
* @param array initial property values
* @return CWidget the fully initialized widget instance.
*/
public function createWidget($className,$properties=array())
{
$className=Yii::import($className,true);
$widget=new $className($this);
foreach($properties as $name=>$value)
$widget->$name=$value;
if(($factory=Yii::app()->getWidgetFactory())!==null)
$widget=$factory->createWidget($this,$className,$properties);
else
{
$className=Yii::import($className,true);
$widget=new $className($this);
foreach($properties as $name=>$value)
$widget->$name=$value;
}
$widget->init();
return $widget;
}
Expand Down
9 changes: 9 additions & 0 deletions framework/web/CTheme.php
Expand Up @@ -75,6 +75,15 @@ 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 the controller
Expand Down
14 changes: 14 additions & 0 deletions framework/web/CWebApplication.php
Expand Up @@ -154,6 +154,10 @@ protected function registerCoreComponents()
'clientScript'=>array(
'class'=>'CClientScript',
),
'widgetFactory'=>array(
'class'=>'CWidgetFactory',
'enabled'=>false,
),
);

$this->setComponents($components);
Expand Down Expand Up @@ -228,6 +232,16 @@ public function getClientScript()
return $this->getComponent('clientScript');
}

/**
* Returns the widget factory.
* @return IWidgetFactory the widget factory
* @since 1.1
*/
public function getWidgetFactory()
{
return $this->getComponent('widgetFactory');
}

/**
* @return CThemeManager the theme manager.
*/
Expand Down
142 changes: 142 additions & 0 deletions framework/web/CWidgetFactory.php
@@ -0,0 +1,142 @@
<?php
/**
* CWidgetFactory class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2009 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/


/**
* CWidgetFactory creates new widgets to be used in views.
*
* CWidgetFactory is used as the default "widgetFactory" application component.
*
* When calling {@link CBaseController::createWidget}, {@link CBaseController::widget}
* or {@link CBaseController::beginWidget}, if the "widgetFactory" component is enabled
* (disabled by default), it will be used to create the requested widget.
*
* CWidgetFactory implements the "skin" feature, which allows a new widget to be created
* and initialized with a set of predefined property values (called skin).
*
* When CWidgetFactory is used to create a new widget, it will first instantiate the
* widget instance. It then checks if there is a skin available for this widget
* according to the widget class name and the widget {@link CWidget::skin} property.
* If a skin is found, it will be merged with the initial properties passed via
* {@link createWidget}. Then the merged initial properties will be used to initialize
* the newly created widget instance.
*
* As aforementioned, a skin is a set of initial property values for a widget.
* It is thus represented as an associative array of name-value pairs.
* Skins are stored in PHP scripts like other configurations. Each script file stores the skins
* for a particular widget type and is named as the wiget class name (e.g. CLinkPager.php).
* Each widget type may have one or several skins, identified by the skin ID set via
* {@link CWidget::skin} property. If the {@link CWidget::skin} property is not set for a given
* widget, it means the default skin would be used. The following shows the possible skins for
* the {@link CLinkPager} widget:
* <pre>
* return array(
* 'default'=>array(
* 'nextPageLabel'=>'&gt;&gt;',
* 'prevPageLabel'=>'&lt;&lt;',
* ),
* 'short'=>array(
* 'header'=>'',
* 'maxButtonCount'=>5,
* ),
* );
* </pre>
* In the above, there are two skins. The first one is the default skin which is indexed by the string "default".
* Note that {@link CWidget::skin} defaults to "default". Therefore, this is the skin that will be applied
* if we do not explicitly specify the {@link CWidget::skin} property.
* The second one is named as the "short" skin which will be used only when we set {@link CWidget::skin}
* to be "short".
*
* By default, CWidgetFactory looks for the skin of a widget under the "skins" directory
* of the current application's {@link CWebApplication::viewPath} (e.g. protected/views/skins).
* If a theme is being used, it will look for the skin under the "skins" directory of
* the theme's {@link CTheme::viewPath}. In case the specified skin is not found, a widget will still be created
* normally without causing any error.
*
* Note that by default, the "widgetFactory" component is not enabled. It may be enabled
* by configuring the component in the application configuration like the following:
* <pre>
* return array(
* 'components'=>array(
* 'widgetFactory'=>array(
* 'enabled'=>true,
* ),
* ),
* )
* </pre>
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.web
* @since 1.1
*/
class CWidgetFactory extends CApplicationComponent implements IWidgetFactory
{
/**
* @var string the directory containing all the skin files. Defaults to null,
* meaning using the "skin" directory under the current application's {@link CWebApplication::viewPath}.
*/
public $skinPath;

private $_skins=array(); // class name, skin name, property name => value

/**
* Initializes the application component.
* This method overrides the parent implementation by resolving the skin path.
*/
public function init()
{
parent::init();

if(($theme=Yii::app()->getTheme())!==null)
$this->skinPath=$theme->getSkinPath();
else if($this->skinPath===null)
$this->skinPath=Yii::app()->getViewPath().DIRECTORY_SEPARATOR.'skins';
}

/**
* Creates a new widget based on the given class name and initial properties.
* @param CBaseController the owner of the new widget
* @param string the class name of the widget. This can also be a path alias (e.g. system.web.widgets.COutputCache)
* @param array the initial property values (name=>value) of the widget.
* @return CWidget the newly created widget whose properties have been initialized with the given values.
*/
public function createWidget($owner,$className,$properties=array())
{
$className=Yii::import($className,true);
$widget=new $className($this);
$skinName=isset($properties['skin']) ? $properties['skin'] : 'default';
if(($skin=$this->getSkin($className,$skinName))!==array())
$properties=$properties===array() ? $skin : CMap::mergeArray($skin,$properties);
foreach($properties as $name=>$value)
$widget->$name=$value;
}

/**
* Returns the skin for the specified widget class and skin name.
* @param string the widget class name
* @param string the widget skin name
* @return array the skin (name=>value) for the widget
*/
protected function getSkin($className,$skinName)
{
if(!isset($this->_skins[$className][$skinName]))
{
$skinFile=$this->skinPath.DIRECTORY_SEPARATOR.$className.'.php';
if(is_file($skinFile))
$this->_skins[$className]=require($skinFile);
else
$this->_skins[$className]=array();
if(!isset($this->_skins[$className][$skinName]))
$this->_skins[$className][$skinName]=array();
}
return $this->_skins[$className][$skinName];
}
}
6 changes: 6 additions & 0 deletions framework/web/widgets/CWidget.php
Expand Up @@ -33,6 +33,12 @@ class CWidget extends CBaseController
* @since 1.0.1
*/
public $actionPrefix;
/**
* @var string the name of the skin to be used by this widget. Defaults to 'default'.
* @see CWidgetFactory
* @since 1.1
*/
public $skin='default';

/**
* @var array view paths for different types of widgets
Expand Down
30 changes: 25 additions & 5 deletions framework/yiilite.php
Expand Up @@ -28,9 +28,10 @@
defined('YII_ENABLE_EXCEPTION_HANDLER') or define('YII_ENABLE_EXCEPTION_HANDLER',true);
defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER',true);
defined('YII_PATH') or define('YII_PATH',dirname(__FILE__));
defined('YII_ZII_PATH') or define('YII_ZII_PATH',YII_PATH.DIRECTORY_SEPARATOR.'zii');
class YiiBase
{
private static $_aliases=array('system'=>YII_PATH); // alias => path
private static $_aliases=array('system'=>YII_PATH,'zii'=>YII_ZII_PATH); // alias => path
private static $_imports=array(); // alias => class name or directory
private static $_classes=array();
private static $_includePaths; // list of include paths
Expand Down Expand Up @@ -402,6 +403,7 @@ public static function t($category,$message,$params=array(),$source=null,$langua
'CUrlManager' => '/web/CUrlManager.php',
'CWebApplication' => '/web/CWebApplication.php',
'CWebModule' => '/web/CWebModule.php',
'CWidgetFactory' => '/web/CWidgetFactory.php',
'CAction' => '/web/actions/CAction.php',
'CInlineAction' => '/web/actions/CInlineAction.php',
'CViewAction' => '/web/actions/CViewAction.php',
Expand Down Expand Up @@ -1352,6 +1354,10 @@ protected function registerCoreComponents()
'clientScript'=>array(
'class'=>'CClientScript',
),
'widgetFactory'=>array(
'class'=>'CWidgetFactory',
'enabled'=>false,
),
);
$this->setComponents($components);
}
Expand Down Expand Up @@ -1387,6 +1393,10 @@ public function getClientScript()
{
return $this->getComponent('clientScript');
}
public function getWidgetFactory()
{
return $this->getComponent('widgetFactory');
}
public function getThemeManager()
{
return $this->getComponent('themeManager');
Expand Down Expand Up @@ -2657,10 +2667,15 @@ public function renderInternal($_viewFile_,$_data_=null,$_return_=false)
}
public function createWidget($className,$properties=array())
{
$className=Yii::import($className,true);
$widget=new $className($this);
foreach($properties as $name=>$value)
$widget->$name=$value;
if(($factory=Yii::app()->getWidgetFactory())!==null)
$widget=$factory->createWidget($this,$className,$properties);
else
{
$className=Yii::import($className,true);
$widget=new $className($this);
foreach($properties as $name=>$value)
$widget->$name=$value;
}
$widget->init();
return $widget;
}
Expand Down Expand Up @@ -4473,6 +4488,7 @@ protected static function renderAttributes($htmlOptions)
class CWidget extends CBaseController
{
public $actionPrefix;
public $skin='default';
private static $_viewPaths;
private static $_counter=0;
private $_id;
Expand Down Expand Up @@ -7169,4 +7185,8 @@ public function detach($component);
public function getEnabled();
public function setEnabled($value);
}
interface IWidgetFactory
{
public function createWidget($owner,$className,$properties=array());
}
?>

0 comments on commit 768d676

Please sign in to comment.