CWidgetFactory.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. /**
  3. * CWidgetFactory class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CWidgetFactory creates new widgets to be used in views.
  12. *
  13. * CWidgetFactory is used as the default "widgetFactory" application component.
  14. *
  15. * When calling {@link CBaseController::createWidget}, {@link CBaseController::widget}
  16. * or {@link CBaseController::beginWidget}, if the "widgetFactory" component is installed,
  17. * it will be used to create the requested widget. To install the "widgetFactory" component,
  18. * we should have the following application configuration:
  19. * <pre>
  20. * return array(
  21. * 'components'=>array(
  22. * 'widgetFactory'=>array(
  23. * 'class'=>'CWidgetFactory',
  24. * ),
  25. * ),
  26. * )
  27. * </pre>
  28. *
  29. * CWidgetFactory implements the "skin" feature, which allows a new widget to be created
  30. * and initialized with a set of predefined property values (called skin).
  31. *
  32. * When CWidgetFactory is used to create a new widget, it will first instantiate the
  33. * widget instance. It then checks if there is a skin available for this widget
  34. * according to the widget class name and the widget {@link CWidget::skin} property.
  35. * If a skin is found, it will be merged with the initial properties passed via
  36. * {@link createWidget}. Then the merged initial properties will be used to initialize
  37. * the newly created widget instance.
  38. *
  39. * As aforementioned, a skin is a set of initial property values for a widget.
  40. * It is thus represented as an associative array of name-value pairs.
  41. * Skins are stored in PHP scripts like other configurations. Each script file stores the skins
  42. * for a particular widget type and is named as the widget class name (e.g. CLinkPager.php).
  43. * Each widget type may have one or several skins, identified by the skin name set via
  44. * {@link CWidget::skin} property. If the {@link CWidget::skin} property is not set for a given
  45. * widget, it means the default skin would be used. The following shows the possible skins for
  46. * the {@link CLinkPager} widget:
  47. * <pre>
  48. * return array(
  49. * 'default'=>array(
  50. * 'nextPageLabel'=>'&gt;&gt;',
  51. * 'prevPageLabel'=>'&lt;&lt;',
  52. * ),
  53. * 'short'=>array(
  54. * 'header'=>'',
  55. * 'maxButtonCount'=>5,
  56. * ),
  57. * );
  58. * </pre>
  59. * In the above, there are two skins. The first one is the default skin which is indexed by the string "default".
  60. * Note that {@link CWidget::skin} defaults to "default". Therefore, this is the skin that will be applied
  61. * if we do not explicitly specify the {@link CWidget::skin} property.
  62. * The second one is named as the "short" skin which will be used only when we set {@link CWidget::skin}
  63. * to be "short".
  64. *
  65. * By default, CWidgetFactory looks for the skin of a widget under the "skins" directory
  66. * of the current application's {@link CWebApplication::viewPath} (e.g. protected/views/skins).
  67. * If a theme is being used, it will look for the skin under the "skins" directory of
  68. * the theme's {@link CTheme::viewPath} (as well as the aforementioned skin directory).
  69. * In case the specified skin is not found, a widget will still be created
  70. * normally without causing any error.
  71. *
  72. * @author Qiang Xue <qiang.xue@gmail.com>
  73. * @package system.web
  74. * @since 1.1
  75. */
  76. class CWidgetFactory extends CApplicationComponent implements IWidgetFactory
  77. {
  78. /**
  79. * @var boolean whether to enable widget skinning. Defaults to false.
  80. * @see skinnableWidgets
  81. * @since 1.1.3
  82. */
  83. public $enableSkin=false;
  84. /**
  85. * @var array widget initial property values. Each array key-value pair
  86. * represents the initial property values for a single widget class, with
  87. * the array key being the widget class name, and array value being the initial
  88. * property value array. For example,
  89. * <pre>
  90. * array(
  91. * 'CLinkPager'=>array(
  92. * 'maxButtonCount'=>5,
  93. * 'cssFile'=>false,
  94. * ),
  95. * 'CJuiDatePicker'=>array(
  96. * 'language'=>'ru',
  97. * ),
  98. * )
  99. * </pre>
  100. *
  101. * Note that the initial values specified here may be overridden by
  102. * the values given in {@link CBaseController::createWidget} calls.
  103. * They may also be overridden by widget skins, if {@link enableSkin} is true.
  104. * @since 1.1.3
  105. */
  106. public $widgets=array();
  107. /**
  108. * @var array list of widget class names that can be skinned.
  109. * Because skinning widgets has performance impact, you may want to specify this property
  110. * to limit skinning only to specific widgets. Any widgets that are not in this list
  111. * will not be skinned. Defaults to null, meaning all widgets can be skinned.
  112. * @since 1.1.3
  113. */
  114. public $skinnableWidgets;
  115. /**
  116. * @var string the directory containing all the skin files. Defaults to null,
  117. * meaning using the "skins" directory under the current application's {@link CWebApplication::viewPath}.
  118. */
  119. public $skinPath;
  120. private $_skins=array(); // class name, skin name, property name => value
  121. /**
  122. * Initializes the application component.
  123. * This method overrides the parent implementation by resolving the skin path.
  124. */
  125. public function init()
  126. {
  127. parent::init();
  128. if($this->enableSkin && $this->skinPath===null)
  129. $this->skinPath=Yii::app()->getViewPath().DIRECTORY_SEPARATOR.'skins';
  130. }
  131. /**
  132. * Creates a new widget based on the given class name and initial properties.
  133. * @param CBaseController $owner the owner of the new widget
  134. * @param string $className the class name of the widget. This can also be a path alias (e.g. system.web.widgets.COutputCache)
  135. * @param array $properties the initial property values (name=>value) of the widget.
  136. * @return CWidget the newly created widget whose properties have been initialized with the given values.
  137. */
  138. public function createWidget($owner,$className,$properties=array())
  139. {
  140. $className=Yii::import($className,true);
  141. $widget=new $className($owner);
  142. if(isset($this->widgets[$className]))
  143. $properties=$properties===array() ? $this->widgets[$className] : CMap::mergeArray($this->widgets[$className],$properties);
  144. if($this->enableSkin)
  145. {
  146. if($this->skinnableWidgets===null || in_array($className,$this->skinnableWidgets))
  147. {
  148. $skinName=isset($properties['skin']) ? $properties['skin'] : 'default';
  149. if($skinName!==false && ($skin=$this->getSkin($className,$skinName))!==array())
  150. $properties=$properties===array() ? $skin : CMap::mergeArray($skin,$properties);
  151. }
  152. }
  153. foreach($properties as $name=>$value)
  154. $widget->$name=$value;
  155. return $widget;
  156. }
  157. /**
  158. * Returns the skin for the specified widget class and skin name.
  159. * @param string $className the widget class name
  160. * @param string $skinName the widget skin name
  161. * @return array the skin (name=>value) for the widget
  162. */
  163. protected function getSkin($className,$skinName)
  164. {
  165. if(!isset($this->_skins[$className][$skinName]))
  166. {
  167. $skinFile=$this->skinPath.DIRECTORY_SEPARATOR.$className.'.php';
  168. if(is_file($skinFile))
  169. $this->_skins[$className]=require($skinFile);
  170. else
  171. $this->_skins[$className]=array();
  172. if(($theme=Yii::app()->getTheme())!==null)
  173. {
  174. $skinFile=$theme->getSkinPath().DIRECTORY_SEPARATOR.$className.'.php';
  175. if(is_file($skinFile))
  176. {
  177. $skins=require($skinFile);
  178. foreach($skins as $name=>$skin)
  179. $this->_skins[$className][$name]=$skin;
  180. }
  181. }
  182. if(!isset($this->_skins[$className][$skinName]))
  183. $this->_skins[$className][$skinName]=array();
  184. }
  185. return $this->_skins[$className][$skinName];
  186. }
  187. }