diff --git a/CHANGELOG b/CHANGELOG index 0e3c41c94..8c704eb6c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,18 @@ Version 1.1a to be released Version 1.0.8 To be released ---------------------------- +- Bug #435: Setting charset for PostgreSQL database connection may fail to work on some servers (Qiang) +- Bug #440: typo in COciColumnSchema (Qiang) +- Bug #444: typo in blog demo (Qiang) +- Bug #446: Fixed bugs in MSSQL driver that cause problem when updating attribues and a column is of timestamp type (Qiang) +- Bug #451: CAssetManager::publish() may not report error when an asset is missing on BSD (Qiang) +- Bug: Fixed an issue in CNumberFormatter which incorrectly cached parsed formats (Qiang) +- New #436: Added error templates in Russian (idlesign) +- New: CDbCommand::bindParam() and bindValue() now return the command object so that they can be used in a chainable fashion (Qiang) +- New: Added thead and tbody to the code generated by crud command (Qiang) +- New: Added CFormModel::init() and CActiveRecord::init() (Qiang) +- New: Improved CFileHelper::getMimeType() so that it can return a meaningful result in most cases (Qiang) +- Chg: Modified the count() method of relational AR to support counting composite keys (Qiang) Version 1.0.7 July 5, 2009 -------------------------- diff --git a/demos/blog/protected/views/post/_post.php b/demos/blog/protected/views/post/_post.php index 6eefb21ca..ff0750ceb 100644 --- a/demos/blog/protected/views/post/_post.php +++ b/demos/blog/protected/views/post/_post.php @@ -24,6 +24,6 @@ 'confirm'=>"Are you sure to delete this post?", )); ?> | - Last updated on createTime); ?> + Last updated on updateTime); ?> diff --git a/docs/guide/basics.controller.txt b/docs/guide/basics.controller.txt index 64d384f8c..e4cb06d7c 100644 --- a/docs/guide/basics.controller.txt +++ b/docs/guide/basics.controller.txt @@ -35,7 +35,7 @@ name without the `action` prefix. For example, if a controller class contains a method named `actionEdit`, the ID of the corresponding action would be `edit`. -> Note: Before version 1.0.3, the controller ID format is `path.to.xyz` +> Note: Before version 1.0.3, the controller ID format was `path.to.xyz` instead of `path/to/xyz`. Users request for a particular controller and action in terms of route. A diff --git a/docs/guide/quickstart.first-app.txt b/docs/guide/quickstart.first-app.txt index 6a1312987..8830a5fc4 100644 --- a/docs/guide/quickstart.first-app.txt +++ b/docs/guide/quickstart.first-app.txt @@ -177,11 +177,13 @@ If you have a 'db' database connection, you can test it now with: >> crud User generate UserController.php - generate create.php mkdir D:/wwwroot/testdrive/protected/views/user + generate create.php generate update.php generate list.php generate show.php + generate admin.php + generate _form.php Crud 'user' has been successfully created. You may access it via: http://hostname/path/to/index.php?r=user diff --git a/framework/base/interfaces.php b/framework/base/interfaces.php index f1e6a5a14..34d55f123 100644 --- a/framework/base/interfaces.php +++ b/framework/base/interfaces.php @@ -448,10 +448,10 @@ interface IAuthManager */ public function getAuthAssignment($itemName,$userId); /** - * Revokes an authorization item assignment. - * @param string the item name + * Returns the item assignments for the specified user. * @param mixed the user ID (see {@link IWebUser::getId}) - * @return boolean whether removal is successful + * @return array the item assignment information for the user. An empty array will be + * returned if there is no item assigned to the user. */ public function getAuthAssignments($userId); /** diff --git a/framework/cli/commands/shell/CrudCommand.php b/framework/cli/commands/shell/CrudCommand.php index 0f019ce0d..4e650f2cb 100644 --- a/framework/cli/commands/shell/CrudCommand.php +++ b/framework/cli/commands/shell/CrudCommand.php @@ -128,7 +128,7 @@ EOD; $module=$m; else { - $controllerFile=$first.'/'.$controllerClass; + $controllerFile=$first.'/'.$controllerFile; $controllerID=$first.'/'.$controllerID; } diff --git a/framework/cli/views/shell/crud/admin.php b/framework/cli/views/shell/crud/admin.php index 116f1bc3f..039ee4438 100644 --- a/framework/cli/views/shell/crud/admin.php +++ b/framework/cli/views/shell/crud/admin.php @@ -15,6 +15,7 @@ + @@ -22,6 +23,8 @@ + + \$model): ?>\n"; ?> "; ?>"> @@ -37,6 +40,7 @@ \n"; ?> +
link('$ID'); ?>"; ?> Actions
{$ID},array('show','id'=>\$model->{$ID})); ?>"; ?>

widget('CLinkPager',array('pages'=>\$pages)); ?>" ?> diff --git a/framework/db/CDbCommand.php b/framework/db/CDbCommand.php index 5f608065e..76c2f00a9 100644 --- a/framework/db/CDbCommand.php +++ b/framework/db/CDbCommand.php @@ -138,6 +138,7 @@ class CDbCommand extends CComponent * @param mixed Name of the PHP variable to bind to the SQL statement parameter * @param int SQL data type of the parameter. If null, the type is determined by the PHP type of the value. * @param int length of the data type + * @return CDbCommand the current command being executed (this is available since version 1.0.8) * @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php */ public function bindParam($name, &$value, $dataType=null, $length=null) @@ -151,6 +152,7 @@ class CDbCommand extends CComponent $this->_statement->bindParam($name,$value,$dataType,$length); if($this->_connection->enableParamLogging) $this->_params[]=$name.'=['.gettype($value).']'; + return $this; } /** @@ -161,6 +163,7 @@ class CDbCommand extends CComponent * placeholders, this will be the 1-indexed position of the parameter. * @param mixed The value to bind to the parameter * @param int SQL data type of the parameter. If null, the type is determined by the PHP type of the value. + * @return CDbCommand the current command being executed (this is available since version 1.0.8) * @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php */ public function bindValue($name, $value, $dataType=null) @@ -172,6 +175,7 @@ class CDbCommand extends CComponent $this->_statement->bindValue($name,$value,$dataType); if($this->_connection->enableParamLogging) $this->_params[]=$name.'='.var_export($value,true); + return $this; } /** diff --git a/framework/db/CDbConnection.php b/framework/db/CDbConnection.php index f04d625b0..b09cefcf3 100644 --- a/framework/db/CDbConnection.php +++ b/framework/db/CDbConnection.php @@ -297,10 +297,7 @@ class CDbConnection extends CApplicationComponent if($this->charset!==null) { if(strcasecmp($pdo->getAttribute(PDO::ATTR_DRIVER_NAME),'sqlite')) - { - $stmt=$pdo->prepare('SET NAMES ?'); - $stmt->execute(array($this->charset)); - } + $pdo->exec('SET NAMES '.$pdo->quote($this->charset)); } } diff --git a/framework/db/ar/CActiveFinder.php b/framework/db/ar/CActiveFinder.php index ee2c05ab3..5c3b1d83d 100644 --- a/framework/db/ar/CActiveFinder.php +++ b/framework/db/ar/CActiveFinder.php @@ -501,18 +501,18 @@ class CJoinElement $this->_finder->joinAll=true; $this->buildQuery($query); - if(is_string($this->_table->primaryKey)) + if($criteria->select!=='*' && stripos($criteria->select,'count')!==false) + $query->selects=array($criteria->select); + else if(is_string($this->_table->primaryKey)) { $prefix=$this->getColumnPrefix(); $schema=$this->_builder->getSchema(); $column=$prefix.$schema->quoteColumnName($this->_table->primaryKey); + $query->selects=array("COUNT(DISTINCT $column)"); } - else if($criteria->select!=='*') - $column=$criteria->select; else - throw new CDbException(Yii::t('yii','Unable to count records with composite primary keys. Please explicitly specify the SELECT option in the query criteria.')); + $query->selects=array("COUNT(*)"); - $query->selects=array("COUNT(DISTINCT $column)"); $query->orders=$query->groups=$query->havings=array(); $command=$query->createCommand($this->_builder); return $command->queryScalar(); diff --git a/framework/db/ar/CActiveRecord.php b/framework/db/ar/CActiveRecord.php index 55a02130f..d90660495 100644 --- a/framework/db/ar/CActiveRecord.php +++ b/framework/db/ar/CActiveRecord.php @@ -57,10 +57,23 @@ abstract class CActiveRecord extends CModel $this->setIsNewRecord(true); $this->_attributes=$this->getMetaData()->attributeDefaults; + $this->init(); + $this->attachBehaviors($this->behaviors()); $this->afterConstruct(); } + /** + * Initializes this model. + * This method is invoked in the constructor right after {@link scenario} is set. + * You may override this method to provide code that is needed to initialize the model (e.g. setting + * initial property values.) + * @since 1.0.8 + */ + public function init() + { + } + /** * PHP sleep magic method. * This method ensures that the model meta data reference is set to null. diff --git a/framework/db/schema/mssql/CMssqlCommandBuilder.php b/framework/db/schema/mssql/CMssqlCommandBuilder.php index 989308d1e..99bbc3bcb 100644 --- a/framework/db/schema/mssql/CMssqlCommandBuilder.php +++ b/framework/db/schema/mssql/CMssqlCommandBuilder.php @@ -77,11 +77,13 @@ class CMssqlCommandBuilder extends CDbCommandBuilder $fields=array(); $values=array(); $bindByPosition=isset($criteria->params[0]); + $i=0; foreach($data as $name=>$value) { if(($column=$table->getColumn($name))!==null) { if ($table->sequenceName !== null && $column->isPrimaryKey === true) continue; + if ($column->dbType === 'timestamp') continue; if($value instanceof CDbExpression) $fields[]=$column->rawName.'='.(string)$value; else if($bindByPosition) @@ -91,8 +93,9 @@ class CMssqlCommandBuilder extends CDbCommandBuilder } else { - $fields[]=$column->rawName.'=:'.$name; - $values[':'.$name]=$column->typecast($value); + $fields[]=$column->rawName.'='.self::PARAM_PREFIX.$i; + $values[self::PARAM_PREFIX.$i]=$column->typecast($value); + $i++; } } } diff --git a/framework/db/schema/oci/COciColumnSchema.php b/framework/db/schema/oci/COciColumnSchema.php index 71bc94fd0..ab4acf0c2 100644 --- a/framework/db/schema/oci/COciColumnSchema.php +++ b/framework/db/schema/oci/COciColumnSchema.php @@ -46,7 +46,7 @@ class COciColumnSchema extends CDbColumnSchema protected function extractDefault($defaultValue) { - if(strpos($dbType,'timestamp')!==false) + if(stripos($defaultValue,'timestamp')!==false) $this->defaultValue=null; else parent::extractDefault($defaultValue); diff --git a/framework/db/schema/oci/COciCommandBuilder.php b/framework/db/schema/oci/COciCommandBuilder.php index c53a459b4..ac7d0839f 100644 --- a/framework/db/schema/oci/COciCommandBuilder.php +++ b/framework/db/schema/oci/COciCommandBuilder.php @@ -85,6 +85,7 @@ EOD; $fields=array(); $values=array(); $placeholders=array(); + $i=0; foreach($data as $name=>$value) { if(($column=$table->getColumn($name))!==null && ($value!==null || $column->allowNull)) @@ -94,8 +95,9 @@ EOD; $placeholders[]=(string)$value; else { - $placeholders[]=':'.$name; - $values[':'.$name]=$column->typecast($value); + $placeholders[]=self::PARAM_PREFIX.$i; + $values[self::PARAM_PREFIX.$i]=$column->typecast($value); + $i++; } } } diff --git a/framework/i18n/CNumberFormatter.php b/framework/i18n/CNumberFormatter.php index 716bd566f..f0812c121 100644 --- a/framework/i18n/CNumberFormatter.php +++ b/framework/i18n/CNumberFormatter.php @@ -61,6 +61,7 @@ class CNumberFormatter extends CComponent { private $_locale; + private $_formats=array(); /** * Constructor. @@ -202,9 +203,8 @@ class CNumberFormatter extends CComponent */ protected function parseFormat($pattern) { - static $formats=array(); // cache - if(isset($formats[$pattern])) - return $formats[$pattern]; + if(isset($this->_formats[$pattern])) + return $this->_formats[$pattern]; $format=array(); @@ -227,28 +227,28 @@ class CNumberFormatter extends CComponent $format['negativePrefix']=$this->_locale->getNumberSymbol('minusSign').$format['positivePrefix']; $format['negativeSuffix']=$format['positiveSuffix']; } - $pattern=$patterns[0]; + $pat=$patterns[0]; // find out multiplier - if(strpos($pattern,'%')!==false) + if(strpos($pat,'%')!==false) $format['multiplier']=100; - else if(strpos($pattern,'‰')!==false) + else if(strpos($pat,'‰')!==false) $format['multiplier']=1000; else $format['multiplier']=1; // find out things about decimal part - if(($pos=strpos($pattern,'.'))!==false) + if(($pos=strpos($pat,'.'))!==false) { - if(($pos2=strrpos($pattern,'0'))>$pos) + if(($pos2=strrpos($pat,'0'))>$pos) $format['decimalDigits']=$pos2-$pos; else $format['decimalDigits']=0; - if(($pos3=strrpos($pattern,'#'))>=$pos2) + if(($pos3=strrpos($pat,'#'))>=$pos2) $format['maxDecimalDigits']=$pos3-$pos; else $format['maxDecimalDigits']=$format['decimalDigits']; - $pattern=substr($pattern,0,$pos); + $pat=substr($pat,0,$pos); } else // no decimal part { @@ -257,14 +257,14 @@ class CNumberFormatter extends CComponent } // find out things about integer part - $p=str_replace(',','',$pattern); + $p=str_replace(',','',$pat); if(($pos=strpos($p,'0'))!==false) $format['integerDigits']=strrpos($p,'0')-$pos+1; else $format['integerDigits']=0; // find out group sizes. some patterns may have two different group sizes - $p=str_replace('#','0',$pattern); - if(($pos=strrpos($pattern,','))!==false) + $p=str_replace('#','0',$pat); + if(($pos=strrpos($pat,','))!==false) { $format['groupSize1']=strrpos($p,'0')-$pos; if(($pos2=strrpos(substr($p,0,$pos),','))!==false) @@ -275,6 +275,6 @@ class CNumberFormatter extends CComponent else $format['groupSize1']=$format['groupSize2']=0; - return $formats[$pattern]=$format; + return $this->_formats[$pattern]=$format; } } \ No newline at end of file diff --git a/framework/utils/CFileHelper.php b/framework/utils/CFileHelper.php index a2da7c675..e810724fb 100644 --- a/framework/utils/CFileHelper.php +++ b/framework/utils/CFileHelper.php @@ -196,12 +196,12 @@ class CFileHelper { if(function_exists('finfo_open')) { - if($info=finfo_open(FILEINFO_MIME)) - return finfo_file($info,$file); + if(($info=finfo_open(FILEINFO_MIME)) && ($result=finfo_file($info,$file))!==false) + return $result; } - if(function_exists('mime_content_type')) - return mime_content_type($file); + if(function_exists('mime_content_type') && ($result=mime_content_type($file))!==false) + return $result; return self::getMimeTypeByExtension($file); } diff --git a/framework/views/ru/error.php b/framework/views/ru/error.php new file mode 100644 index 000000000..272a09825 --- /dev/null +++ b/framework/views/ru/error.php @@ -0,0 +1,37 @@ + + + + +Ошибка <?php echo $data['code']; ?> + + + + + +

Ошибка

+

+

+При обработке веб-сервером вашего запроса произошла указанная выше ошибка. +

+

+Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

+

+Спасибо. +

+
+ +
+ + \ No newline at end of file diff --git a/framework/views/ru/error400.php b/framework/views/ru/error400.php new file mode 100644 index 000000000..e2d7edd38 --- /dev/null +++ b/framework/views/ru/error400.php @@ -0,0 +1,33 @@ + + + + +Ошибка в запросе + + + +

Ошибка в запросе

+

+

+В запросе обнаружена синтаксическая ошибка, по причине которой сервер не смог обработать этот запрос. +Следует исправить ошибку, после чего повторить запрос. +

+

+Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

+
+ +
+ + \ No newline at end of file diff --git a/framework/views/ru/error403.php b/framework/views/ru/error403.php new file mode 100644 index 000000000..cc64812ec --- /dev/null +++ b/framework/views/ru/error403.php @@ -0,0 +1,32 @@ + + + + +Ошибка авторизации + + + +

Ошибка авторизации

+

+

+У вас недостаточно прав для доступа к запрошенной странице. +

+

+Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

+
+ +
+ + \ No newline at end of file diff --git a/framework/views/ru/error404.php b/framework/views/ru/error404.php new file mode 100644 index 000000000..1dec89f76 --- /dev/null +++ b/framework/views/ru/error404.php @@ -0,0 +1,33 @@ + + + + +Страница не найдена + + + +

Страница не найдена

+

+

+Запрошенная страница не найдена на сервере. +Если вы ввели адрес страницы вручную, проверьте правильность его написания и повторите попытку. +

+

+Если вы считаете, что это ошибка настройки сервера, обратитесь по адресу . +

+
+ +
+ + \ No newline at end of file diff --git a/framework/views/ru/error500.php b/framework/views/ru/error500.php new file mode 100644 index 000000000..178f00120 --- /dev/null +++ b/framework/views/ru/error500.php @@ -0,0 +1,35 @@ + + + + +Внутренняя ошибка сервера + + + + + +

Внутренняя ошибка сервера

+

+

+В ходе обработки вашего запроса произошла внутренняя ошибка сервера. +Вы можете сообщить об ошибке по адресу . +

+

+Спасибо. +

+
+ +
+ + \ No newline at end of file diff --git a/framework/views/ru/error503.php b/framework/views/ru/error503.php new file mode 100644 index 000000000..06bf6629b --- /dev/null +++ b/framework/views/ru/error503.php @@ -0,0 +1,31 @@ + + + + +Служба временно недоступна + + + +

Служба временно недоступна

+

+В настоящий момент производится техническое обслуживание системы. Возвращайтесь позже. +

+

+Спасибо. +

+
+ +
+ + \ No newline at end of file diff --git a/framework/views/ru/exception.php b/framework/views/ru/exception.php new file mode 100644 index 000000000..323c5fa2b --- /dev/null +++ b/framework/views/ru/exception.php @@ -0,0 +1,74 @@ + + + + + +<?php echo CHtml::encode($data['type']); ?> + + + + + + +

+ +

Описание

+

+ +

+ +

Исходный код

+

+ +

+ +
+
+$code)
+	{
+		if($line!==$data['line'])
+			echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",'    ',$code)));
+		else
+		{
+			echo "
"; + echo CHtml::encode(sprintf("%05d: %s",$line,str_replace("\t",' ',$code))); + echo "
"; + } + } +} +?> +
+
+ +

Содержимое стека

+
+
+
+
+
+ +
+ +
+ + \ No newline at end of file diff --git a/framework/views/ru/log-firebug.php b/framework/views/ru/log-firebug.php new file mode 100644 index 000000000..3d8b745d6 --- /dev/null +++ b/framework/views/ru/log-firebug.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/framework/views/ru/log.php b/framework/views/ru/log.php new file mode 100644 index 000000000..7830b4710 --- /dev/null +++ b/framework/views/ru/log.php @@ -0,0 +1,32 @@ + + + + + + + + + + + +$log) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $message='
'.CHtml::encode(wordwrap($log[0])).'
'; + $time=date('H:i:s.',$log[3]).(int)(($log[3]-(int)$log[3])*1000000); + + echo << + + + + + +EOD; +} +?> +
+ Журнал приложения +
ВремяУровеньКатегорияСообщение
{$time}{$log[1]}{$log[2]}{$message}
+ \ No newline at end of file diff --git a/framework/views/ru/profile-callstack-firebug.php b/framework/views/ru/profile-callstack-firebug.php new file mode 100644 index 000000000..72aaf78de --- /dev/null +++ b/framework/views/ru/profile-callstack-firebug.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/framework/views/ru/profile-callstack.php b/framework/views/ru/profile-callstack.php new file mode 100644 index 000000000..42aaf2a82 --- /dev/null +++ b/framework/views/ru/profile-callstack.php @@ -0,0 +1,30 @@ + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + list($proc,$time,$level)=$entry; + $proc=CHtml::encode($proc); + $time=sprintf('%0.5f',$time); + $spaces=str_repeat(' ',$level*8); + + echo << + + + +EOD; +} +?> +
+ Стек вызовов +
ПроцедураВремя (с)
{$spaces}{$proc}{$time}
+ \ No newline at end of file diff --git a/framework/views/ru/profile-summary-firebug.php b/framework/views/ru/profile-summary-firebug.php new file mode 100644 index 000000000..a72bc2c1a --- /dev/null +++ b/framework/views/ru/profile-summary-firebug.php @@ -0,0 +1,22 @@ + diff --git a/framework/views/ru/profile-summary.php b/framework/views/ru/profile-summary.php new file mode 100644 index 000000000..1200cb0ce --- /dev/null +++ b/framework/views/ru/profile-summary.php @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +$entry) +{ + $color=($index%2)?'#F5F5F5':'#EBF8FE'; + $proc=CHtml::encode($entry[0]); + $min=sprintf('%0.5f',$entry[2]); + $max=sprintf('%0.5f',$entry[3]); + $total=sprintf('%0.5f',$entry[4]); + $average=sprintf('%0.5f',$entry[4]/$entry[1]); + + echo << + + + + + + + +EOD; +} +?> +
+ Общий отчет + (Время: getExecutionTime()); ?>с, + Память: getMemoryUsage()/1024); ?>Кб) +
ПроцедураНомерВсего (с)Средн. (с)Мин. (с)Макс. (с)
{$proc}{$entry[1]}{$total}{$average}{$min}{$max}
+ \ No newline at end of file diff --git a/framework/web/CAssetManager.php b/framework/web/CAssetManager.php index 94c4807ea..6033eb00f 100644 --- a/framework/web/CAssetManager.php +++ b/framework/web/CAssetManager.php @@ -148,7 +148,7 @@ class CAssetManager extends CApplicationComponent return $this->_published[$path]=$this->getBaseUrl()."/$dir/$fileName"; } - else + else if(is_dir($src)) { $dir=$this->hash($hashByName ? basename($src) : $src); $dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir; @@ -159,9 +159,8 @@ class CAssetManager extends CApplicationComponent return $this->_published[$path]=$this->getBaseUrl().'/'.$dir; } } - else - throw new CException(Yii::t('yii','The asset "{asset}" to be published does not exist.', - array('{asset}'=>$path))); + throw new CException(Yii::t('yii','The asset "{asset}" to be published does not exist.', + array('{asset}'=>$path))); } /** diff --git a/framework/web/CFormModel.php b/framework/web/CFormModel.php index b4ca9e4e5..d4a933b96 100644 --- a/framework/web/CFormModel.php +++ b/framework/web/CFormModel.php @@ -37,9 +37,21 @@ class CFormModel extends CModel public function __construct($scenario='') { $this->setScenario($scenario); + $this->init(); $this->attachBehaviors($this->behaviors()); } + /** + * Initializes this model. + * This method is invoked in the constructor right after {@link scenario} is set. + * You may override this method to provide code that is needed to initialize the model (e.g. setting + * initial property values.) + * @since 1.0.8 + */ + public function init() + { + } + /** * Returns the list of attribute names. * By default, this method returns all public properties of the class. diff --git a/framework/web/helpers/CHtml.php b/framework/web/helpers/CHtml.php index 6fc71c724..562aed0ec 100644 --- a/framework/web/helpers/CHtml.php +++ b/framework/web/helpers/CHtml.php @@ -635,7 +635,7 @@ class CHtml /** * Generates a list box. * @param string the input name - * @param string the selected value + * @param mixed the selected value(s). This can be either a string for single selection or an array for multiple selections. * @param array data for generating the list options (value=>display) * You may use {@link listData} to generate this data. * Please refer to {@link listOptions} on how this data is used to generate the list options.