summaryrefslogtreecommitdiff
path: root/framework/web/widgets
diff options
context:
space:
mode:
authorPatrick Seeger <pseeger@ccwn.org>2012-04-13 23:11:05 +0200
committerPatrick Seeger <pseeger@ccwn.org>2012-04-13 23:11:05 +0200
commit341cc4dd9c53ffbfb863e026dd58549c1082c7a7 (patch)
tree1bbbed20313bafb9b063b6b4d894fe580d8b000f /framework/web/widgets
yii-framework 1.1.10 hinzugefügtHEADmaster
Diffstat (limited to 'framework/web/widgets')
-rw-r--r--framework/web/widgets/CActiveForm.php787
-rw-r--r--framework/web/widgets/CAutoComplete.php291
-rw-r--r--framework/web/widgets/CClipWidget.php53
-rw-r--r--framework/web/widgets/CContentDecorator.php82
-rw-r--r--framework/web/widgets/CFilterWidget.php75
-rw-r--r--framework/web/widgets/CFlexWidget.php122
-rw-r--r--framework/web/widgets/CHtmlPurifier.php82
-rw-r--r--framework/web/widgets/CInputWidget.php81
-rw-r--r--framework/web/widgets/CMarkdown.php118
-rw-r--r--framework/web/widgets/CMaskedTextField.php113
-rw-r--r--framework/web/widgets/CMultiFileUpload.php142
-rw-r--r--framework/web/widgets/COutputCache.php347
-rw-r--r--framework/web/widgets/COutputProcessor.php77
-rw-r--r--framework/web/widgets/CStarRating.php217
-rw-r--r--framework/web/widgets/CTabView.php212
-rw-r--r--framework/web/widgets/CTextHighlighter.php125
-rw-r--r--framework/web/widgets/CTreeView.php246
-rw-r--r--framework/web/widgets/CWidget.php249
-rw-r--r--framework/web/widgets/captcha/CCaptcha.php171
-rw-r--r--framework/web/widgets/captcha/CCaptchaAction.php272
-rw-r--r--framework/web/widgets/captcha/Duality.ttfbin0 -> 50560 bytes
-rw-r--r--framework/web/widgets/pagers/CBasePager.php135
-rw-r--r--framework/web/widgets/pagers/CLinkPager.php195
-rw-r--r--framework/web/widgets/pagers/CListPager.php89
-rw-r--r--framework/web/widgets/pagers/pager.css67
-rw-r--r--framework/web/widgets/views/flexWidget.php100
26 files changed, 4448 insertions, 0 deletions
diff --git a/framework/web/widgets/CActiveForm.php b/framework/web/widgets/CActiveForm.php
new file mode 100644
index 0000000..76a49b7
--- /dev/null
+++ b/framework/web/widgets/CActiveForm.php
@@ -0,0 +1,787 @@
+<?php
+/**
+ * CActiveForm class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CActiveForm provides a set of methods that can help to simplify the creation
+ * of complex and interactive HTML forms that are associated with data models.
+ *
+ * The 'beginWidget' and 'endWidget' call of CActiveForm widget will render
+ * the open and close form tags. Most other methods of CActiveForm are wrappers
+ * of the corresponding 'active' methods in {@link CHtml}. Calling them in between
+ * the 'beginWidget' and 'endWidget' calls will render text labels, input fields,
+ * etc. For example, calling {@link CActiveForm::textField}
+ * would generate an input field for a specified model attribute.
+ *
+ * What makes CActiveForm extremely useful is its support for data validation.
+ * CActiveForm supports data validation at three levels:
+ * <ul>
+ * <li>server-side validation: the validation is performed at server side after
+ * the whole page containing the form is submitted. If there is any validation error,
+ * CActiveForm will render the error in the page back to user.</li>
+ * <li>AJAX-based validation: when the user enters data into an input field,
+ * an AJAX request is triggered which requires server-side validation. The validation
+ * result is sent back in AJAX response and the input field changes its appearance
+ * accordingly.</li>
+ * <li>client-side validation (available since version 1.1.7):
+ * when the user enters data into an input field,
+ * validation is performed on the client side using JavaScript. No server contact
+ * will be made, which reduces the workload on the server.</li>
+ * </ul>
+ *
+ * All these validations share the same set of validation rules declared in
+ * the associated model class. CActiveForm is designed in such a way that
+ * all these validations will lead to the same user interface changes and error
+ * message content.
+ *
+ * To ensure data validity, server-side validation is always performed.
+ * By setting {@link enableAjaxValidation} to true, one can enable AJAX-based validation;
+ * and by setting {@link enableClientValidation} to true, one can enable client-side validation.
+ * Note that in order to make the latter two validations work, the user's browser
+ * must has its JavaScript enabled. If not, only the server-side validation will
+ * be performed.
+ *
+ * The AJAX-based validation and client-side validation may be used together
+ * or separately. For example, in a user registration form, one may use AJAX-based
+ * validation to check if the user has picked a unique username, and use client-side
+ * validation to ensure all required fields are entered with data.
+ * Because the AJAX-based validation may bring extra workload on the server,
+ * if possible, one should mainly use client-side validation.
+ *
+ * The AJAX-based validation has a few limitations. First, it does not work
+ * with file upload fields. Second, it should not be used to perform validations that
+ * may cause server-side state changes. Third, it is not designed
+ * to work with tabular data input for the moment.
+ *
+ * Support for client-side validation varies for different validators. A validator
+ * will support client-side validation only if it implements {@link CValidator::clientValidateAttribute}
+ * and has its {@link CValidator::enableClientValidation} property set true.
+ * At this moment, the following core validators support client-side validation:
+ * <ul>
+ * <li>{@link CBooleanValidator}</li>
+ * <li>{@link CCaptchaValidator}</li>
+ * <li>{@link CCompareValidator}</li>
+ * <li>{@link CEmailValidator}</li>
+ * <li>{@link CNumberValidator}</li>
+ * <li>{@link CRangeValidator}</li>
+ * <li>{@link CRegularExpressionValidator}</li>
+ * <li>{@link CRequiredValidator}</li>
+ * <li>{@link CStringValidator}</li>
+ * <li>{@link CUrlValidator}</li>
+ * </ul>
+ *
+ * CActiveForm relies on CSS to customize the appearance of input fields
+ * which are in different validation states. In particular, each input field
+ * may be one of the four states: initial (not validated),
+ * validating, error and success. To differentiate these states, CActiveForm
+ * automatically assigns different CSS classes for the last three states
+ * to the HTML element containing the input field.
+ * By default, these CSS classes are named as 'validating', 'error' and 'success',
+ * respectively. We may customize these CSS classes by configuring the
+ * {@link clientOptions} property or specifying in the {@link error} method.
+ *
+ * The following is a piece of sample view code showing how to use CActiveForm:
+ *
+ * <pre>
+ * <?php $form = $this->beginWidget('CActiveForm', array(
+ * 'id'=>'user-form',
+ * 'enableAjaxValidation'=>true,
+ * 'enableClientValidation'=>true,
+ * 'focus'=>array($model,'firstName'),
+ * )); ?>
+ *
+ * <?php echo $form->errorSummary($model); ?>
+ *
+ * <div class="row">
+ * <?php echo $form->labelEx($model,'firstName'); ?>
+ * <?php echo $form->textField($model,'firstName'); ?>
+ * <?php echo $form->error($model,'firstName'); ?>
+ * </div>
+ * <div class="row">
+ * <?php echo $form->labelEx($model,'lastName'); ?>
+ * <?php echo $form->textField($model,'lastName'); ?>
+ * <?php echo $form->error($model,'lastName'); ?>
+ * </div>
+ *
+ * <?php $this->endWidget(); ?>
+ * </pre>
+ *
+ * To respond to the AJAX validation requests, we need the following class code:
+ * <pre>
+ * public function actionCreate()
+ * {
+ * $model=new User;
+ * $this->performAjaxValidation($model);
+ * if(isset($_POST['User']))
+ * {
+ * $model->attributes=$_POST['User'];
+ * if($model->save())
+ * $this->redirect('index');
+ * }
+ * $this->render('create',array('model'=>$model));
+ * }
+ *
+ * protected function performAjaxValidation($model)
+ * {
+ * if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
+ * {
+ * echo CActiveForm::validate($model);
+ * Yii::app()->end();
+ * }
+ * }
+ * </pre>
+ *
+ * In the above code, if we do not enable the AJAX-based validation, we can remove
+ * the <code>performAjaxValidation</code> method and its invocation.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CActiveForm.php 3542 2012-01-17 12:46:49Z mdomba $
+ * @package system.web.widgets
+ * @since 1.1.1
+ */
+class CActiveForm extends CWidget
+{
+ /**
+ * @var mixed the form action URL (see {@link CHtml::normalizeUrl} for details about this parameter).
+ * If not set, the current page URL is used.
+ */
+ public $action='';
+ /**
+ * @var string the form submission method. This should be either 'post' or 'get'.
+ * Defaults to 'post'.
+ */
+ public $method='post';
+ /**
+ * @var boolean whether to generate a stateful form (See {@link CHtml::statefulForm}). Defaults to false.
+ */
+ public $stateful=false;
+ /**
+ * @var string the CSS class name for error messages. Defaults to 'errorMessage'.
+ * Individual {@link error} call may override this value by specifying the 'class' HTML option.
+ */
+ public $errorMessageCssClass='errorMessage';
+ /**
+ * @var array additional HTML attributes that should be rendered for the form tag.
+ */
+ public $htmlOptions=array();
+ /**
+ * @var array the options to be passed to the javascript validation plugin.
+ * The following options are supported:
+ * <ul>
+ * <li>ajaxVar: string, the name of the parameter indicating the request is an AJAX request.
+ * When the AJAX validation is triggered, a parameter named as this property will be sent
+ * together with the other form data to the server. The parameter value is the form ID.
+ * The server side can then detect who triggers the AJAX validation and react accordingly.
+ * Defaults to 'ajax'.</li>
+ * <li>validationUrl: string, the URL that performs the AJAX validations.
+ * If not set, it will take the value of {@link action}.</li>
+ * <li>validationDelay: integer, the number of milliseconds that an AJAX validation should be
+ * delayed after an input is changed. A value 0 means the validation will be triggered immediately
+ * when an input is changed. A value greater than 0 means changing several inputs may only
+ * trigger a single validation if they happen fast enough, which may help reduce the server load.
+ * Defaults to 200 (0.2 second).</li>
+ * <li>validateOnSubmit: boolean, whether to perform AJAX validation when the form is being submitted.
+ * If there are any validation errors, the form submission will be stopped.
+ * Defaults to false.</li>
+ * <li>validateOnChange: boolean, whether to trigger an AJAX validation
+ * each time when an input's value is changed. You may want to turn this off
+ * if it causes too much performance impact, because each AJAX validation request
+ * will submit the data of the whole form. Defaults to true.</li>
+ * <li>validateOnType: boolean, whether to trigger an AJAX validation each time when the user
+ * presses a key. When setting this property to be true, you should tune up the 'validationDelay'
+ * option to avoid triggering too many AJAX validations. Defaults to false.</li>
+ * <li>hideErrorMessage: boolean, whether to hide the error message even if there is an error.
+ * Defaults to false, which means the error message will show up whenever the input has an error.</li>
+ * <li>inputContainer: string, the jQuery selector for the HTML element containing the input field.
+ * During the validation process, CActiveForm will set different CSS class for the container element
+ * to indicate the state change. If not set, it means the closest 'div' element that contains the input field.</li>
+ * <li>errorCssClass: string, the CSS class to be assigned to the container whose associated input
+ * has AJAX validation error. Defaults to 'error'.</li>
+ * <li>successCssClass: string, the CSS class to be assigned to the container whose associated input
+ * passes AJAX validation without any error. Defaults to 'success'.</li>
+ * <li>validatingCssClass: string, the CSS class to be assigned to the container whose associated input
+ * is currently being validated via AJAX. Defaults to 'validating'.</li>
+ * <li>errorMessageCssClass: string, the CSS class assigned to the error messages returned
+ * by AJAX validations. Defaults to 'errorMessage'.</li>
+ * <li>beforeValidate: function, the function that will be invoked before performing ajax-based validation
+ * triggered by form submission action (available only when validateOnSubmit is set true).
+ * The expected function signature should be <code>beforeValidate(form) {...}</code>, where 'form' is
+ * the jquery representation of the form object. If the return value of this function is NOT true, the validation
+ * will be cancelled.
+ *
+ * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
+ * from being encoded as a string. This option has been available since version 1.1.3.</li>
+ * <li>afterValidate: function, the function that will be invoked after performing ajax-based validation
+ * triggered by form submission action (available only when validateOnSubmit is set true).
+ * The expected function signature should be <code>afterValidate(form, data, hasError) {...}</code>, where 'form' is
+ * the jquery representation of the form object; 'data' is the JSON response from the server-side validation; 'hasError'
+ * is a boolean value indicating whether there is any validation error. If the return value of this function is NOT true,
+ * the normal form submission will be cancelled.
+ *
+ * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
+ * from being encoded as a string. This option has been available since version 1.1.3.</li>
+ * <li>beforeValidateAttribute: function, the function that will be invoked before performing ajax-based validation
+ * triggered by a single attribute input change. The expected function signature should be
+ * <code>beforeValidateAttribute(form, attribute) {...}</code>, where 'form' is the jquery representation of the form object
+ * and 'attribute' refers to the js options for the triggering attribute (see {@link error}).
+ * If the return value of this function is NOT true, the validation will be cancelled.
+ *
+ * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
+ * from being encoded as a string. This option has been available since version 1.1.3.</li>
+ * <li>afterValidateAttribute: function, the function that will be invoked after performing ajax-based validation
+ * triggered by a single attribute input change. The expected function signature should be
+ * <code>afterValidateAttribute(form, attribute, data, hasError) {...}</code>, where 'form' is the jquery
+ * representation of the form object; 'attribute' refers to the js options for the triggering attribute (see {@link error});
+ * 'data' is the JSON response from the server-side validation; 'hasError' is a boolean value indicating whether
+ * there is any validation error.
+ *
+ * Note that because this option refers to a js function, you should prefix the value with 'js:' to prevent it
+ * from being encoded as a string. This option has been available since version 1.1.3.</li>
+ * </ul>
+ *
+ * Some of the above options may be overridden in individual calls of {@link error()}.
+ * They include: validationDelay, validateOnChange, validateOnType, hideErrorMessage,
+ * inputContainer, errorCssClass, successCssClass, validatingCssClass, beforeValidateAttribute, afterValidateAttribute.
+ */
+ public $clientOptions=array();
+ /**
+ * @var boolean whether to enable data validation via AJAX. Defaults to false.
+ * When this property is set true, you should respond to the AJAX validation request on the server side as shown below:
+ * <pre>
+ * public function actionCreate()
+ * {
+ * $model=new User;
+ * if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
+ * {
+ * echo CActiveForm::validate($model);
+ * Yii::app()->end();
+ * }
+ * ......
+ * }
+ * </pre>
+ */
+ public $enableAjaxValidation=false;
+ /**
+ * @var boolean whether to enable client-side data validation. Defaults to false.
+ *
+ * When this property is set true, client-side validation will be performed by validators
+ * that support it (see {@link CValidator::enableClientValidation} and {@link CValidator::clientValidateAttribute}).
+ *
+ * @see error
+ * @since 1.1.7
+ */
+ public $enableClientValidation=false;
+
+ /**
+ * @var mixed form element to get initial input focus on page load.
+ *
+ * Defaults to null meaning no input field has a focus.
+ * If set as array, first element should be model and second element should be the attribute.
+ * If set as string any jQuery selector can be used
+ *
+ * Example - set input focus on page load to:
+ * <ul>
+ * <li>'focus'=>array($model,'username') - $model->username input filed</li>
+ * <li>'focus'=>'#'.CHtml::activeId($model,'username') - $model->username input field</li>
+ * <li>'focus'=>'#LoginForm_username' - input field with ID LoginForm_username</li>
+ * <li>'focus'=>'input[type="text"]:first' - first input element of type text</li>
+ * <li>'focus'=>'input:visible:enabled:first' - first visible and enabled input element</li>
+ * <li>'focus'=>'input:text[value=""]:first' - first empty input</li>
+ * </ul>
+ *
+ * @since 1.1.4
+ */
+ public $focus;
+ /**
+ * @var array the javascript options for model attributes (input ID => options)
+ * @see error
+ * @since 1.1.7
+ */
+ protected $attributes=array();
+ /**
+ * @var string the ID of the container element for error summary
+ * @see errorSummary
+ * @since 1.1.7
+ */
+ protected $summaryID;
+
+ /**
+ * Initializes the widget.
+ * This renders the form open tag.
+ */
+ public function init()
+ {
+ if(!isset($this->htmlOptions['id']))
+ $this->htmlOptions['id']=$this->id;
+ if($this->stateful)
+ echo CHtml::statefulForm($this->action, $this->method, $this->htmlOptions);
+ else
+ echo CHtml::beginForm($this->action, $this->method, $this->htmlOptions);
+ }
+
+ /**
+ * Runs the widget.
+ * This registers the necessary javascript code and renders the form close tag.
+ */
+ public function run()
+ {
+ if(is_array($this->focus))
+ $this->focus="#".CHtml::activeId($this->focus[0],$this->focus[1]);
+
+ echo CHtml::endForm();
+ $cs=Yii::app()->clientScript;
+ if(!$this->enableAjaxValidation && !$this->enableClientValidation || empty($this->attributes))
+ {
+ if($this->focus!==null)
+ {
+ $cs->registerCoreScript('jquery');
+ $cs->registerScript('CActiveForm#focus',"
+ if(!window.location.hash)
+ $('".$this->focus."').focus();
+ ");
+ }
+ return;
+ }
+
+ $options=$this->clientOptions;
+ if(isset($this->clientOptions['validationUrl']) && is_array($this->clientOptions['validationUrl']))
+ $options['validationUrl']=CHtml::normalizeUrl($this->clientOptions['validationUrl']);
+
+ $options['attributes']=array_values($this->attributes);
+
+ if($this->summaryID!==null)
+ $options['summaryID']=$this->summaryID;
+
+ if($this->focus!==null)
+ $options['focus']=$this->focus;
+
+ $options=CJavaScript::encode($options);
+ $cs->registerCoreScript('yiiactiveform');
+ $id=$this->id;
+ $cs->registerScript(__CLASS__.'#'.$id,"\$('#$id').yiiactiveform($options);");
+ }
+
+ /**
+ * Displays the first validation error for a model attribute.
+ * This is similar to {@link CHtml::error} except that it registers the model attribute
+ * so that if its value is changed by users, an AJAX validation may be triggered.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute name
+ * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
+ * Besides all those options available in {@link CHtml::error}, the following options are recognized in addition:
+ * <ul>
+ * <li>validationDelay</li>
+ * <li>validateOnChange</li>
+ * <li>validateOnType</li>
+ * <li>hideErrorMessage</li>
+ * <li>inputContainer</li>
+ * <li>errorCssClass</li>
+ * <li>successCssClass</li>
+ * <li>validatingCssClass</li>
+ * <li>beforeValidateAttribute</li>
+ * <li>afterValidateAttribute</li>
+ * </ul>
+ * These options override the corresponding options as declared in {@link options} for this
+ * particular model attribute. For more details about these options, please refer to {@link clientOptions}.
+ * Note that these options are only used when {@link enableAjaxValidation} or {@link enableClientValidation}
+ * is set true.
+ *
+ * When client-side validation is enabled, an option named "clientValidation" is also recognized.
+ * This option should take a piece of JavaScript code to perform client-side validation. In the code,
+ * the variables are predefined:
+ * <ul>
+ * <li>value: the current input value associated with this attribute.</li>
+ * <li>messages: an array that may be appended with new error messages for the attribute.</li>
+ * <li>attribute: a data structure keeping all client-side options for the attribute</li>
+ * </ul>
+ * @param boolean $enableAjaxValidation whether to enable AJAX validation for the specified attribute.
+ * Note that in order to enable AJAX validation, both {@link enableAjaxValidation} and this parameter
+ * must be true.
+ * @param boolean $enableClientValidation whether to enable client-side validation for the specified attribute.
+ * Note that in order to enable client-side validation, both {@link enableClientValidation} and this parameter
+ * must be true. This parameter has been available since version 1.1.7.
+ * @return string the validation result (error display or success message).
+ * @see CHtml::error
+ */
+ public function error($model,$attribute,$htmlOptions=array(),$enableAjaxValidation=true,$enableClientValidation=true)
+ {
+ if(!$this->enableAjaxValidation)
+ $enableAjaxValidation=false;
+ if(!$this->enableClientValidation)
+ $enableClientValidation=false;
+
+ if(!isset($htmlOptions['class']))
+ $htmlOptions['class']=$this->errorMessageCssClass;
+
+ if(!$enableAjaxValidation && !$enableClientValidation)
+ return CHtml::error($model,$attribute,$htmlOptions);
+
+ $id=CHtml::activeId($model,$attribute);
+ $inputID=isset($htmlOptions['inputID']) ? $htmlOptions['inputID'] : $id;
+ unset($htmlOptions['inputID']);
+ if(!isset($htmlOptions['id']))
+ $htmlOptions['id']=$inputID.'_em_';
+
+ $option=array(
+ 'id'=>$id,
+ 'inputID'=>$inputID,
+ 'errorID'=>$htmlOptions['id'],
+ 'model'=>get_class($model),
+ 'name'=>$attribute,
+ 'enableAjaxValidation'=>$enableAjaxValidation,
+ );
+
+ $optionNames=array(
+ 'validationDelay',
+ 'validateOnChange',
+ 'validateOnType',
+ 'hideErrorMessage',
+ 'inputContainer',
+ 'errorCssClass',
+ 'successCssClass',
+ 'validatingCssClass',
+ 'beforeValidateAttribute',
+ 'afterValidateAttribute',
+ );
+ foreach($optionNames as $name)
+ {
+ if(isset($htmlOptions[$name]))
+ {
+ $option[$name]=$htmlOptions[$name];
+ unset($htmlOptions[$name]);
+ }
+ }
+ if($model instanceof CActiveRecord && !$model->isNewRecord)
+ $option['status']=1;
+
+ if($enableClientValidation)
+ {
+ $validators=isset($htmlOptions['clientValidation']) ? array($htmlOptions['clientValidation']) : array();
+
+ $attributeName = $attribute;
+ if(($pos=strrpos($attribute,']'))!==false && $pos!==strlen($attribute)-1) // e.g. [a]name
+ {
+ $attributeName=substr($attribute,$pos+1);
+ }
+
+ foreach($model->getValidators($attributeName) as $validator)
+ {
+ if($validator->enableClientValidation)
+ {
+ if(($js=$validator->clientValidateAttribute($model,$attributeName))!='')
+ $validators[]=$js;
+ }
+ }
+ if($validators!==array())
+ $option['clientValidation']="js:function(value, messages, attribute) {\n".implode("\n",$validators)."\n}";
+ }
+
+ $html=CHtml::error($model,$attribute,$htmlOptions);
+ if($html==='')
+ {
+ if(isset($htmlOptions['style']))
+ $htmlOptions['style']=rtrim($htmlOptions['style'],';').';display:none';
+ else
+ $htmlOptions['style']='display:none';
+ $html=CHtml::tag('div',$htmlOptions,'');
+ }
+
+ $this->attributes[$inputID]=$option;
+ return $html;
+ }
+
+ /**
+ * Displays a summary of validation errors for one or several models.
+ * This method is very similar to {@link CHtml::errorSummary} except that it also works
+ * when AJAX validation is performed.
+ * @param mixed $models the models whose input errors are to be displayed. This can be either
+ * a single model or an array of models.
+ * @param string $header a piece of HTML code that appears in front of the errors
+ * @param string $footer a piece of HTML code that appears at the end of the errors
+ * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
+ * @return string the error summary. Empty if no errors are found.
+ * @see CHtml::errorSummary
+ */
+ public function errorSummary($models,$header=null,$footer=null,$htmlOptions=array())
+ {
+ if(!$this->enableAjaxValidation && !$this->enableClientValidation)
+ return CHtml::errorSummary($models,$header,$footer,$htmlOptions);
+
+ if(!isset($htmlOptions['id']))
+ $htmlOptions['id']=$this->id.'_es_';
+ $html=CHtml::errorSummary($models,$header,$footer,$htmlOptions);
+ if($html==='')
+ {
+ if($header===null)
+ $header='<p>'.Yii::t('yii','Please fix the following input errors:').'</p>';
+ if(!isset($htmlOptions['class']))
+ $htmlOptions['class']=CHtml::$errorSummaryCss;
+ $htmlOptions['style']=isset($htmlOptions['style']) ? rtrim($htmlOptions['style'],';').';display:none' : 'display:none';
+ $html=CHtml::tag('div',$htmlOptions,$header."\n<ul><li>dummy</li></ul>".$footer);
+ }
+
+ $this->summaryID=$htmlOptions['id'];
+ return $html;
+ }
+
+ /**
+ * Renders an HTML label for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeLabel}.
+ * Please check {@link CHtml::activeLabel} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated label tag
+ */
+ public function label($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeLabel($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders an HTML label for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeLabelEx}.
+ * Please check {@link CHtml::activeLabelEx} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated label tag
+ */
+ public function labelEx($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeLabelEx($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a text field for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeTextField}.
+ * Please check {@link CHtml::activeTextField} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated input field
+ */
+ public function textField($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeTextField($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a hidden field for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeHiddenField}.
+ * Please check {@link CHtml::activeHiddenField} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated input field
+ */
+ public function hiddenField($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeHiddenField($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a password field for a model attribute.
+ * This method is a wrapper of {@link CHtml::activePasswordField}.
+ * Please check {@link CHtml::activePasswordField} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated input field
+ */
+ public function passwordField($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activePasswordField($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a text area for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeTextArea}.
+ * Please check {@link CHtml::activeTextArea} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated text area
+ */
+ public function textArea($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeTextArea($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a file field for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeFileField}.
+ * Please check {@link CHtml::activeFileField} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes
+ * @return string the generated input field
+ */
+ public function fileField($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeFileField($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a radio button for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeRadioButton}.
+ * Please check {@link CHtml::activeRadioButton} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated radio button
+ */
+ public function radioButton($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeRadioButton($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a checkbox for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeCheckBox}.
+ * Please check {@link CHtml::activeCheckBox} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated check box
+ */
+ public function checkBox($model,$attribute,$htmlOptions=array())
+ {
+ return CHtml::activeCheckBox($model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Renders a dropdown list for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeDropDownList}.
+ * Please check {@link CHtml::activeDropDownList} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data data for generating the list options (value=>display)
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated drop down list
+ */
+ public function dropDownList($model,$attribute,$data,$htmlOptions=array())
+ {
+ return CHtml::activeDropDownList($model,$attribute,$data,$htmlOptions);
+ }
+
+ /**
+ * Renders a list box for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeListBox}.
+ * Please check {@link CHtml::activeListBox} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data data for generating the list options (value=>display)
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated list box
+ */
+ public function listBox($model,$attribute,$data,$htmlOptions=array())
+ {
+ return CHtml::activeListBox($model,$attribute,$data,$htmlOptions);
+ }
+
+ /**
+ * Renders a checkbox list for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeCheckBoxList}.
+ * Please check {@link CHtml::activeCheckBoxList} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data value-label pairs used to generate the check box list.
+ * @param array $htmlOptions addtional HTML options.
+ * @return string the generated check box list
+ */
+ public function checkBoxList($model,$attribute,$data,$htmlOptions=array())
+ {
+ return CHtml::activeCheckBoxList($model,$attribute,$data,$htmlOptions);
+ }
+
+ /**
+ * Renders a radio button list for a model attribute.
+ * This method is a wrapper of {@link CHtml::activeRadioButtonList}.
+ * Please check {@link CHtml::activeRadioButtonList} for detailed information
+ * about the parameters for this method.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data value-label pairs used to generate the radio button list.
+ * @param array $htmlOptions addtional HTML options.
+ * @return string the generated radio button list
+ */
+ public function radioButtonList($model,$attribute,$data,$htmlOptions=array())
+ {
+ return CHtml::activeRadioButtonList($model,$attribute,$data,$htmlOptions);
+ }
+
+ /**
+ * Validates one or several models and returns the results in JSON format.
+ * This is a helper method that simplifies the way of writing AJAX validation code.
+ * @param mixed $models a single model instance or an array of models.
+ * @param array $attributes list of attributes that should be validated. Defaults to null,
+ * meaning any attribute listed in the applicable validation rules of the models should be
+ * validated. If this parameter is given as a list of attributes, only
+ * the listed attributes will be validated.
+ * @param boolean $loadInput whether to load the data from $_POST array in this method.
+ * If this is true, the model will be populated from <code>$_POST[ModelClass]</code>.
+ * @return string the JSON representation of the validation error messages.
+ */
+ public static function validate($models, $attributes=null, $loadInput=true)
+ {
+ $result=array();
+ if(!is_array($models))
+ $models=array($models);
+ foreach($models as $model)
+ {
+ if($loadInput && isset($_POST[get_class($model)]))
+ $model->attributes=$_POST[get_class($model)];
+ $model->validate($attributes);
+ foreach($model->getErrors() as $attribute=>$errors)
+ $result[CHtml::activeId($model,$attribute)]=$errors;
+ }
+ return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
+ }
+
+ /**
+ * Validates an array of model instances and returns the results in JSON format.
+ * This is a helper method that simplifies the way of writing AJAX validation code for tabular input.
+ * @param mixed $models an array of model instances.
+ * @param array $attributes list of attributes that should be validated. Defaults to null,
+ * meaning any attribute listed in the applicable validation rules of the models should be
+ * validated. If this parameter is given as a list of attributes, only
+ * the listed attributes will be validated.
+ * @param boolean $loadInput whether to load the data from $_POST array in this method.
+ * If this is true, the model will be populated from <code>$_POST[ModelClass][$i]</code>.
+ * @return string the JSON representation of the validation error messages.
+ */
+ public static function validateTabular($models, $attributes=null, $loadInput=true)
+ {
+ $result=array();
+ if(!is_array($models))
+ $models=array($models);
+ foreach($models as $i=>$model)
+ {
+ if($loadInput && isset($_POST[get_class($model)][$i]))
+ $model->attributes=$_POST[get_class($model)][$i];
+ $model->validate($attributes);
+ foreach($model->getErrors() as $attribute=>$errors)
+ $result[CHtml::activeId($model,'['.$i.']'.$attribute)]=$errors;
+ }
+ return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/CAutoComplete.php b/framework/web/widgets/CAutoComplete.php
new file mode 100644
index 0000000..8803109
--- /dev/null
+++ b/framework/web/widgets/CAutoComplete.php
@@ -0,0 +1,291 @@
+<?php
+/**
+ * CAutoComplete class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CAutoComplete generates an auto-complete text field.
+ *
+ * CAutoComplete is based on the {@link http://plugins.jquery.com/project/autocompletex jQuery Autocomplete}.
+ *
+ * This class is deprecated since Yii 1.1.3. Consider using CJuiAutoComplete.
+ * There is {@link http://www.learningjquery.com/2010/06/autocomplete-migration-guide a good migration guide from the author of both JavaScript solutions}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CAutoComplete.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ * @deprecated in 1.1.3
+ */
+class CAutoComplete extends CInputWidget
+{
+ /**
+ * @var boolean whether to show the autocomplete using a text area. Defaults to false,
+ * meaning a text field is used.
+ */
+ public $textArea=false;
+ /**
+ * @var array data that would be saved as client-side data to provide candidate selections.
+ * Each array element can be string or an associative array.
+ * The {@link url} property will be ignored if this property is set.
+ * @see url
+ */
+ public $data;
+ /**
+ * @var string|array the URL that can return the candidate selections.
+ * A 'q' GET parameter will be sent with the URL which contains what the user has entered so far.
+ * If the URL is given as an array, it is considered as a route to a controller action and will
+ * be used to generate a URL using {@link CController::createUrl};
+ * If the URL is an empty string, the currently requested URL is used.
+ * This property will be ignored if {@link data} is set.
+ * @see data
+ */
+ public $url='';
+ /**
+ * @var mixed the CSS file used for the widget. Defaults to null, meaning
+ * using the default CSS file included together with the widget.
+ * If false, no CSS file will be used. Otherwise, the specified CSS file
+ * will be included when using this widget.
+ */
+ public $cssFile;
+ /**
+ * @var integer the minimum number of characters a user has to type before
+ * the autocompleter activates. Defaults to 1.
+ */
+ public $minChars;
+ /**
+ * @var integer the delay in milliseconds the autocompleter waits after
+ * a keystroke to activate itself. Defaults to 400.
+ */
+ public $delay;
+ /**
+ * @var integer the number of backend query results to store in cache.
+ * If set to 1 (the current result), no caching will happen. Must be >= 1. Defaults to 10.
+ */
+ public $cacheLength;
+ /**
+ * @var boolean whether or not the autocompleter can use a cache for more
+ * specific queries. This means that all matches of "foot" are a subset
+ * of all matches for "foo". Usually this is true, and using this options
+ * decreases server load and increases performance. Only useful with
+ * cacheLength settings bigger than one, like 10. Defaults to true.
+ */
+ public $matchSubset;
+ /**
+ * @var boolean whether or not the comparison is case sensitive. Important
+ * only if you use caching. Defaults to false.
+ */
+ public $matchCase;
+ /**
+ * @var boolean whether or not the comparison looks inside
+ * (i.e. does "ba" match "foo bar") the search results. Important only if
+ * you use caching. Don't mix with autofill. Defaults to false.
+ */
+ public $matchContains;
+ /**
+ * @var boolean if set to true, the autocompleter will only allow results that
+ * are presented by the backend. Note that illegal values result in an empty
+ * input box. Defaults to false.
+ */
+ public $mustMatch;
+ /**
+ * @var boolean if this is set to true, the first autocomplete value will
+ * be automatically selected on tab/return, even if it has not been handpicked
+ * by keyboard or mouse action. If there is a handpicked (highlighted) result,
+ * that result will take precedence. Defaults to true.
+ */
+ public $selectFirst;
+ /**
+ * @var array extra parameters for the backend. If you were to specify
+ * array('bar'=>4), the autocompleter would call the backend with a GET
+ * parameter 'bar' 4. The param can be a function that is called to calculate
+ * the param before each request.
+ */
+ public $extraParams;
+ /**
+ * @var string a javascript function that provides advanced markup for an item.
+ * For each row of results, this function will be called. The returned value will
+ * be displayed inside an LI element in the results list. Autocompleter will
+ * provide 4 parameters: the results row, the position of the row in the list of
+ * results (starting at 1), the number of items in the list of results and the search term.
+ * The default behavior assumes that a single row contains a single value.
+ */
+ public $formatItem;
+ /**
+ * @var string a javascript function that can be used to limit the data that autocomplete
+ * searches for matches. For example, there may be items you want displayed to the user,
+ * but don't want included in the data that's searched. The function is called with the same arguments
+ * as {@link formatItem}. Defaults to formatItem.
+ */
+ public $formatMatch;
+ /**
+ * @var string a javascript function that provides the formatting for the value to be
+ * put into the input field. Again three arguments: Data, position (starting with one) and
+ * total number of data. The default behavior assumes either plain data to use as result
+ * or uses the same value as provided by formatItem.
+ */
+ public $formatResult;
+ /**
+ * @var boolean whether to allow more than one autocompleted-value to enter. Defaults to false.
+ */
+ public $multiple;
+ /**
+ * @var string seperator to put between values when using multiple option. Defaults to ", ".
+ */
+ public $multipleSeparator;
+ /**
+ * @var integer specify a custom width for the select box. Defaults to the width of the input element.
+ */
+ public $width;
+ /**
+ * @var boolean fill the textinput while still selecting a value, replacing the value
+ * if more is typed or something else is selected. Defaults to false.
+ */
+ public $autoFill;
+ /**
+ * @var integer limit the number of items in the select box. Is also sent as
+ * a "limit" parameter with a remote request. Defaults to 10.
+ */
+ public $max;
+ /**
+ * @var boolean|string Whether and how to highlight matches in the select box.
+ * Set to false to disable. Set to a javascript function to customize.
+ * The function gets the value as the first argument and the search term as the
+ * second and must return the formatted value. Defaults to Wraps the search term in a &lt;strong&gt; element.
+ */
+ public $highlight;
+ /**
+ * @var boolean whether to scroll when more results than configured via scrollHeight are available. Defaults to true.
+ */
+ public $scroll;
+ /**
+ * @var integer height of scrolled autocomplete control in pixels. Defaults to 180.
+ */
+ public $scrollHeight;
+ /**
+ * @var string the CSS class for the input element. Defaults to "ac_input".
+ */
+ public $inputClass;
+ /**
+ * @var string the CSS class for the dropdown list. Defaults to "ac_results".
+ */
+ public $resultsClass;
+ /**
+ * @var string the CSS class used when the data is being loaded from backend. Defaults to "ac_loading".
+ */
+ public $loadingClass;
+ /**
+ * @var array additional options that can be passed to the constructor of the autocomplete js object.
+ * This allows you to override existing functions of the autocomplete js class (e.g. the parse() function)
+ *
+ * If you want to provide JavaScript native code, you have to prefix the string with js: otherwise it will
+ * be enclosed by quotes.
+ */
+ public $options=array();
+ /**
+ * @var string the chain of method calls that would be appended at the end of the autocomplete constructor.
+ * For example, ".result(function(...){})" would cause the specified js function to execute
+ * when the user selects an option.
+ */
+ public $methodChain;
+
+ /**
+ * Initializes the widget.
+ * This method registers all needed client scripts and renders
+ * the autocomplete input.
+ */
+ public function init()
+ {
+ list($name,$id)=$this->resolveNameID();
+ if(isset($this->htmlOptions['id']))
+ $id=$this->htmlOptions['id'];
+ else
+ $this->htmlOptions['id']=$id;
+ if(isset($this->htmlOptions['name']))
+ $name=$this->htmlOptions['name'];
+
+ $this->registerClientScript();
+
+ if($this->hasModel())
+ {
+ $field=$this->textArea ? 'activeTextArea' : 'activeTextField';
+ echo CHtml::$field($this->model,$this->attribute,$this->htmlOptions);
+ }
+ else
+ {
+ $field=$this->textArea ? 'textArea' : 'textField';
+ echo CHtml::$field($name,$this->value,$this->htmlOptions);
+ }
+ }
+
+ /**
+ * Registers the needed CSS and JavaScript.
+ */
+ public function registerClientScript()
+ {
+ $id=$this->htmlOptions['id'];
+
+ $acOptions=$this->getClientOptions();
+ $options=$acOptions===array()?'{}' : CJavaScript::encode($acOptions);
+
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('autocomplete');
+ if($this->data!==null)
+ $data=CJavaScript::encode($this->data);
+ else
+ {
+ $url=CHtml::normalizeUrl($this->url);
+ $data='"'.$url.'"';
+ }
+ $cs->registerScript('Yii.CAutoComplete#'.$id,"jQuery(\"#{$id}\").legacyautocomplete($data,{$options}){$this->methodChain};");
+
+ if($this->cssFile!==false)
+ self::registerCssFile($this->cssFile);
+ }
+
+ /**
+ * Registers the needed CSS file.
+ * @param string $url the CSS URL. If null, a default CSS URL will be used.
+ */
+ public static function registerCssFile($url=null)
+ {
+ $cs=Yii::app()->getClientScript();
+ if($url===null)
+ $url=$cs->getCoreScriptUrl().'/autocomplete/jquery.autocomplete.css';
+ $cs->registerCssFile($url);
+ }
+
+ /**
+ * @return array the javascript options
+ */
+ protected function getClientOptions()
+ {
+ static $properties=array(
+ 'minChars', 'delay', 'cacheLength', 'matchSubset',
+ 'matchCase', 'matchContains', 'mustMatch', 'selectFirst',
+ 'extraParams', 'multiple', 'multipleSeparator', 'width',
+ 'autoFill', 'max', 'scroll', 'scrollHeight', 'inputClass',
+ 'formatItem', 'formatMatch', 'formatResult', 'highlight',
+ 'resultsClass', 'loadingClass');
+ static $functions=array('formatItem', 'formatMatch', 'formatResult', 'highlight');
+
+ $options=$this->options;
+ foreach($properties as $property)
+ {
+ if($this->$property!==null)
+ $options[$property]=$this->$property;
+ }
+ foreach($functions as $func)
+ {
+ if(is_string($this->$func) && strncmp($this->$func,'js:',3))
+ $options[$func]='js:'.$this->$func;
+ }
+
+ return $options;
+ }
+}
diff --git a/framework/web/widgets/CClipWidget.php b/framework/web/widgets/CClipWidget.php
new file mode 100644
index 0000000..d963947
--- /dev/null
+++ b/framework/web/widgets/CClipWidget.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * CClipWidget class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CClipWidget records its content and makes it available elsewhere.
+ *
+ * Content rendered between its {@link init()} and {@link run()} calls are saved
+ * as a clip in the controller. The clip is named after the widget ID.
+ *
+ * See {@link CBaseController::beginClip} and {@link CBaseController::endClip}
+ * for a shortcut usage of CClipWidget.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CClipWidget.php 2799 2011-01-01 19:31:13Z qiang.xue $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CClipWidget extends CWidget
+{
+ /**
+ * @var boolean whether to render the clip content in place. Defaults to false,
+ * meaning the captured clip will not be displayed.
+ */
+ public $renderClip=false;
+
+ /**
+ * Starts recording a clip.
+ */
+ public function init()
+ {
+ ob_start();
+ ob_implicit_flush(false);
+ }
+
+ /**
+ * Ends recording a clip.
+ * This method stops output buffering and saves the rendering result as a named clip in the controller.
+ */
+ public function run()
+ {
+ $clip=ob_get_clean();
+ if($this->renderClip)
+ echo $clip;
+ $this->getController()->getClips()->add($this->getId(),$clip);
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/CContentDecorator.php b/framework/web/widgets/CContentDecorator.php
new file mode 100644
index 0000000..9a3cff9
--- /dev/null
+++ b/framework/web/widgets/CContentDecorator.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * CContentDecorator class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CContentDecorator decorates the content it encloses with the specified view.
+ *
+ * CContentDecorator is mostly used to implement nested layouts, i.e., a layout
+ * is embedded within another layout. {@link CBaseController} defines a pair of
+ * convenient methods to use CContentDecorator:
+ * <pre>
+ * $this->beginContent('path/to/view');
+ * // ... content to be decorated
+ * $this->endContent();
+ * </pre>
+ *
+ * The property {@link view} specifies the name of the view that is used to
+ * decorate the content. In the view, the content being decorated may be
+ * accessed with variable <code>$content</code>.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CContentDecorator.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CContentDecorator extends COutputProcessor
+{
+ /**
+ * @var mixed the name of the view that will be used to decorate the captured content.
+ * If this property is null (default value), the default layout will be used as
+ * the decorative view. Note that if the current controller does not belong to
+ * any module, the default layout refers to the application's {@link CWebApplication::layout default layout};
+ * If the controller belongs to a module, the default layout refers to the module's
+ * {@link CWebModule::layout default layout}.
+ */
+ public $view;
+ /**
+ * @var array the variables (name=>value) to be extracted and made available in the decorative view.
+ */
+ public $data=array();
+
+ /**
+ * Processes the captured output.
+ * This method decorates the output with the specified {@link view}.
+ * @param string $output the captured output to be processed
+ */
+ public function processOutput($output)
+ {
+ $output=$this->decorate($output);
+ parent::processOutput($output);
+ }
+
+ /**
+ * Decorates the content by rendering a view and embedding the content in it.
+ * The content being embedded can be accessed in the view using variable <code>$content</code>
+ * The decorated content will be displayed directly.
+ * @param string $content the content to be decorated
+ * @return string the decorated content
+ */
+ protected function decorate($content)
+ {
+ $owner=$this->getOwner();
+ if($this->view===null)
+ $viewFile=Yii::app()->getController()->getLayoutFile(null);
+ else
+ $viewFile=$owner->getViewFile($this->view);
+ if($viewFile!==false)
+ {
+ $data=$this->data;
+ $data['content']=$content;
+ return $owner->renderFile($viewFile,$data,true);
+ }
+ else
+ return $content;
+ }
+}
diff --git a/framework/web/widgets/CFilterWidget.php b/framework/web/widgets/CFilterWidget.php
new file mode 100644
index 0000000..42cb8d4
--- /dev/null
+++ b/framework/web/widgets/CFilterWidget.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * CFilterWidget class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CFilterWidget is the base class for widgets that can also be used as filters.
+ *
+ * Derived classes may need to override the following methods:
+ * <ul>
+ * <li>{@link CWidget::init()} : called when this is object is used as a widget and needs initialization.</li>
+ * <li>{@link CWidget::run()} : called when this is object is used as a widget.</li>
+ * <li>{@link filter()} : the filtering method called when this object is used as an action filter.</li>
+ * </ul>
+ *
+ * CFilterWidget provides all properties and methods of {@link CWidget} and {@link CFilter}.
+ *
+ * @property boolean $isFilter Whether this widget is used as a filter.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CFilterWidget.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CFilterWidget extends CWidget implements IFilter
+{
+ /**
+ * @var boolean whether to stop the action execution when this widget is used as a filter.
+ * This property should be changed only in {@link CWidget::init} method.
+ * Defaults to false, meaning the action should be executed.
+ */
+ public $stopAction=false;
+
+ private $_isFilter;
+
+ /**
+ * Constructor.
+ * @param CBaseController $owner owner/creator of this widget. It could be either a widget or a controller.
+ */
+ public function __construct($owner=null)
+ {
+ parent::__construct($owner);
+ $this->_isFilter=($owner===null);
+ }
+
+ /**
+ * @return boolean whether this widget is used as a filter.
+ */
+ public function getIsFilter()
+ {
+ return $this->_isFilter;
+ }
+
+ /**
+ * Performs the filtering.
+ * The default implementation simply calls {@link init()},
+ * {@link CFilterChain::run()} and {@link run()} in order
+ * Derived classes may want to override this method to change this behavior.
+ * @param CFilterChain $filterChain the filter chain that the filter is on.
+ */
+ public function filter($filterChain)
+ {
+ $this->init();
+ if(!$this->stopAction)
+ {
+ $filterChain->run();
+ $this->run();
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/CFlexWidget.php b/framework/web/widgets/CFlexWidget.php
new file mode 100644
index 0000000..f6ff982
--- /dev/null
+++ b/framework/web/widgets/CFlexWidget.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * CFlexWidget class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CFlexWidget embeds a Flex 3.x application into a page.
+ *
+ * To use CFlexWidget, set {@link name} to be the Flex application name
+ * (without the .swf suffix), and set {@link baseUrl} to be URL (without the ending slash)
+ * of the directory containing the SWF file of the Flex application.
+ *
+ * @property string $flashVarsAsString The flash parameter string.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CFlexWidget.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CFlexWidget extends CWidget
+{
+ /**
+ * @var string name of the Flex application.
+ * This should be the SWF file name without the ".swf" suffix.
+ */
+ public $name;
+ /**
+ * @var string the base URL of the Flex application.
+ * This refers to the URL of the directory containing the SWF file.
+ */
+ public $baseUrl;
+ /**
+ * @var string width of the application region. Defaults to 450.
+ */
+ public $width='100%';
+ /**
+ * @var string height of the application region. Defaults to 300.
+ */
+ public $height='100%';
+ /**
+ * @var string quality of the animation. Defaults to 'high'.
+ */
+ public $quality='high';
+ /**
+ * @var string background color of the application region. Defaults to '#FFFFFF', meaning white.
+ */
+ public $bgColor='#FFFFFF';
+ /**
+ * @var string align of the application region. Defaults to 'middle'.
+ */
+ public $align='middle';
+ /**
+ * @var string the access method of the script. Defaults to 'sameDomain'.
+ */
+ public $allowScriptAccess='sameDomain';
+ /**
+ * @var boolean whether to allow running the Flash in full screen mode. Defaults to false.
+ * @since 1.1.1
+ */
+ public $allowFullScreen=false;
+ /**
+ * @var string the HTML content to be displayed if Flash player is not installed.
+ */
+ public $altHtmlContent;
+ /**
+ * @var boolean whether history should be enabled. Defaults to true.
+ */
+ public $enableHistory=true;
+ /**
+ * @var array parameters to be passed to the Flex application.
+ */
+ public $flashVars=array();
+
+ /**
+ * Renders the widget.
+ */
+ public function run()
+ {
+ if(empty($this->name))
+ throw new CException(Yii::t('yii','CFlexWidget.name cannot be empty.'));
+ if(empty($this->baseUrl))
+ throw new CException(Yii::t('yii','CFlexWidget.baseUrl cannot be empty.'));
+ if($this->altHtmlContent===null)
+ $this->altHtmlContent=Yii::t('yii','This content requires the <a href="http://www.adobe.com/go/getflash/">Adobe Flash Player</a>.');
+
+ $this->registerClientScript();
+
+ $this->render('flexWidget');
+ }
+
+ /**
+ * Registers the needed CSS and JavaScript.
+ */
+ public function registerClientScript()
+ {
+ $cs=Yii::app()->getClientScript();
+ $cs->registerScriptFile($this->baseUrl.'/AC_OETags.js');
+
+ if($this->enableHistory)
+ {
+ $cs->registerCssFile($this->baseUrl.'/history/history.css');
+ $cs->registerScriptFile($this->baseUrl.'/history/history.js');
+ }
+ }
+
+ /**
+ * Generates the properly quoted flash parameter string.
+ * @return string the flash parameter string.
+ */
+ public function getFlashVarsAsString()
+ {
+ $params=array();
+ foreach($this->flashVars as $k=>$v)
+ $params[]=urlencode($k).'='.urlencode($v);
+ return CJavaScript::quote(implode('&',$params));
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/CHtmlPurifier.php b/framework/web/widgets/CHtmlPurifier.php
new file mode 100644
index 0000000..c0f13fe
--- /dev/null
+++ b/framework/web/widgets/CHtmlPurifier.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * CHtmlPurifier class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+if(!class_exists('HTMLPurifier_Bootstrap',false))
+{
+ require_once(Yii::getPathOfAlias('system.vendors.htmlpurifier').DIRECTORY_SEPARATOR.'HTMLPurifier.standalone.php');
+ HTMLPurifier_Bootstrap::registerAutoload();
+}
+
+/**
+ * CHtmlPurifier is wrapper of {@link http://htmlpurifier.org HTML Purifier}.
+ *
+ * CHtmlPurifier removes all malicious code (better known as XSS) with a thoroughly audited,
+ * secure yet permissive whitelist. It will also make sure the resulting code
+ * is standard-compliant.
+ *
+ * CHtmlPurifier can be used as either a widget or a controller filter.
+ *
+ * Note: since HTML Purifier is a big package, its performance is not very good.
+ * You should consider either caching the purification result or purifying the user input
+ * before saving to database.
+ *
+ * Usage as a class:
+ * <pre>
+ * $p = new CHtmlPurifier();
+ * $p->options = array('URI.AllowedSchemes'=>array(
+ * 'http' => true,
+ * 'https' => true,
+ * ));
+ * $text = $p->purify($text);
+ * </pre>
+ *
+ * Usage as validation rule:
+ * <pre>
+ * array('text','filter','filter'=>array($obj=new CHtmlPurifier(),'purify')),
+ * </pre>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CHtmlPurifier.php 3541 2012-01-17 07:27:46Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CHtmlPurifier extends COutputProcessor
+{
+ /**
+ * @var mixed the options to be passed to HTML Purifier instance.
+ * This can be a HTMLPurifier_Config object, an array of directives (Namespace.Directive => Value)
+ * or the filename of an ini file.
+ * @see http://htmlpurifier.org/live/configdoc/plain.html
+ */
+ public $options=null;
+
+ /**
+ * Processes the captured output.
+ * This method purifies the output using {@link http://htmlpurifier.org HTML Purifier}.
+ * @param string $output the captured output to be processed
+ */
+ public function processOutput($output)
+ {
+ $output=$this->purify($output);
+ parent::processOutput($output);
+ }
+
+ /**
+ * Purifies the HTML content by removing malicious code.
+ * @param string $content the content to be purified.
+ * @return string the purified content
+ */
+ public function purify($content)
+ {
+ $purifier=new HTMLPurifier($this->options);
+ $purifier->config->set('Cache.SerializerPath',Yii::app()->getRuntimePath());
+ return $purifier->purify($content);
+ }
+}
diff --git a/framework/web/widgets/CInputWidget.php b/framework/web/widgets/CInputWidget.php
new file mode 100644
index 0000000..f08fa82
--- /dev/null
+++ b/framework/web/widgets/CInputWidget.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * CInputWidget class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CInputWidget is the base class for widgets that collect user inputs.
+ *
+ * CInputWidget declares properties common among input widgets. An input widget
+ * can be associated with a data model and an attribute, or a name and a value.
+ * If the former, the name and the value will be generated automatically.
+ * Child classes may use {@link resolveNameID} and {@link hasModel}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CInputWidget.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+abstract class CInputWidget extends CWidget
+{
+ /**
+ * @var CModel the data model associated with this widget.
+ */
+ public $model;
+ /**
+ * @var string the attribute associated with this widget.
+ * The name can contain square brackets (e.g. 'name[1]') which is used to collect tabular data input.
+ */
+ public $attribute;
+ /**
+ * @var string the input name. This must be set if {@link model} is not set.
+ */
+ public $name;
+ /**
+ * @var string the input value
+ */
+ public $value;
+ /**
+ * @var array additional HTML options to be rendered in the input tag
+ */
+ public $htmlOptions=array();
+
+
+ /**
+ * @return array the name and the ID of the input.
+ */
+ protected function resolveNameID()
+ {
+ if($this->name!==null)
+ $name=$this->name;
+ else if(isset($this->htmlOptions['name']))
+ $name=$this->htmlOptions['name'];
+ else if($this->hasModel())
+ $name=CHtml::activeName($this->model,$this->attribute);
+ else
+ throw new CException(Yii::t('yii','{class} must specify "model" and "attribute" or "name" property values.',array('{class}'=>get_class($this))));
+
+ if(($id=$this->getId(false))===null)
+ {
+ if(isset($this->htmlOptions['id']))
+ $id=$this->htmlOptions['id'];
+ else
+ $id=CHtml::getIdByName($name);
+ }
+
+ return array($name,$id);
+ }
+
+ /**
+ * @return boolean whether this widget is associated with a data model.
+ */
+ protected function hasModel()
+ {
+ return $this->model instanceof CModel && $this->attribute!==null;
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/CMarkdown.php b/framework/web/widgets/CMarkdown.php
new file mode 100644
index 0000000..d2ce142
--- /dev/null
+++ b/framework/web/widgets/CMarkdown.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * CMarkdown class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CMarkdown converts the captured content from markdown syntax to HTML code.
+ *
+ * CMarkdown can be used as either a widget or a filter. It is a wrapper of {@link CMarkdownParser}.
+ * CMarkdown adds an additional option {@link purifyOutput} which can be set true
+ * so that the converted HTML code is purified before being displayed.
+ *
+ * For details about the markdown syntax, please check the following:
+ * <ul>
+ * <li>{@link http://daringfireball.net/projects/markdown/syntax official markdown syntax}</li>
+ * <li>{@link http://michelf.com/projects/php-markdown/extra/ markdown extra syntax}</li>
+ * <li>{@link CMarkdownParser markdown with syntax highlighting}</li>
+ * </ul>
+ *
+ * @property CMarkdownParser $markdownParser The parser instance.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CMarkdown.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CMarkdown extends COutputProcessor
+{
+ /**
+ * @var mixed the CSS file used for the widget. Defaults to null, meaning
+ * using the default CSS file included together with the widget.
+ * If false, no CSS file will be used. Otherwise, the specified CSS file
+ * will be included when using this widget.
+ */
+ public $cssFile;
+ /**
+ * @var boolean whether to use {@link CHtmlPurifier} to purify the generated HTML code. Defaults to false.
+ */
+ public $purifyOutput=false;
+
+ private $_parser;
+
+ /**
+ * Processes the captured output.
+ * This method converts the content in markdown syntax to HTML code.
+ * If {@link purifyOutput} is true, the HTML code will also be purified.
+ * @param string $output the captured output to be processed
+ * @see convert
+ */
+ public function processOutput($output)
+ {
+ $output=$this->transform($output);
+ if($this->purifyOutput)
+ {
+ $purifier=new CHtmlPurifier;
+ $output=$purifier->purify($output);
+ }
+ parent::processOutput($output);
+ }
+
+ /**
+ * Converts the content in markdown syntax to HTML code.
+ * This method uses {@link CMarkdownParser} to do the conversion.
+ * @param string $output the content to be converted
+ * @return string the converted content
+ */
+ public function transform($output)
+ {
+ $this->registerClientScript();
+ return $this->getMarkdownParser()->transform($output);
+ }
+
+ /**
+ * Registers the needed CSS and JavaScript.
+ */
+ public function registerClientScript()
+ {
+ if($this->cssFile!==false)
+ self::registerCssFile($this->cssFile);
+ }
+
+ /**
+ * Registers the needed CSS file.
+ * @param string $url the CSS URL. If null, a default CSS URL will be used.
+ */
+ public static function registerCssFile($url=null)
+ {
+ CTextHighlighter::registerCssFile($url);
+ }
+
+ /**
+ * Returns the markdown parser instance.
+ * This method calls {@link createMarkdownParser} to create the parser instance.
+ * Call this method multipe times will only return the same instance.
+ * @return CMarkdownParser the parser instance
+ */
+ public function getMarkdownParser()
+ {
+ if($this->_parser===null)
+ $this->_parser=$this->createMarkdownParser();
+ return $this->_parser;
+ }
+
+ /**
+ * Creates a markdown parser.
+ * By default, this method creates a {@link CMarkdownParser} instance.
+ * @return CMarkdownParser the markdown parser.
+ */
+ protected function createMarkdownParser()
+ {
+ return new CMarkdownParser;
+ }
+}
diff --git a/framework/web/widgets/CMaskedTextField.php b/framework/web/widgets/CMaskedTextField.php
new file mode 100644
index 0000000..f7bf40f
--- /dev/null
+++ b/framework/web/widgets/CMaskedTextField.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * CMaskedTextField class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CMaskedTextField generates a masked text field.
+ *
+ * CMaskedTextField is similar to {@link CHtml::textField} except that
+ * an input mask will be used to help users enter properly formatted data.
+ * The masked text field is implemented based on the jQuery masked input plugin
+ * (see {@link http://digitalbush.com/projects/masked-input-plugin}).
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CMaskedTextField.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CMaskedTextField extends CInputWidget
+{
+ /**
+ * @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined:
+ * <ul>
+ * <li>a: represents an alpha character (A-Z,a-z).</li>
+ * <li>9: represents a numeric character (0-9).</li>
+ * <li>*: represents an alphanumeric character (A-Z,a-z,0-9).</li>
+ * <li>?: anything listed after '?' within the mask is considered optional user input.</li>
+ * </ul>
+ * Additional characters can be defined by specifying the {@link charMap} property.
+ */
+ public $mask;
+ /**
+ * @var array the mapping between mask characters and the corresponding patterns.
+ * For example, array('~'=>'[+-]') specifies that the '~' character expects '+' or '-' input.
+ * Defaults to null, meaning using the map as described in {@link mask}.
+ */
+ public $charMap;
+ /**
+ * @var string the character prompting for user input. Defaults to underscore '_'.
+ */
+ public $placeholder;
+ /**
+ * @var string a JavaScript function callback that will be invoked when user finishes the input.
+ */
+ public $completed;
+
+ /**
+ * Executes the widget.
+ * This method registers all needed client scripts and renders
+ * the text field.
+ */
+ public function run()
+ {
+ if($this->mask=='')
+ throw new CException(Yii::t('yii','Property CMaskedTextField.mask cannot be empty.'));
+
+ list($name,$id)=$this->resolveNameID();
+ if(isset($this->htmlOptions['id']))
+ $id=$this->htmlOptions['id'];
+ else
+ $this->htmlOptions['id']=$id;
+ if(isset($this->htmlOptions['name']))
+ $name=$this->htmlOptions['name'];
+
+ $this->registerClientScript();
+
+ if($this->hasModel())
+ echo CHtml::activeTextField($this->model,$this->attribute,$this->htmlOptions);
+ else
+ echo CHtml::textField($name,$this->value,$this->htmlOptions);
+ }
+
+ /**
+ * Registers the needed CSS and JavaScript.
+ */
+ public function registerClientScript()
+ {
+ $id=$this->htmlOptions['id'];
+ $miOptions=$this->getClientOptions();
+ $options=$miOptions!==array() ? ','.CJavaScript::encode($miOptions) : '';
+ $js='';
+ if(is_array($this->charMap))
+ $js.='jQuery.mask.definitions='.CJavaScript::encode($this->charMap).";\n";
+ $js.="jQuery(\"#{$id}\").mask(\"{$this->mask}\"{$options});";
+
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('maskedinput');
+ $cs->registerScript('Yii.CMaskedTextField#'.$id,$js);
+ }
+
+ /**
+ * @return array the options for the text field
+ */
+ protected function getClientOptions()
+ {
+ $options=array();
+ if($this->placeholder!==null)
+ $options['placeholder']=$this->placeholder;
+ if(is_string($this->completed))
+ {
+ if(strncmp($this->completed,'js:',3))
+ $options['completed']='js:'.$this->completed;
+ else
+ $options['completed']=$this->completed;
+ }
+ return $options;
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/CMultiFileUpload.php b/framework/web/widgets/CMultiFileUpload.php
new file mode 100644
index 0000000..60ef23e
--- /dev/null
+++ b/framework/web/widgets/CMultiFileUpload.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * CMultiFileUpload class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CMultiFileUpload generates a file input that can allow uploading multiple files at a time.
+ *
+ * This is based on the {@link http://www.fyneworks.com/jquery/multiple-file-upload/ jQuery Multi File Upload plugin}.
+ * The uploaded file information can be accessed via $_FILES[widget-name], which gives an array of the uploaded
+ * files. Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'.
+ *
+ * Example:
+ * <pre>
+ * <?php
+ * $this->widget('CMultiFileUpload', array(
+ * 'model'=>$model,
+ * 'attribute'=>'files',
+ * 'accept'=>'jpg|gif',
+ * 'options'=>array(
+ * 'onFileSelect'=>'function(e, v, m){ alert("onFileSelect - "+v) }',
+ * 'afterFileSelect'=>'function(e, v, m){ alert("afterFileSelect - "+v) }',
+ * 'onFileAppend'=>'function(e, v, m){ alert("onFileAppend - "+v) }',
+ * 'afterFileAppend'=>'function(e, v, m){ alert("afterFileAppend - "+v) }',
+ * 'onFileRemove'=>'function(e, v, m){ alert("onFileRemove - "+v) }',
+ * 'afterFileRemove'=>'function(e, v, m){ alert("afterFileRemove - "+v) }',
+ * ),
+ * ));
+ * ?>
+ * </pre>
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CMultiFileUpload.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CMultiFileUpload extends CInputWidget
+{
+ /**
+ * @var string the file types that are allowed (eg "gif|jpg").
+ * Note, the server side still needs to check if the uploaded files have allowed types.
+ */
+ public $accept;
+ /**
+ * @var integer the maximum number of files that can be uploaded. If -1, it means no limits. Defaults to -1.
+ */
+ public $max=-1;
+ /**
+ * @var string the label for the remove button. Defaults to "Remove".
+ */
+ public $remove;
+ /**
+ * @var string message that is displayed when a file type is not allowed.
+ */
+ public $denied;
+ /**
+ * @var string message that is displayed when a file is selected.
+ */
+ public $selected;
+ /**
+ * @var string message that is displayed when a file appears twice.
+ */
+ public $duplicate;
+ /**
+ * @var string the message template for displaying the uploaded file name
+ * @since 1.1.3
+ */
+ public $file;
+ /**
+ * @var array additional options that can be passed to the constructor of the multifile js object.
+ * @since 1.1.7
+ */
+ public $options=array();
+
+
+ /**
+ * Runs the widget.
+ * This method registers all needed client scripts and renders
+ * the multiple file uploader.
+ */
+ public function run()
+ {
+ list($name,$id)=$this->resolveNameID();
+ if(substr($name,-2)!=='[]')
+ $name.='[]';
+ if(isset($this->htmlOptions['id']))
+ $id=$this->htmlOptions['id'];
+ else
+ $this->htmlOptions['id']=$id;
+ $this->registerClientScript();
+ echo CHtml::fileField($name,'',$this->htmlOptions);
+ }
+
+ /**
+ * Registers the needed CSS and JavaScript.
+ */
+ public function registerClientScript()
+ {
+ $id=$this->htmlOptions['id'];
+
+ $options=$this->getClientOptions();
+ $options=$options===array()? '' : CJavaScript::encode($options);
+
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('multifile');
+ $cs->registerScript('Yii.CMultiFileUpload#'.$id,"jQuery(\"#{$id}\").MultiFile({$options});");
+ }
+
+ /**
+ * @return array the javascript options
+ */
+ protected function getClientOptions()
+ {
+ $options=$this->options;
+ foreach(array('onFileRemove','afterFileRemove','onFileAppend','afterFileAppend','onFileSelect','afterFileSelect') as $event)
+ {
+ if(isset($options[$event]) && strpos($options[$event],'js:')!==0)
+ $options[$event]='js:'.$options[$event];
+ }
+
+ if($this->accept!==null)
+ $options['accept']=$this->accept;
+ if($this->max>0)
+ $options['max']=$this->max;
+
+ $messages=array();
+ foreach(array('remove','denied','selected','duplicate','file') as $messageName)
+ {
+ if($this->$messageName!==null)
+ $messages[$messageName]=$this->$messageName;
+ }
+ if($messages!==array())
+ $options['STRING']=$messages;
+
+ return $options;
+ }
+}
diff --git a/framework/web/widgets/COutputCache.php b/framework/web/widgets/COutputCache.php
new file mode 100644
index 0000000..1878ba2
--- /dev/null
+++ b/framework/web/widgets/COutputCache.php
@@ -0,0 +1,347 @@
+<?php
+/**
+ * COutputCache class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * COutputCache enables caching the output generated by an action or a view fragment.
+ *
+ * If the output to be displayed is found valid in cache, the cached
+ * version will be displayed instead, which saves the time for generating
+ * the original output.
+ *
+ * Since COutputCache extends from {@link CFilterWidget}, it can be used
+ * as either a filter (for action caching) or a widget (for fragment caching).
+ * For the latter, the shortcuts {@link CBaseController::beginCache()} and {@link CBaseController::endCache()}
+ * are often used instead, like the following in a view file:
+ * <pre>
+ * if($this->beginCache('cacheName',array('property1'=>'value1',...))
+ * {
+ * // ... display the content to be cached here
+ * $this->endCache();
+ * }
+ * </pre>
+ *
+ * COutputCache must work with a cache application component specified via {@link cacheID}.
+ * If the cache application component is not available, COutputCache will be disabled.
+ *
+ * The validity of the cached content is determined based on two factors:
+ * the {@link duration} and the cache {@link dependency}.
+ * The former specifies the number of seconds that the data can remain
+ * valid in cache (defaults to 60s), while the latter specifies conditions
+ * that the cached data depends on. If a dependency changes,
+ * (e.g. relevant data in DB are updated), the cached data will be invalidated.
+ * For more details about cache dependency, see {@link CCacheDependency}.
+ *
+ * Sometimes, it is necessary to turn off output caching only for certain request types.
+ * For example, we only want to cache a form when it is initially requested;
+ * any subsequent display of the form should not be cached because it contains user input.
+ * We can set {@link requestTypes} to be <code>array('GET')</code> to accomplish this task.
+ *
+ * The content fetched from cache may be variated with respect to
+ * some parameters. COutputCache supports four kinds of variations:
+ * <ul>
+ * <li>{@link varyByRoute}: this specifies whether the cached content
+ * should be varied with the requested route (controller and action)</li>
+ * <li>{@link varyByParam}: this specifies a list of GET parameter names
+ * and uses the corresponding values to determine the version of the cached content.</li>
+ * <li>{@link varyBySession}: this specifies whether the cached content
+ * should be varied with the user session.</li>
+ * <li>{@link varyByExpression}: this specifies whether the cached content
+ * should be varied with the result of the specified PHP expression.</li>
+ * </ul>
+ * For more advanced variation, override {@link getBaseCacheKey()} method.
+ *
+ * @property boolean $isContentCached Whether the content can be found from cache.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: COutputCache.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class COutputCache extends CFilterWidget
+{
+ /**
+ * Prefix to the keys for storing cached data
+ */
+ const CACHE_KEY_PREFIX='Yii.COutputCache.';
+
+ /**
+ * @var integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
+ * If it is 0, existing cached content would be removed from the cache.
+ * If it is a negative value, the cache will be disabled (any existing cached content will
+ * remain in the cache.)
+ *
+ * Note, if cache dependency changes or cache space is limited,
+ * the data may be purged out of cache earlier.
+ */
+ public $duration=60;
+ /**
+ * @var boolean whether the content being cached should be differentiated according to route.
+ * A route consists of the requested controller ID and action ID.
+ * Defaults to true.
+ */
+ public $varyByRoute=true;
+ /**
+ * @var boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
+ */
+ public $varyBySession=false;
+ /**
+ * @var array list of GET parameters that should participate in cache key calculation.
+ * By setting this property, the output cache will use different cached data
+ * for each different set of GET parameter values.
+ */
+ public $varyByParam;
+ /**
+ * @var string a PHP expression whose result is used in the cache key calculation.
+ * By setting this property, the output cache will use different cached data
+ * for each different expression result.
+ * The expression can also be a valid PHP callback,
+ * including class method name (array(ClassName/Object, MethodName)),
+ * or anonymous function (PHP 5.3.0+). The function/method signature should be as follows:
+ * <pre>
+ * function foo($cache) { ... }
+ * </pre>
+ * where $cache refers to the output cache component.
+ */
+ public $varyByExpression;
+ /**
+ * @var array list of request types (e.g. GET, POST) for which the cache should be enabled only.
+ * Defaults to null, meaning all request types.
+ */
+ public $requestTypes;
+ /**
+ * @var string the ID of the cache application component. Defaults to 'cache' (the primary cache application component.)
+ */
+ public $cacheID='cache';
+ /**
+ * @var mixed the dependency that the cached content depends on.
+ * This can be either an object implementing {@link ICacheDependency} interface or an array
+ * specifying the configuration of the dependency object. For example,
+ * <pre>
+ * array(
+ * 'class'=>'CDbCacheDependency',
+ * 'sql'=>'SELECT MAX(lastModified) FROM Post',
+ * )
+ * </pre>
+ * would make the output cache depends on the last modified time of all posts.
+ * If any post has its modification time changed, the cached content would be invalidated.
+ */
+ public $dependency;
+
+ private $_key;
+ private $_cache;
+ private $_contentCached;
+ private $_content;
+ private $_actions;
+
+ /**
+ * Performs filtering before the action is executed.
+ * This method is meant to be overridden by child classes if begin-filtering is needed.
+ * @param CFilterChain $filterChain list of filters being applied to an action
+ * @return boolean whether the filtering process should stop after this filter. Defaults to false.
+ */
+ public function filter($filterChain)
+ {
+ if(!$this->getIsContentCached())
+ $filterChain->run();
+ $this->run();
+ }
+
+ /**
+ * Marks the start of content to be cached.
+ * Content displayed after this method call and before {@link endCache()}
+ * will be captured and saved in cache.
+ * This method does nothing if valid content is already found in cache.
+ */
+ public function init()
+ {
+ if($this->getIsContentCached())
+ $this->replayActions();
+ else if($this->_cache!==null)
+ {
+ $this->getController()->getCachingStack()->push($this);
+ ob_start();
+ ob_implicit_flush(false);
+ }
+ }
+
+ /**
+ * Marks the end of content to be cached.
+ * Content displayed before this method call and after {@link init()}
+ * will be captured and saved in cache.
+ * This method does nothing if valid content is already found in cache.
+ */
+ public function run()
+ {
+ if($this->getIsContentCached())
+ {
+ if($this->getController()->isCachingStackEmpty())
+ echo $this->getController()->processDynamicOutput($this->_content);
+ else
+ echo $this->_content;
+ }
+ else if($this->_cache!==null)
+ {
+ $this->_content=ob_get_clean();
+ $this->getController()->getCachingStack()->pop();
+ $data=array($this->_content,$this->_actions);
+ if(is_array($this->dependency))
+ $this->dependency=Yii::createComponent($this->dependency);
+ $this->_cache->set($this->getCacheKey(),$data,$this->duration,$this->dependency);
+
+ if($this->getController()->isCachingStackEmpty())
+ echo $this->getController()->processDynamicOutput($this->_content);
+ else
+ echo $this->_content;
+ }
+ }
+
+ /**
+ * @return boolean whether the content can be found from cache
+ */
+ public function getIsContentCached()
+ {
+ if($this->_contentCached!==null)
+ return $this->_contentCached;
+ else
+ return $this->_contentCached=$this->checkContentCache();
+ }
+
+ /**
+ * Looks for content in cache.
+ * @return boolean whether the content is found in cache.
+ */
+ protected function checkContentCache()
+ {
+ if((empty($this->requestTypes) || in_array(Yii::app()->getRequest()->getRequestType(),$this->requestTypes))
+ && ($this->_cache=$this->getCache())!==null)
+ {
+ if($this->duration>0 && ($data=$this->_cache->get($this->getCacheKey()))!==false)
+ {
+ $this->_content=$data[0];
+ $this->_actions=$data[1];
+ return true;
+ }
+ if($this->duration==0)
+ $this->_cache->delete($this->getCacheKey());
+ if($this->duration<=0)
+ $this->_cache=null;
+ }
+ return false;
+ }
+
+ /**
+ * @return ICache the cache used for caching the content.
+ */
+ protected function getCache()
+ {
+ return Yii::app()->getComponent($this->cacheID);
+ }
+
+ /**
+ * Caclulates the base cache key.
+ * The calculated key will be further variated in {@link getCacheKey}.
+ * Derived classes may override this method if more variations are needed.
+ * @return string basic cache key without variations
+ */
+ protected function getBaseCacheKey()
+ {
+ return self::CACHE_KEY_PREFIX.$this->getId().'.';
+ }
+
+ /**
+ * Calculates the cache key.
+ * The key is calculated based on {@link getBaseCacheKey} and other factors, including
+ * {@link varyByRoute}, {@link varyByParam} and {@link varyBySession}.
+ * @return string cache key
+ */
+ protected function getCacheKey()
+ {
+ if($this->_key!==null)
+ return $this->_key;
+ else
+ {
+ $key=$this->getBaseCacheKey().'.';
+ if($this->varyByRoute)
+ {
+ $controller=$this->getController();
+ $key.=$controller->getUniqueId().'/';
+ if(($action=$controller->getAction())!==null)
+ $key.=$action->getId();
+ }
+ $key.='.';
+
+ if($this->varyBySession)
+ $key.=Yii::app()->getSession()->getSessionID();
+ $key.='.';
+
+ if(is_array($this->varyByParam) && isset($this->varyByParam[0]))
+ {
+ $params=array();
+ foreach($this->varyByParam as $name)
+ {
+ if(isset($_GET[$name]))
+ $params[$name]=$_GET[$name];
+ else
+ $params[$name]='';
+ }
+ $key.=serialize($params);
+ }
+ $key.='.';
+
+ if($this->varyByExpression!==null)
+ $key.=$this->evaluateExpression($this->varyByExpression);
+ $key.='.';
+
+ return $this->_key=$key;
+ }
+ }
+
+ /**
+ * Records a method call when this output cache is in effect.
+ * When the content is served from the output cache, the recorded
+ * method will be re-invoked.
+ * @param string $context a property name of the controller. The property should refer to an object
+ * whose method is being recorded. If empty it means the controller itself.
+ * @param string $method the method name
+ * @param array $params parameters passed to the method
+ */
+ public function recordAction($context,$method,$params)
+ {
+ $this->_actions[]=array($context,$method,$params);
+ }
+
+ /**
+ * Replays the recorded method calls.
+ */
+ protected function replayActions()
+ {
+ if(empty($this->_actions))
+ return;
+ $controller=$this->getController();
+ $cs=Yii::app()->getClientScript();
+ foreach($this->_actions as $action)
+ {
+ if($action[0]==='clientScript')
+ $object=$cs;
+ else if($action[0]==='')
+ $object=$controller;
+ else
+ $object=$controller->{$action[0]};
+ if(method_exists($object,$action[1]))
+ call_user_func_array(array($object,$action[1]),$action[2]);
+ else if($action[0]==='' && function_exists($action[1]))
+ call_user_func_array($action[1],$action[2]);
+ else
+ throw new CException(Yii::t('yii','Unable to replay the action "{object}.{method}". The method does not exist.',
+ array('object'=>$action[0],
+ 'method'=>$action[1])));
+ }
+ }
+}
diff --git a/framework/web/widgets/COutputProcessor.php b/framework/web/widgets/COutputProcessor.php
new file mode 100644
index 0000000..1fc8ffd
--- /dev/null
+++ b/framework/web/widgets/COutputProcessor.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * COutputProcessor class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * COutputProcessor transforms the content into a different format.
+ *
+ * COutputProcessor captures the output generated by an action or a view fragment
+ * and passes it to its {@link onProcessOutput} event handlers for further processing.
+ *
+ * The event handler may process the output and store it back to the {@link COutputEvent::output}
+ * property. By setting the {@link CEvent::handled handled} property of the event parameter
+ * to true, the output will not be echoed anymore. Otherwise (by default), the output will be echoed.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: COutputProcessor.php 2799 2011-01-01 19:31:13Z qiang.xue $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class COutputProcessor extends CFilterWidget
+{
+ /**
+ * Initializes the widget.
+ * This method starts the output buffering.
+ */
+ public function init()
+ {
+ ob_start();
+ ob_implicit_flush(false);
+ }
+
+ /**
+ * Executes the widget.
+ * This method stops output buffering and processes the captured output.
+ */
+ public function run()
+ {
+ $output=ob_get_clean();
+ $this->processOutput($output);
+ }
+
+ /**
+ * Processes the captured output.
+ *
+ * The default implementation raises an {@link onProcessOutput} event.
+ * If the event is not handled by any event handler, the output will be echoed.
+ *
+ * @param string $output the captured output to be processed
+ */
+ public function processOutput($output)
+ {
+ if($this->hasEventHandler('onProcessOutput'))
+ {
+ $event=new COutputEvent($this,$output);
+ $this->onProcessOutput($event);
+ if(!$event->handled)
+ echo $output;
+ }
+ else
+ echo $output;
+ }
+
+ /**
+ * Raised when the output has been captured.
+ * @param COutputEvent $event event parameter
+ */
+ public function onProcessOutput($event)
+ {
+ $this->raiseEvent('onProcessOutput',$event);
+ }
+}
diff --git a/framework/web/widgets/CStarRating.php b/framework/web/widgets/CStarRating.php
new file mode 100644
index 0000000..5a50c8e
--- /dev/null
+++ b/framework/web/widgets/CStarRating.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * CStarRating class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CStarRating displays a star rating control that can collect user rating input.
+ *
+ * CStarRating is based on {@link http://www.fyneworks.com/jquery/star-rating/ jQuery Star Rating Plugin}.
+ * It displays a list of stars indicating the rating values. Users can toggle these stars
+ * to indicate their rating input. On the server side, when the rating input is submitted,
+ * the value can be retrieved in the same way as working with a normal HTML input.
+ * For example, using
+ * <pre>
+ * $this->widget('CStarRating',array('name'=>'rating'));
+ * </pre>
+ * we can retrieve the rating value via <code>$_POST['rating']</code>.
+ *
+ * CStarRating allows customization of its appearance. It also supports empty rating as well as read-only rating.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CStarRating.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CStarRating extends CInputWidget
+{
+ /**
+ * @var integer the number of stars. Defaults to 5.
+ */
+ public $starCount=5;
+ /**
+ * @var mixed the minimum rating allowed. This can be either an integer or a float value. Defaults to 1.
+ */
+ public $minRating=1;
+ /**
+ * @var mixed the maximum rating allowed. This can be either an integer or a float value. Defaults to 10.
+ */
+ public $maxRating=10;
+ /**
+ * @var mixed the step size of rating. This is the minimum difference between two rating values. Defaults to 1.
+ */
+ public $ratingStepSize=1;
+ /**
+ * @var mixed the CSS file used for the widget. Defaults to null, meaning
+ * using the default CSS file included together with the widget.
+ * If false, no CSS file will be used. Otherwise, the specified CSS file
+ * will be included when using this widget.
+ */
+ public $cssFile;
+ /**
+ * @var array the titles associated with the rating options. The keys are ratings and the values are the corresponding titles.
+ * Defaults to null, meaning using the rating value as the title.
+ */
+ public $titles;
+ /**
+ * @var string the hint text for the reset button. Defaults to null, meaning using the system-defined text (which is 'Cancel Rating').
+ */
+ public $resetText;
+ /**
+ * @var string the value taken when the rating is cleared. Defaults to null, meaning using the system-defined value (which is '').
+ */
+ public $resetValue;
+ /**
+ * @var boolean whether the rating value can be empty (not set). Defaults to true.
+ * When this is true, a reset button will be displayed in front of stars.
+ */
+ public $allowEmpty;
+ /**
+ * @var integer the width of star image. Defaults to null, meaning using the system-defined value (which is 16).
+ */
+ public $starWidth;
+ /**
+ * @var boolean whether the rating value is read-only or not. Defaults to false.
+ * When this is true, the rating cannot be changed.
+ */
+ public $readOnly;
+ /**
+ * @var string Callback when the stars are focused.
+ */
+ public $focus;
+ /**
+ * @var string Callback when the stars are not focused.
+ */
+ public $blur;
+ /**
+ * @var string Callback when the stars are clicked.
+ */
+ public $callback;
+
+
+ /**
+ * Executes the widget.
+ * This method registers all needed client scripts and renders
+ * the text field.
+ */
+ public function run()
+ {
+ list($name,$id)=$this->resolveNameID();
+ if(isset($this->htmlOptions['id']))
+ $id=$this->htmlOptions['id'];
+ else
+ $this->htmlOptions['id']=$id;
+ if(isset($this->htmlOptions['name']))
+ $name=$this->htmlOptions['name'];
+
+ $this->registerClientScript($id);
+
+ echo CHtml::openTag('span',$this->htmlOptions)."\n";
+ $this->renderStars($id,$name);
+ echo "</span>";
+ }
+
+ /**
+ * Registers the necessary javascript and css scripts.
+ * @param string $id the ID of the container
+ */
+ public function registerClientScript($id)
+ {
+ $jsOptions=$this->getClientOptions();
+ $jsOptions=empty($jsOptions) ? '' : CJavaScript::encode($jsOptions);
+ $js="jQuery('#{$id} > input').rating({$jsOptions});";
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('rating');
+ $cs->registerScript('Yii.CStarRating#'.$id,$js);
+
+ if($this->cssFile!==false)
+ self::registerCssFile($this->cssFile);
+ }
+
+ /**
+ * Registers the needed CSS file.
+ * @param string $url the CSS URL. If null, a default CSS URL will be used.
+ */
+ public static function registerCssFile($url=null)
+ {
+ $cs=Yii::app()->getClientScript();
+ if($url===null)
+ $url=$cs->getCoreScriptUrl().'/rating/jquery.rating.css';
+ $cs->registerCssFile($url);
+ }
+
+ /**
+ * Renders the stars.
+ * @param string $id the ID of the container
+ * @param string $name the name of the input
+ */
+ protected function renderStars($id,$name)
+ {
+ $inputCount=(int)(($this->maxRating-$this->minRating)/$this->ratingStepSize+1);
+ $starSplit=(int)($inputCount/$this->starCount);
+ if($this->hasModel())
+ {
+ $attr=$this->attribute;
+ CHtml::resolveName($this->model,$attr);
+ $selection=$this->model->$attr;
+ }
+ else
+ $selection=$this->value;
+ $options=$starSplit>1 ? array('class'=>"{split:{$starSplit}}") : array();
+ for($value=$this->minRating, $i=0;$i<$inputCount; ++$i, $value+=$this->ratingStepSize)
+ {
+ $options['id']=$id.'_'.$i;
+ $options['value']=$value;
+ if(isset($this->titles[$value]))
+ $options['title']=$this->titles[$value];
+ else
+ unset($options['title']);
+ echo CHtml::radioButton($name,!strcmp($value,$selection),$options) . "\n";
+ }
+ }
+
+ /**
+ * @return array the javascript options for the star rating
+ */
+ protected function getClientOptions()
+ {
+ $options=array();
+ if($this->resetText!==null)
+ $options['cancel']=$this->resetText;
+ if($this->resetValue!==null)
+ $options['cancelValue']=$this->resetValue;
+ if($this->allowEmpty===false)
+ $options['required']=true;
+ if($this->starWidth!==null)
+ $options['starWidth']=$this->starWidth;
+ if($this->readOnly===true)
+ $options['readOnly']=true;
+ if($this->focus!==null)
+ {
+ if(strncmp($this->focus,'js:',3))
+ $options['focus']='js:'.$this->focus;
+ else
+ $options['focus']=$this->focus;
+ }
+ if($this->blur!==null)
+ {
+ if(strncmp($this->blur,'js:',3))
+ $options['blur']='js:'.$this->blur;
+ else
+ $options['blur']=$this->blur;
+ }
+ if($this->callback!==null)
+ {
+ if(strncmp($this->callback,'js:',3))
+ $options['callback']='js:'.$this->callback;
+ else
+ $options['callback']=$this->callback;
+ }
+ return $options;
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/CTabView.php b/framework/web/widgets/CTabView.php
new file mode 100644
index 0000000..89b3e03
--- /dev/null
+++ b/framework/web/widgets/CTabView.php
@@ -0,0 +1,212 @@
+<?php
+/**
+ * CTabView class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CTabView displays contents in multiple tabs.
+ *
+ * At any time, only one tab is visible. Users can click on the tab header
+ * to switch to see another tab of content.
+ *
+ * JavaScript is used to control the tab switching. If JavaScript is disabled,
+ * CTabView still manages to display the content in a semantically appropriate way.
+ *
+ * To specify contents and their tab structure, configure the {@link tabs} property.
+ * The {@link tabs} property takes an array with tab ID being mapped tab definition.
+ * Each tab definition is an array of the following structure:
+ * <ul>
+ * <li>title: the tab title.</li>
+ * <li>content: the content to be displayed in the tab.</li>
+ * <li>view: the name of the view to be displayed in this tab.
+ * The view will be rendered using the current controller's
+ * {@link CController::renderPartial} method.
+ * When both 'content' and 'view' are specified, 'content' will take precedence.
+ * </li>
+ * <li>url: a URL that the user browser will be redirected to when clicking on this tab.</li>
+ * <li>data: array (name=>value), this will be passed to the view when 'view' is specified.</li>
+ * </ul>
+ *
+ * For example, the {@link tabs} property can be configured as follows,
+ * <pre>
+ * array(
+ * 'tab1'=>array(
+ * 'title'=>'tab 1 title',
+ * 'view'=>'view1',
+ * 'data'=>array('model'=>$model),
+ * ),
+ * 'tab2'=>array(
+ * 'title'=>'tab 2 title',
+ * 'url'=>'http://www.yiiframework.com/',
+ * ),
+ * )
+ * </pre>
+ *
+ * By default, the first tab will be activated. To activate a different tab
+ * when the page is initially loaded, set {@link activeTab} to be the ID of the desired tab.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CTabView.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CTabView extends CWidget
+{
+ /**
+ * Default CSS class for the tab container
+ */
+ const CSS_CLASS='yiiTab';
+
+ /**
+ * @var mixed the CSS file used for the widget. Defaults to null, meaning
+ * using the default CSS file included together with the widget.
+ * If false, no CSS file will be used. Otherwise, the specified CSS file
+ * will be included when using this widget.
+ */
+ public $cssFile;
+ /**
+ * @var string the ID of the tab that should be activated when the page is initially loaded.
+ * If not set, the first tab will be activated.
+ */
+ public $activeTab;
+ /**
+ * @var array the data that will be passed to the partial view rendered by each tab.
+ */
+ public $viewData;
+ /**
+ * @var array additional HTML options to be rendered in the container tag.
+ */
+ public $htmlOptions;
+ /**
+ * @var array tab definitions. The array keys are the IDs,
+ * and the array values are the corresponding tab contents.
+ * Each array value must be an array with the following elements:
+ * <ul>
+ * <li>title: the tab title. You need to make sure this is HTML-encoded.</li>
+ * <li>content: the content to be displayed in the tab.</li>
+ * <li>view: the name of the view to be displayed in this tab.
+ * The view will be rendered using the current controller's
+ * {@link CController::renderPartial} method.
+ * When both 'content' and 'view' are specified, 'content' will take precedence.
+ * </li>
+ * <li>url: a URL that the user browser will be redirected to when clicking on this tab.</li>
+ * <li>data: array (name=>value), this will be passed to the view when 'view' is specified.
+ * This option is available since version 1.1.1.</li>
+ * </ul>
+ * <pre>
+ * array(
+ * 'tab1'=>array(
+ * 'title'=>'tab 1 title',
+ * 'view'=>'view1',
+ * ),
+ * 'tab2'=>array(
+ * 'title'=>'tab 2 title',
+ * 'url'=>'http://www.yiiframework.com/',
+ * ),
+ * )
+ * </pre>
+ */
+ public $tabs=array();
+
+ /**
+ * Runs the widget.
+ */
+ public function run()
+ {
+ if(empty($this->tabs))
+ return;
+
+ if($this->activeTab===null || !isset($this->tabs[$this->activeTab]))
+ {
+ reset($this->tabs);
+ list($this->activeTab, )=each($this->tabs);
+ }
+
+ $htmlOptions=$this->htmlOptions;
+ $htmlOptions['id']=$this->getId();
+ if(!isset($htmlOptions['class']))
+ $htmlOptions['class']=self::CSS_CLASS;
+
+ $this->registerClientScript();
+
+ echo CHtml::openTag('div',$htmlOptions)."\n";
+ $this->renderHeader();
+ $this->renderBody();
+ echo CHtml::closeTag('div');
+ }
+
+ /**
+ * Registers the needed CSS and JavaScript.
+ */
+ public function registerClientScript()
+ {
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('yiitab');
+ $id=$this->getId();
+ $cs->registerScript('Yii.CTabView#'.$id,"jQuery(\"#{$id}\").yiitab();");
+
+ if($this->cssFile!==false)
+ self::registerCssFile($this->cssFile);
+ }
+
+ /**
+ * Registers the needed CSS file.
+ * @param string $url the CSS URL. If null, a default CSS URL will be used.
+ */
+ public static function registerCssFile($url=null)
+ {
+ $cs=Yii::app()->getClientScript();
+ if($url===null)
+ $url=$cs->getCoreScriptUrl().'/yiitab/jquery.yiitab.css';
+ $cs->registerCssFile($url,'screen');
+ }
+
+ /**
+ * Renders the header part.
+ */
+ protected function renderHeader()
+ {
+ echo "<ul class=\"tabs\">\n";
+ foreach($this->tabs as $id=>$tab)
+ {
+ $title=isset($tab['title'])?$tab['title']:'undefined';
+ $active=$id===$this->activeTab?' class="active"' : '';
+ $url=isset($tab['url'])?$tab['url']:"#{$id}";
+ echo "<li><a href=\"{$url}\"{$active}>{$title}</a></li>\n";
+ }
+ echo "</ul>\n";
+ }
+
+ /**
+ * Renders the body part.
+ */
+ protected function renderBody()
+ {
+ foreach($this->tabs as $id=>$tab)
+ {
+ $inactive=$id!==$this->activeTab?' style="display:none"' : '';
+ echo "<div class=\"view\" id=\"{$id}\"{$inactive}>\n";
+ if(isset($tab['content']))
+ echo $tab['content'];
+ else if(isset($tab['view']))
+ {
+ if(isset($tab['data']))
+ {
+ if(is_array($this->viewData))
+ $data=array_merge($this->viewData, $tab['data']);
+ else
+ $data=$tab['data'];
+ }
+ else
+ $data=$this->viewData;
+ $this->getController()->renderPartial($tab['view'], $data);
+ }
+ echo "</div><!-- {$id} -->\n";
+ }
+ }
+}
diff --git a/framework/web/widgets/CTextHighlighter.php b/framework/web/widgets/CTextHighlighter.php
new file mode 100644
index 0000000..b2a48a2
--- /dev/null
+++ b/framework/web/widgets/CTextHighlighter.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * CTextHighlighter class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter').'.php');
+require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter.Renderer.Html').'.php');
+
+/**
+ * CTextHighlighter does syntax highlighting for its body content.
+ *
+ * The language of the syntax to be applied is specified via {@link language} property.
+ * Currently, CTextHighlighter supports the following languages:
+ * ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT, MYSQL, PERL,
+ * PHP, PYTHON, RUBY, SQL, XML. By setting {@link showLineNumbers}
+ * to true, the highlighted result may be shown with line numbers.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CTextHighlighter.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CTextHighlighter extends COutputProcessor
+{
+ /**
+ * @var string the language whose syntax is to be used for highlighting.
+ * Valid values are those file names (without suffix) that are contained
+ * in 'vendors/TextHighlighter/Text/Highlighter'. Currently, the following
+ * languages are supported:
+ * ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT,
+ * MYSQL, PERL, PHP, PYTHON, RUBY, SQL, XML
+ * If a language is not supported, it will be displayed as plain text.
+ * Language names are case-insensitive.
+ */
+ public $language;
+ /**
+ * @var boolean whether to show line numbers in the highlighted result. Defaults to false.
+ * @see lineNumberStyle
+ */
+ public $showLineNumbers=false;
+ /**
+ * @var string the style of line number display. It can be either 'list' or 'table'. Defaults to 'list'.
+ * @see showLineNumbers
+ */
+ public $lineNumberStyle='list';
+ /**
+ * @var integer tab size. Defaults to 4.
+ */
+ public $tabSize=4;
+ /**
+ * @var mixed the CSS file used for the widget. Defaults to null, meaning
+ * using the default CSS file included together with the widget.
+ * If false, no CSS file will be used. Otherwise, the specified CSS file
+ * will be included when using this widget.
+ */
+ public $cssFile;
+ /**
+ * @var array the HTML attributes to be applied to the container element.
+ * The highlighted content is contained in a DIV element.
+ */
+ public $containerOptions=array();
+
+
+ /**
+ * Processes the captured output.
+ * This method highlights the output according to the syntax of the specified {@link language}.
+ * @param string $output the captured output to be processed
+ */
+ public function processOutput($output)
+ {
+ $output=$this->highlight($output);
+ parent::processOutput($output);
+ }
+
+ /**
+ * Highlights the content by the syntax of the specified language.
+ * @param string $content the content to be highlighted.
+ * @return string the highlighted content
+ */
+ public function highlight($content)
+ {
+ $this->registerClientScript();
+
+ $options['use_language']=true;
+ $options['tabsize']=$this->tabSize;
+ if($this->showLineNumbers)
+ $options['numbers']=($this->lineNumberStyle==='list')?HL_NUMBERS_LI:HL_NUMBERS_TABLE;
+
+ $highlighter=empty($this->language)?false:Text_Highlighter::factory($this->language);
+ if($highlighter===false)
+ $o='<pre>'.CHtml::encode($content).'</pre>';
+ else
+ {
+ $highlighter->setRenderer(new Text_Highlighter_Renderer_Html($options));
+ $o=preg_replace('/<span\s+[^>]*>(\s*)<\/span>/','\1',$highlighter->highlight($content));
+ }
+
+ return CHtml::tag('div',$this->containerOptions,$o);
+ }
+
+ /**
+ * Registers the needed CSS and JavaScript.
+ */
+ public function registerClientScript()
+ {
+ if($this->cssFile!==false)
+ self::registerCssFile($this->cssFile);
+ }
+
+ /**
+ * Registers the needed CSS file.
+ * @param string $url the CSS URL. If null, a default CSS URL will be used.
+ */
+ public static function registerCssFile($url=null)
+ {
+ if($url===null)
+ $url=CHtml::asset(Yii::getPathOfAlias('system.vendors.TextHighlighter.highlight').'.css');
+ Yii::app()->getClientScript()->registerCssFile($url);
+ }
+}
diff --git a/framework/web/widgets/CTreeView.php b/framework/web/widgets/CTreeView.php
new file mode 100644
index 0000000..a10d5a7
--- /dev/null
+++ b/framework/web/widgets/CTreeView.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ * CTreeView class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CTreeView displays a tree view of hierarchical data.
+ *
+ * It encapsulates the excellent tree view plugin for jQuery
+ * ({@link http://bassistance.de/jquery-plugins/jquery-plugin-treeview/}).
+ *
+ * To use CTreeView, simply sets {@link data} to the data that you want
+ * to present and you are there.
+ *
+ * CTreeView also supports dynamic data loading via AJAX. To do so, set
+ * {@link url} to be the URL that can serve the tree view data upon request.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CTreeView.php 3144 2011-03-30 07:03:48Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CTreeView extends CWidget
+{
+ /**
+ * @var array the data that can be used to generate the tree view content.
+ * Each array element corresponds to a tree view node with the following structure:
+ * <ul>
+ * <li>text: string, required, the HTML text associated with this node.</li>
+ * <li>expanded: boolean, optional, whether the tree view node is expanded.</li>
+ * <li>id: string, optional, the ID identifying the node. This is used
+ * in dynamic loading of tree view (see {@link url}).</li>
+ * <li>hasChildren: boolean, optional, defaults to false, whether clicking on this
+ * node should trigger dynamic loading of more tree view nodes from server.
+ * The {@link url} property must be set in order to make this effective.</li>
+ * <li>children: array, optional, child nodes of this node.</li>
+ * <li>htmlOptions: array, additional HTML attributes (see {@link CHtml::tag}).
+ * This option has been available since version 1.1.7.</li>
+ * </ul>
+ * Note, anything enclosed between the beginWidget and endWidget calls will
+ * also be treated as tree view content, which appends to the content generated
+ * from this data.
+ */
+ public $data;
+ /**
+ * @var mixed the CSS file used for the widget. Defaults to null, meaning
+ * using the default CSS file included together with the widget.
+ * If false, no CSS file will be used. Otherwise, the specified CSS file
+ * will be included when using this widget.
+ */
+ public $cssFile;
+ /**
+ * @var string|array the URL to which the treeview can be dynamically loaded (in AJAX).
+ * See {@link CHtml::normalizeUrl} for possible URL formats.
+ * Setting this property will enable the dynamic treeview loading.
+ * When the page is displayed, the browser will request this URL with a GET parameter
+ * named 'root' whose value is 'source'. The server script should then generate the
+ * needed tree view data corresponding to the root of the tree (see {@link saveDataAsJson}.)
+ * When a node has a CSS class 'hasChildren', then expanding this node will also
+ * cause a dynamic loading of its child nodes. In this case, the value of the 'root' GET parameter
+ * is the 'id' property of the node.
+ */
+ public $url;
+ /**
+ * @var string|integer animation speed. This can be one of the three predefined speeds
+ * ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
+ * If not set, no animation is used.
+ */
+ public $animated;
+ /**
+ * @var boolean whether the tree should start with all branches collapsed. Defaults to false.
+ */
+ public $collapsed;
+ /**
+ * @var string container for a tree-control, allowing the user to expand, collapse and toggle all branches with one click.
+ * In the container, clicking on the first hyperlink will collapse the tree;
+ * the second hyperlink will expand the tree; while the third hyperlink will toggle the tree.
+ * The property should be a valid jQuery selector (e.g. '#treecontrol' where 'treecontrol' is
+ * the ID of the 'div' element containing the hyperlinks.)
+ */
+ public $control;
+ /**
+ * @var boolean set to allow only one branch on one level to be open (closing siblings which opening).
+ * Defaults to false.
+ */
+ public $unique;
+ /**
+ * @var string Callback when toggling a branch. Arguments: "this" refers to the UL that was shown or hidden
+ */
+ public $toggle;
+ /**
+ * @var string Persist the tree state in cookies or the page location. If set to "location", looks for
+ * the anchor that matches location.href and activates that part of the treeview it.
+ * Great for href-based state-saving. If set to "cookie", saves the state of the tree on
+ * each click to a cookie and restores that state on page load.
+ */
+ public $persist;
+ /**
+ * @var string The cookie name to use when persisting via persist:"cookie". Defaults to 'treeview'.
+ */
+ public $cookieId;
+ /**
+ * @var boolean Set to skip rendering of classes and hitarea divs, assuming that is done by the serverside. Defaults to false.
+ */
+ public $prerendered;
+ /**
+ * @var array additional options that can be passed to the constructor of the treeview js object.
+ */
+ public $options=array();
+ /**
+ * @var array additional HTML attributes that will be rendered in the UL tag.
+ * The default tree view CSS has defined the following CSS classes which can be enabled
+ * by specifying the 'class' option here:
+ * <ul>
+ * <li>treeview-black</li>
+ * <li>treeview-gray</li>
+ * <li>treeview-red</li>
+ * <li>treeview-famfamfam</li>
+ * <li>filetree</li>
+ * </ul>
+ */
+ public $htmlOptions;
+
+
+ /**
+ * Initializes the widget.
+ * This method registers all needed client scripts and renders
+ * the tree view content.
+ */
+ public function init()
+ {
+ if(isset($this->htmlOptions['id']))
+ $id=$this->htmlOptions['id'];
+ else
+ $id=$this->htmlOptions['id']=$this->getId();
+ if($this->url!==null)
+ $this->url=CHtml::normalizeUrl($this->url);
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('treeview');
+ $options=$this->getClientOptions();
+ $options=$options===array()?'{}' : CJavaScript::encode($options);
+ $cs->registerScript('Yii.CTreeView#'.$id,"jQuery(\"#{$id}\").treeview($options);");
+ if($this->cssFile===null)
+ $cs->registerCssFile($cs->getCoreScriptUrl().'/treeview/jquery.treeview.css');
+ else if($this->cssFile!==false)
+ $cs->registerCssFile($this->cssFile);
+
+ echo CHtml::tag('ul',$this->htmlOptions,false,false)."\n";
+ echo self::saveDataAsHtml($this->data);
+ }
+
+ /**
+ * Ends running the widget.
+ */
+ public function run()
+ {
+ echo "</ul>";
+ }
+
+ /**
+ * @return array the javascript options
+ */
+ protected function getClientOptions()
+ {
+ $options=$this->options;
+ foreach(array('url','animated','collapsed','control','unique','toggle','persist','cookieId','prerendered') as $name)
+ {
+ if($this->$name!==null)
+ $options[$name]=$this->$name;
+ }
+ return $options;
+ }
+
+ /**
+ * Generates tree view nodes in HTML from the data array.
+ * @param array $data the data for the tree view (see {@link data} for possible data structure).
+ * @return string the generated HTML for the tree view
+ */
+ public static function saveDataAsHtml($data)
+ {
+ $html='';
+ if(is_array($data))
+ {
+ foreach($data as $node)
+ {
+ if(!isset($node['text']))
+ continue;
+
+ if(isset($node['expanded']))
+ $css=$node['expanded'] ? 'open' : 'closed';
+ else
+ $css='';
+
+ if(isset($node['hasChildren']) && $node['hasChildren'])
+ {
+ if($css!=='')
+ $css.=' ';
+ $css.='hasChildren';
+ }
+
+ $options=isset($node['htmlOptions']) ? $node['htmlOptions'] : array();
+ if($css!=='')
+ {
+ if(isset($options['class']))
+ $options['class'].=' '.$css;
+ else
+ $options['class']=$css;
+ }
+
+ if(isset($node['id']))
+ $options['id']=$node['id'];
+
+ $html.=CHtml::tag('li',$options,$node['text'],false);
+ if(!empty($node['children']))
+ {
+ $html.="\n<ul>\n";
+ $html.=self::saveDataAsHtml($node['children']);
+ $html.="</ul>\n";
+ }
+ $html.=CHtml::closeTag('li')."\n";
+ }
+ }
+ return $html;
+ }
+
+ /**
+ * Saves tree view data in JSON format.
+ * This method is typically used in dynamic tree view loading
+ * when the server code needs to send to the client the dynamic
+ * tree view data.
+ * @param array $data the data for the tree view (see {@link data} for possible data structure).
+ * @return string the JSON representation of the data
+ */
+ public static function saveDataAsJson($data)
+ {
+ if(empty($data))
+ return '[]';
+ else
+ return CJavaScript::jsonEncode($data);
+ }
+}
diff --git a/framework/web/widgets/CWidget.php b/framework/web/widgets/CWidget.php
new file mode 100644
index 0000000..2964f67
--- /dev/null
+++ b/framework/web/widgets/CWidget.php
@@ -0,0 +1,249 @@
+<?php
+/**
+ * CWidget class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CWidget is the base class for widgets.
+ *
+ * A widget is a self-contained component that may generate presentation
+ * based on model data. It can be viewed as a micro-controller that embeds
+ * into the controller-managed views.
+ *
+ * Compared with {@link CController controller}, a widget has neither actions nor filters.
+ *
+ * Usage is described at {@link CBaseController} and {@link CBaseController::widget}.
+ *
+ * @property CBaseController $owner Owner/creator of this widget. It could be either a widget or a controller.
+ * @property string $id Id of the widget.
+ * @property CController $controller The controller that this widget belongs to.
+ * @property string $viewPath The directory containing the view files for this widget.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CWidget.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets
+ * @since 1.0
+ */
+class CWidget extends CBaseController
+{
+ /**
+ * @var string the prefix to the IDs of the {@link actions}.
+ * When a widget is declared an action provider in {@link CController::actions},
+ * a prefix can be specified to differentiate its action IDs from others.
+ * The same prefix should then also be used to configure this property
+ * when the widget is used in a view of the controller.
+ */
+ public $actionPrefix;
+ /**
+ * @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.
+ * @see CWidgetFactory
+ * @since 1.1
+ */
+ public $skin='default';
+
+ /**
+ * @var array view paths for different types of widgets
+ */
+ private static $_viewPaths;
+ /**
+ * @var integer the counter for generating implicit IDs.
+ */
+ private static $_counter=0;
+ /**
+ * @var string id of the widget.
+ */
+ private $_id;
+ /**
+ * @var CBaseController owner/creator of this widget. It could be either a widget or a controller.
+ */
+ private $_owner;
+
+ /**
+ * Returns a list of actions that are used by this widget.
+ * The structure of this method's return value is similar to
+ * that returned by {@link CController::actions}.
+ *
+ * When a widget uses several actions, you can declare these actions using
+ * this method. The widget will then become an action provider, and the actions
+ * can be easily imported into a controller.
+ *
+ * Note, when creating URLs referring to the actions listed in this method,
+ * make sure the action IDs are prefixed with {@link actionPrefix}.
+ *
+ * @return array
+ *
+ * @see actionPrefix
+ * @see CController::actions
+ */
+ public static function actions()
+ {
+ return array();
+ }
+
+ /**
+ * Constructor.
+ * @param CBaseController $owner owner/creator of this widget. It could be either a widget or a controller.
+ */
+ public function __construct($owner=null)
+ {
+ $this->_owner=$owner===null?Yii::app()->getController():$owner;
+ }
+
+ /**
+ * Returns the owner/creator of this widget.
+ * @return CBaseController owner/creator of this widget. It could be either a widget or a controller.
+ */
+ public function getOwner()
+ {
+ return $this->_owner;
+ }
+
+ /**
+ * Returns the ID of the widget or generates a new one if requested.
+ * @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($this->_id!==null)
+ return $this->_id;
+ else if($autoGenerate)
+ return $this->_id='yw'.self::$_counter++;
+ }
+
+ /**
+ * Sets the ID of the widget.
+ * @param string $value id of the widget.
+ */
+ public function setId($value)
+ {
+ $this->_id=$value;
+ }
+
+ /**
+ * Returns the controller that this widget belongs to.
+ * @return CController the controller that this widget belongs to.
+ */
+ public function getController()
+ {
+ if($this->_owner instanceof CController)
+ return $this->_owner;
+ else
+ return Yii::app()->getController();
+ }
+
+ /**
+ * Initializes the widget.
+ * This method is called by {@link CBaseController::createWidget}
+ * and {@link CBaseController::beginWidget} after the widget's
+ * properties have been initialized.
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * Executes the widget.
+ * This method is called by {@link CBaseController::endWidget}.
+ */
+ public function run()
+ {
+ }
+
+ /**
+ * Returns the directory containing the view files for this widget.
+ * The default implementation returns the 'views' subdirectory of the directory containing the widget class file.
+ * If $checkTheme is set true, the directory "ThemeID/views/ClassName" will be returned when it exists.
+ * @param boolean $checkTheme whether to check if the theme contains a view path for the widget.
+ * @return string the directory containing the view files for this widget.
+ */
+ public function getViewPath($checkTheme=false)
+ {
+ $className=get_class($this);
+ if(isset(self::$_viewPaths[$className]))
+ return self::$_viewPaths[$className];
+ else
+ {
+ if($checkTheme && ($theme=Yii::app()->getTheme())!==null)
+ {
+ $path=$theme->getViewPath().DIRECTORY_SEPARATOR;
+ if(strpos($className,'\\')!==false) // namespaced class
+ $path.=str_replace('\\','_',ltrim($className,'\\'));
+ else
+ $path.=$className;
+ if(is_dir($path))
+ return self::$_viewPaths[$className]=$path;
+ }
+
+ $class=new ReflectionClass($className);
+ return self::$_viewPaths[$className]=dirname($class->getFileName()).DIRECTORY_SEPARATOR.'views';
+ }
+ }
+
+ /**
+ * Looks for the view script file according to the view name.
+ * This method will look for the view under the widget's {@link getViewPath viewPath}.
+ * The view script file is named as "ViewName.php". A localized view file
+ * may be returned if internationalization is needed. See {@link CApplication::findLocalizedFile}
+ * for more details.
+ * The view name can also refer to a path alias if it contains dot characters.
+ * @param string $viewName name of the view (without file extension)
+ * @return string the view file path. False if the view file does not exist
+ * @see CApplication::findLocalizedFile
+ */
+ public function getViewFile($viewName)
+ {
+ if(($renderer=Yii::app()->getViewRenderer())!==null)
+ $extension=$renderer->fileExtension;
+ else
+ $extension='.php';
+ if(strpos($viewName,'.')) // a path alias
+ $viewFile=Yii::getPathOfAlias($viewName);
+ else
+ {
+ $viewFile=$this->getViewPath(true).DIRECTORY_SEPARATOR.$viewName;
+ if(is_file($viewFile.$extension))
+ return Yii::app()->findLocalizedFile($viewFile.$extension);
+ else if($extension!=='.php' && is_file($viewFile.'.php'))
+ return Yii::app()->findLocalizedFile($viewFile.'.php');
+ $viewFile=$this->getViewPath(false).DIRECTORY_SEPARATOR.$viewName;
+ }
+
+ if(is_file($viewFile.$extension))
+ return Yii::app()->findLocalizedFile($viewFile.$extension);
+ else if($extension!=='.php' && is_file($viewFile.'.php'))
+ return Yii::app()->findLocalizedFile($viewFile.'.php');
+ else
+ return false;
+ }
+
+ /**
+ * Renders a view.
+ *
+ * The named view refers to a PHP script (resolved via {@link getViewFile})
+ * that is included by this method. If $data is an associative array,
+ * it will be extracted as PHP variables and made available to the script.
+ *
+ * @param string $view name of the view to be rendered. See {@link getViewFile} for details
+ * about how the view script is resolved.
+ * @param array $data data to be extracted into PHP variables and made available to the view script
+ * @param boolean $return whether the rendering result should be returned instead of being displayed to end users
+ * @return string the rendering result. Null if the rendering result is not required.
+ * @throws CException if the view does not exist
+ * @see getViewFile
+ */
+ public function render($view,$data=null,$return=false)
+ {
+ if(($viewFile=$this->getViewFile($view))!==false)
+ return $this->renderFile($viewFile,$data,$return);
+ else
+ throw new CException(Yii::t('yii','{widget} cannot find the view "{view}".',
+ array('{widget}'=>get_class($this), '{view}'=>$view)));
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/captcha/CCaptcha.php b/framework/web/widgets/captcha/CCaptcha.php
new file mode 100644
index 0000000..ea8366c
--- /dev/null
+++ b/framework/web/widgets/captcha/CCaptcha.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * CCaptcha class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CCaptcha renders a CAPTCHA image element.
+ *
+ * CCaptcha is used together with {@link CCaptchaAction} to provide {@link http://en.wikipedia.org/wiki/Captcha CAPTCHA}
+ * - a way of preventing site spam.
+ *
+ * The image element rendered by CCaptcha will display a CAPTCHA image generated
+ * by an action of class {@link CCaptchaAction} belonging to the current controller.
+ * By default, the action ID should be 'captcha', which can be changed by setting {@link captchaAction}.
+ *
+ * CCaptcha may also render a button next to the CAPTCHA image. Clicking on the button
+ * will change the CAPTCHA image to be a new one in an AJAX way.
+ *
+ * If {@link clickableImage} is set true, clicking on the CAPTCHA image
+ * will refresh the CAPTCHA.
+ *
+ * A {@link CCaptchaValidator} may be used to validate that the user enters
+ * a verification code matching the code displayed in the CAPTCHA image.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CCaptcha.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets.captcha
+ * @since 1.0
+ */
+class CCaptcha extends CWidget
+{
+ /**
+ * @var string the ID of the action that should provide CAPTCHA image. Defaults to 'captcha',
+ * meaning the 'captcha' action of the current controller. This property may also
+ * be in the format of 'ControllerID/ActionID'. Underneath, this property is used
+ * by {@link CController::createUrl} to create the URL that would serve the CAPTCHA image.
+ * The action has to be of {@link CCaptchaAction}.
+ */
+ public $captchaAction='captcha';
+ /**
+ * @var boolean whether to display a button next to the CAPTCHA image. Clicking on the button
+ * will cause the CAPTCHA image to be changed to a new one. Defaults to true.
+ */
+ public $showRefreshButton=true;
+ /**
+ * @var boolean whether to allow clicking on the CAPTCHA image to refresh the CAPTCHA letters.
+ * Defaults to false. Hint: you may want to set {@link showRefreshButton} to false if you set
+ * this property to be true because they serve for the same purpose.
+ * To enhance accessibility, you may set {@link imageOptions} to provide hints to end-users that
+ * the image is clickable.
+ */
+ public $clickableImage=false;
+ /**
+ * @var string the label for the refresh button. Defaults to 'Get a new code'.
+ */
+ public $buttonLabel;
+ /**
+ * @var string the type of the refresh button. This should be either 'link' or 'button'.
+ * The former refers to hyperlink button while the latter a normal push button.
+ * Defaults to 'link'.
+ */
+ public $buttonType='link';
+ /**
+ * @var array HTML attributes to be applied to the rendered image element.
+ */
+ public $imageOptions=array();
+ /**
+ * @var array HTML attributes to be applied to the rendered refresh button element.
+ */
+ public $buttonOptions=array();
+
+
+ /**
+ * Renders the widget.
+ */
+ public function run()
+ {
+ if(self::checkRequirements())
+ {
+ $this->renderImage();
+ $this->registerClientScript();
+ }
+ else
+ throw new CException(Yii::t('yii','GD and FreeType PHP extensions are required.'));
+ }
+
+ /**
+ * Renders the CAPTCHA image.
+ */
+ protected function renderImage()
+ {
+ if(!isset($this->imageOptions['id']))
+ $this->imageOptions['id']=$this->getId();
+
+ $url=$this->getController()->createUrl($this->captchaAction,array('v'=>uniqid()));
+ $alt=isset($this->imageOptions['alt'])?$this->imageOptions['alt']:'';
+ echo CHtml::image($url,$alt,$this->imageOptions);
+ }
+
+ /**
+ * Registers the needed client scripts.
+ */
+ public function registerClientScript()
+ {
+ $cs=Yii::app()->clientScript;
+ $id=$this->imageOptions['id'];
+ $url=$this->getController()->createUrl($this->captchaAction,array(CCaptchaAction::REFRESH_GET_VAR=>true));
+
+ $js="";
+ if($this->showRefreshButton)
+ {
+ $cs->registerScript('Yii.CCaptcha#'.$id,'dummy');
+ $label=$this->buttonLabel===null?Yii::t('yii','Get a new code'):$this->buttonLabel;
+ $options=$this->buttonOptions;
+ if(isset($options['id']))
+ $buttonID=$options['id'];
+ else
+ $buttonID=$options['id']=$id.'_button';
+ if($this->buttonType==='button')
+ $html=CHtml::button($label, $options);
+ else
+ $html=CHtml::link($label, $url, $options);
+ $js="jQuery('#$id').after(".CJSON::encode($html).");";
+ $selector="#$buttonID";
+ }
+
+ if($this->clickableImage)
+ $selector=isset($selector) ? "$selector, #$id" : "#$id";
+
+ if(!isset($selector))
+ return;
+
+ $js.="
+jQuery('$selector').live('click',function(){
+ jQuery.ajax({
+ url: ".CJSON::encode($url).",
+ dataType: 'json',
+ cache: false,
+ success: function(data) {
+ jQuery('#$id').attr('src', data['url']);
+ jQuery('body').data('{$this->captchaAction}.hash', [data['hash1'], data['hash2']]);
+ }
+ });
+ return false;
+});
+";
+ $cs->registerScript('Yii.CCaptcha#'.$id,$js);
+ }
+
+ /**
+ * Checks if GD with FreeType support is loaded.
+ * @return boolean true if GD with FreeType support is loaded, otherwise false
+ * @since 1.1.5
+ */
+ public static function checkRequirements()
+ {
+ if (extension_loaded('gd'))
+ {
+ $gdinfo=gd_info();
+ if( $gdinfo['FreeType Support'])
+ return true;
+ }
+ return false;
+ }
+}
+
diff --git a/framework/web/widgets/captcha/CCaptchaAction.php b/framework/web/widgets/captcha/CCaptchaAction.php
new file mode 100644
index 0000000..05ec194
--- /dev/null
+++ b/framework/web/widgets/captcha/CCaptchaAction.php
@@ -0,0 +1,272 @@
+<?php
+
+/**
+ * CCaptchaAction class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CCaptchaAction renders a CAPTCHA image.
+ *
+ * CCaptchaAction is used together with {@link CCaptcha} and {@link CCaptchaValidator}
+ * to provide the {@link http://en.wikipedia.org/wiki/Captcha CAPTCHA} feature.
+ *
+ * You must configure properties of CCaptchaAction to customize the appearance of
+ * the generated image.
+ *
+ * Note, CCaptchaAction requires PHP GD2 extension.
+ *
+ * Using CAPTCHA involves the following steps:
+ * <ol>
+ * <li>Override {@link CController::actions()} and register an action of class CCaptchaAction with ID 'captcha'.</li>
+ * <li>In the form model, declare an attribute to store user-entered verification code, and declare the attribute
+ * to be validated by the 'captcha' validator.</li>
+ * <li>In the controller view, insert a {@link CCaptcha} widget in the form.</li>
+ * </ol>
+ *
+ * @property string $verifyCode The verification code.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CCaptchaAction.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets.captcha
+ * @since 1.0
+ */
+class CCaptchaAction extends CAction
+{
+ /**
+ * The name of the GET parameter indicating whether the CAPTCHA image should be regenerated.
+ */
+ const REFRESH_GET_VAR='refresh';
+ /**
+ * Prefix to the session variable name used by the action.
+ */
+ const SESSION_VAR_PREFIX='Yii.CCaptchaAction.';
+ /**
+ * @var integer how many times should the same CAPTCHA be displayed. Defaults to 3.
+ * A value less than or equal to 0 means the test is unlimited (available since version 1.1.2).
+ */
+ public $testLimit = 3;
+ /**
+ * @var integer the width of the generated CAPTCHA image. Defaults to 120.
+ */
+ public $width = 120;
+ /**
+ * @var integer the height of the generated CAPTCHA image. Defaults to 50.
+ */
+ public $height = 50;
+ /**
+ * @var integer padding around the text. Defaults to 2.
+ */
+ public $padding = 2;
+ /**
+ * @var integer the background color. For example, 0x55FF00.
+ * Defaults to 0xFFFFFF, meaning white color.
+ */
+ public $backColor = 0xFFFFFF;
+ /**
+ * @var integer the font color. For example, 0x55FF00. Defaults to 0x2040A0 (blue color).
+ */
+ public $foreColor = 0x2040A0;
+ /**
+ * @var boolean whether to use transparent background. Defaults to false.
+ */
+ public $transparent = false;
+ /**
+ * @var integer the minimum length for randomly generated word. Defaults to 6.
+ */
+ public $minLength = 6;
+ /**
+ * @var integer the maximum length for randomly generated word. Defaults to 7.
+ */
+ public $maxLength = 7;
+ /**
+ * @var integer the offset between characters. Defaults to -2. You can adjust this property
+ * in order to decrease or increase the readability of the captcha.
+ * @since 1.1.7
+ **/
+ public $offset = -2;
+ /**
+ * @var string the TrueType font file. Defaults to Duality.ttf which is provided
+ * with the Yii release.
+ */
+ public $fontFile;
+ /**
+ * @var string the fixed verification code. When this is property is set,
+ * {@link getVerifyCode} will always return this value.
+ * This is mainly used in automated tests where we want to be able to reproduce
+ * the same verification code each time we run the tests.
+ * Defaults to null, meaning the verification code will be randomly generated.
+ * @since 1.1.4
+ */
+ public $fixedVerifyCode;
+
+ /**
+ * Runs the action.
+ */
+ public function run()
+ {
+ if(isset($_GET[self::REFRESH_GET_VAR])) // AJAX request for regenerating code
+ {
+ $code=$this->getVerifyCode(true);
+ echo CJSON::encode(array(
+ 'hash1'=>$this->generateValidationHash($code),
+ 'hash2'=>$this->generateValidationHash(strtolower($code)),
+ // we add a random 'v' parameter so that FireFox can refresh the image
+ // when src attribute of image tag is changed
+ 'url'=>$this->getController()->createUrl($this->getId(),array('v' => uniqid())),
+ ));
+ }
+ else
+ $this->renderImage($this->getVerifyCode());
+ Yii::app()->end();
+ }
+
+ /**
+ * Generates a hash code that can be used for client side validation.
+ * @param string $code the CAPTCHA code
+ * @return string a hash code generated from the CAPTCHA code
+ * @since 1.1.7
+ */
+ public function generateValidationHash($code)
+ {
+ for($h=0,$i=strlen($code)-1;$i>=0;--$i)
+ $h+=ord($code[$i]);
+ return $h;
+ }
+
+ /**
+ * Gets the verification code.
+ * @param boolean $regenerate whether the verification code should be regenerated.
+ * @return string the verification code.
+ */
+ public function getVerifyCode($regenerate=false)
+ {
+ if($this->fixedVerifyCode !== null)
+ return $this->fixedVerifyCode;
+
+ $session = Yii::app()->session;
+ $session->open();
+ $name = $this->getSessionKey();
+ if($session[$name] === null || $regenerate)
+ {
+ $session[$name] = $this->generateVerifyCode();
+ $session[$name . 'count'] = 1;
+ }
+ return $session[$name];
+ }
+
+ /**
+ * Validates the input to see if it matches the generated code.
+ * @param string $input user input
+ * @param boolean $caseSensitive whether the comparison should be case-sensitive
+ * @return boolean whether the input is valid
+ */
+ public function validate($input,$caseSensitive)
+ {
+ $code = $this->getVerifyCode();
+ $valid = $caseSensitive ? ($input === $code) : !strcasecmp($input,$code);
+ $session = Yii::app()->session;
+ $session->open();
+ $name = $this->getSessionKey() . 'count';
+ $session[$name] = $session[$name] + 1;
+ if($session[$name] > $this->testLimit && $this->testLimit > 0)
+ $this->getVerifyCode(true);
+ return $valid;
+ }
+
+ /**
+ * Generates a new verification code.
+ * @return string the generated verification code
+ */
+ protected function generateVerifyCode()
+ {
+ if($this->minLength < 3)
+ $this->minLength = 3;
+ if($this->maxLength > 20)
+ $this->maxLength = 20;
+ if($this->minLength > $this->maxLength)
+ $this->maxLength = $this->minLength;
+ $length = mt_rand($this->minLength,$this->maxLength);
+
+ $letters = 'bcdfghjklmnpqrstvwxyz';
+ $vowels = 'aeiou';
+ $code = '';
+ for($i = 0; $i < $length; ++$i)
+ {
+ if($i % 2 && mt_rand(0,10) > 2 || !($i % 2) && mt_rand(0,10) > 9)
+ $code.=$vowels[mt_rand(0,4)];
+ else
+ $code.=$letters[mt_rand(0,20)];
+ }
+
+ return $code;
+ }
+
+ /**
+ * Returns the session variable name used to store verification code.
+ * @return string the session variable name
+ */
+ protected function getSessionKey()
+ {
+ return self::SESSION_VAR_PREFIX . Yii::app()->getId() . '.' . $this->getController()->getUniqueId() . '.' . $this->getId();
+ }
+
+ /**
+ * Renders the CAPTCHA image based on the code.
+ * @param string $code the verification code
+ * @return string image content
+ */
+ protected function renderImage($code)
+ {
+ $image = imagecreatetruecolor($this->width,$this->height);
+
+ $backColor = imagecolorallocate($image,
+ (int)($this->backColor % 0x1000000 / 0x10000),
+ (int)($this->backColor % 0x10000 / 0x100),
+ $this->backColor % 0x100);
+ imagefilledrectangle($image,0,0,$this->width,$this->height,$backColor);
+ imagecolordeallocate($image,$backColor);
+
+ if($this->transparent)
+ imagecolortransparent($image,$backColor);
+
+ $foreColor = imagecolorallocate($image,
+ (int)($this->foreColor % 0x1000000 / 0x10000),
+ (int)($this->foreColor % 0x10000 / 0x100),
+ $this->foreColor % 0x100);
+
+ if($this->fontFile === null)
+ $this->fontFile = dirname(__FILE__) . '/Duality.ttf';
+
+ $length = strlen($code);
+ $box = imagettfbbox(30,0,$this->fontFile,$code);
+ $w = $box[4] - $box[0] + $this->offset * ($length - 1);
+ $h = $box[1] - $box[5];
+ $scale = min(($this->width - $this->padding * 2) / $w,($this->height - $this->padding * 2) / $h);
+ $x = 10;
+ $y = round($this->height * 27 / 40);
+ for($i = 0; $i < $length; ++$i)
+ {
+ $fontSize = (int)(rand(26,32) * $scale * 0.8);
+ $angle = rand(-10,10);
+ $letter = $code[$i];
+ $box = imagettftext($image,$fontSize,$angle,$x,$y,$foreColor,$this->fontFile,$letter);
+ $x = $box[2] + $this->offset;
+ }
+
+ imagecolordeallocate($image,$foreColor);
+
+ header('Pragma: public');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Content-Transfer-Encoding: binary');
+ header("Content-type: image/png");
+ imagepng($image);
+ imagedestroy($image);
+ }
+
+} \ No newline at end of file
diff --git a/framework/web/widgets/captcha/Duality.ttf b/framework/web/widgets/captcha/Duality.ttf
new file mode 100644
index 0000000..581d5ce
--- /dev/null
+++ b/framework/web/widgets/captcha/Duality.ttf
Binary files differ
diff --git a/framework/web/widgets/pagers/CBasePager.php b/framework/web/widgets/pagers/CBasePager.php
new file mode 100644
index 0000000..62ee4a2
--- /dev/null
+++ b/framework/web/widgets/pagers/CBasePager.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * CBasePager class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CBasePager is the base class for all pagers.
+ *
+ * It provides the calculation of page count and maintains the current page.
+ *
+ * @property CPagination $pages The pagination information.
+ * @property integer $pageSize Number of items in each page.
+ * @property integer $itemCount Total number of items.
+ * @property integer $pageCount Number of pages.
+ * @property integer $currentPage The zero-based index of the current page. Defaults to 0.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CBasePager.php 3426 2011-10-25 00:01:09Z alexander.makarow $
+ * @package system.web.widgets.pagers
+ * @since 1.0
+ */
+abstract class CBasePager extends CWidget
+{
+ private $_pages;
+
+ /**
+ * Returns the pagination information used by this pager.
+ * @return CPagination the pagination information
+ */
+ public function getPages()
+ {
+ if($this->_pages===null)
+ $this->_pages=$this->createPages();
+ return $this->_pages;
+ }
+
+ /**
+ * Sets the pagination information used by this pager.
+ * @param CPagination $pages the pagination information
+ */
+ public function setPages($pages)
+ {
+ $this->_pages=$pages;
+ }
+
+ /**
+ * Creates the default pagination.
+ * This is called by {@link getPages} when the pagination is not set before.
+ * @return CPagination the default pagination instance.
+ */
+ protected function createPages()
+ {
+ return new CPagination;
+ }
+
+ /**
+ * @return integer number of items in each page.
+ * @see CPagination::getPageSize
+ */
+ public function getPageSize()
+ {
+ return $this->getPages()->getPageSize();
+ }
+
+ /**
+ * @param integer $value number of items in each page
+ * @see CPagination::setPageSize
+ */
+ public function setPageSize($value)
+ {
+ $this->getPages()->setPageSize($value);
+ }
+
+ /**
+ * @return integer total number of items.
+ * @see CPagination::getItemCount
+ */
+ public function getItemCount()
+ {
+ return $this->getPages()->getItemCount();
+ }
+
+ /**
+ * @param integer $value total number of items.
+ * @see CPagination::setItemCount
+ */
+ public function setItemCount($value)
+ {
+ $this->getPages()->setItemCount($value);
+ }
+
+ /**
+ * @return integer number of pages
+ * @see CPagination::getPageCount
+ */
+ public function getPageCount()
+ {
+ return $this->getPages()->getPageCount();
+ }
+
+ /**
+ * @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.
+ * @see CPagination::getCurrentPage
+ */
+ public function getCurrentPage($recalculate=true)
+ {
+ return $this->getPages()->getCurrentPage($recalculate);
+ }
+
+ /**
+ * @param integer $value the zero-based index of the current page.
+ * @see CPagination::setCurrentPage
+ */
+ public function setCurrentPage($value)
+ {
+ $this->getPages()->setCurrentPage($value);
+ }
+
+ /**
+ * Creates the URL suitable for pagination.
+ * @param integer $page the page that the URL should point to.
+ * @return string the created URL
+ * @see CPagination::createPageUrl
+ */
+ protected function createPageUrl($page)
+ {
+ return $this->getPages()->createPageUrl($this->getController(),$page);
+ }
+}
diff --git a/framework/web/widgets/pagers/CLinkPager.php b/framework/web/widgets/pagers/CLinkPager.php
new file mode 100644
index 0000000..921dcda
--- /dev/null
+++ b/framework/web/widgets/pagers/CLinkPager.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * CLinkPager class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CLinkPager displays a list of hyperlinks that lead to different pages of target.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CLinkPager.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.widgets.pagers
+ * @since 1.0
+ */
+class CLinkPager extends CBasePager
+{
+ const CSS_FIRST_PAGE='first';
+ const CSS_LAST_PAGE='last';
+ const CSS_PREVIOUS_PAGE='previous';
+ const CSS_NEXT_PAGE='next';
+ const CSS_INTERNAL_PAGE='page';
+ const CSS_HIDDEN_PAGE='hidden';
+ const CSS_SELECTED_PAGE='selected';
+
+ /**
+ * @var integer maximum number of page buttons that can be displayed. Defaults to 10.
+ */
+ public $maxButtonCount=10;
+ /**
+ * @var string the text label for the next page button. Defaults to 'Next &gt;'.
+ */
+ public $nextPageLabel;
+ /**
+ * @var string the text label for the previous page button. Defaults to '&lt; Previous'.
+ */
+ public $prevPageLabel;
+ /**
+ * @var string the text label for the first page button. Defaults to '&lt;&lt; First'.
+ */
+ public $firstPageLabel;
+ /**
+ * @var string the text label for the last page button. Defaults to 'Last &gt;&gt;'.
+ */
+ public $lastPageLabel;
+ /**
+ * @var string the text shown before page buttons. Defaults to 'Go to page: '.
+ */
+ public $header;
+ /**
+ * @var string the text shown after page buttons.
+ */
+ public $footer='';
+ /**
+ * @var mixed the CSS file used for the widget. Defaults to null, meaning
+ * using the default CSS file included together with the widget.
+ * If false, no CSS file will be used. Otherwise, the specified CSS file
+ * will be included when using this widget.
+ */
+ public $cssFile;
+ /**
+ * @var array HTML attributes for the pager container tag.
+ */
+ public $htmlOptions=array();
+
+ /**
+ * Initializes the pager by setting some default property values.
+ */
+ public function init()
+ {
+ if($this->nextPageLabel===null)
+ $this->nextPageLabel=Yii::t('yii','Next &gt;');
+ if($this->prevPageLabel===null)
+ $this->prevPageLabel=Yii::t('yii','&lt; Previous');
+ if($this->firstPageLabel===null)
+ $this->firstPageLabel=Yii::t('yii','&lt;&lt; First');
+ if($this->lastPageLabel===null)
+ $this->lastPageLabel=Yii::t('yii','Last &gt;&gt;');
+ if($this->header===null)
+ $this->header=Yii::t('yii','Go to page: ');
+
+ if(!isset($this->htmlOptions['id']))
+ $this->htmlOptions['id']=$this->getId();
+ if(!isset($this->htmlOptions['class']))
+ $this->htmlOptions['class']='yiiPager';
+ }
+
+ /**
+ * Executes the widget.
+ * This overrides the parent implementation by displaying the generated page buttons.
+ */
+ public function run()
+ {
+ $this->registerClientScript();
+ $buttons=$this->createPageButtons();
+ if(empty($buttons))
+ return;
+ echo $this->header;
+ echo CHtml::tag('ul',$this->htmlOptions,implode("\n",$buttons));
+ echo $this->footer;
+ }
+
+ /**
+ * Creates the page buttons.
+ * @return array a list of page buttons (in HTML code).
+ */
+ protected function createPageButtons()
+ {
+ if(($pageCount=$this->getPageCount())<=1)
+ return array();
+
+ list($beginPage,$endPage)=$this->getPageRange();
+ $currentPage=$this->getCurrentPage(false); // currentPage is calculated in getPageRange()
+ $buttons=array();
+
+ // first page
+ $buttons[]=$this->createPageButton($this->firstPageLabel,0,self::CSS_FIRST_PAGE,$currentPage<=0,false);
+
+ // prev page
+ if(($page=$currentPage-1)<0)
+ $page=0;
+ $buttons[]=$this->createPageButton($this->prevPageLabel,$page,self::CSS_PREVIOUS_PAGE,$currentPage<=0,false);
+
+ // internal pages
+ for($i=$beginPage;$i<=$endPage;++$i)
+ $buttons[]=$this->createPageButton($i+1,$i,self::CSS_INTERNAL_PAGE,false,$i==$currentPage);
+
+ // next page
+ if(($page=$currentPage+1)>=$pageCount-1)
+ $page=$pageCount-1;
+ $buttons[]=$this->createPageButton($this->nextPageLabel,$page,self::CSS_NEXT_PAGE,$currentPage>=$pageCount-1,false);
+
+ // last page
+ $buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,self::CSS_LAST_PAGE,$currentPage>=$pageCount-1,false);
+
+ return $buttons;
+ }
+
+ /**
+ * Creates a page button.
+ * You may override this method to customize the page buttons.
+ * @param string $label the text label for the button
+ * @param integer $page the page number
+ * @param string $class the CSS class for the page button. This could be 'page', 'first', 'last', 'next' or 'previous'.
+ * @param boolean $hidden whether this page button is visible
+ * @param boolean $selected whether this page button is selected
+ * @return string the generated button
+ */
+ protected function createPageButton($label,$page,$class,$hidden,$selected)
+ {
+ if($hidden || $selected)
+ $class.=' '.($hidden ? self::CSS_HIDDEN_PAGE : self::CSS_SELECTED_PAGE);
+ return '<li class="'.$class.'">'.CHtml::link($label,$this->createPageUrl($page)).'</li>';
+ }
+
+ /**
+ * @return array the begin and end pages that need to be displayed.
+ */
+ protected function getPageRange()
+ {
+ $currentPage=$this->getCurrentPage();
+ $pageCount=$this->getPageCount();
+
+ $beginPage=max(0, $currentPage-(int)($this->maxButtonCount/2));
+ if(($endPage=$beginPage+$this->maxButtonCount-1)>=$pageCount)
+ {
+ $endPage=$pageCount-1;
+ $beginPage=max(0,$endPage-$this->maxButtonCount+1);
+ }
+ return array($beginPage,$endPage);
+ }
+
+ /**
+ * Registers the needed client scripts (mainly CSS file).
+ */
+ public function registerClientScript()
+ {
+ if($this->cssFile!==false)
+ self::registerCssFile($this->cssFile);
+ }
+
+ /**
+ * Registers the needed CSS file.
+ * @param string $url the CSS URL. If null, a default CSS URL will be used.
+ */
+ public static function registerCssFile($url=null)
+ {
+ if($url===null)
+ $url=CHtml::asset(Yii::getPathOfAlias('system.web.widgets.pagers.pager').'.css');
+ Yii::app()->getClientScript()->registerCssFile($url);
+ }
+}
diff --git a/framework/web/widgets/pagers/CListPager.php b/framework/web/widgets/pagers/CListPager.php
new file mode 100644
index 0000000..0ab1f3e
--- /dev/null
+++ b/framework/web/widgets/pagers/CListPager.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * CListPager class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+
+/**
+ * CListPager displays a dropdown list that contains options leading to different pages of target.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CListPager.php 2799 2011-01-01 19:31:13Z qiang.xue $
+ * @package system.web.widgets.pagers
+ * @since 1.0
+ */
+class CListPager extends CBasePager
+{
+ /**
+ * @var string the text shown before page buttons. Defaults to 'Go to page: '.
+ */
+ public $header;
+ /**
+ * @var string the text shown after page buttons.
+ */
+ public $footer;
+ /**
+ * @var string the text displayed as a prompt option in the dropdown list. Defaults to null, meaning no prompt.
+ */
+ public $promptText;
+ /**
+ * @var string the format string used to generate page selection text.
+ * The sprintf function will be used to perform the formatting.
+ */
+ public $pageTextFormat;
+ /**
+ * @var array HTML attributes for the enclosing 'div' tag.
+ */
+ public $htmlOptions=array();
+
+ /**
+ * Initializes the pager by setting some default property values.
+ */
+ public function init()
+ {
+ if($this->header===null)
+ $this->header=Yii::t('yii','Go to page: ');
+ if(!isset($this->htmlOptions['id']))
+ $this->htmlOptions['id']=$this->getId();
+ if($this->promptText!==null)
+ $this->htmlOptions['prompt']=$this->promptText;
+ if(!isset($this->htmlOptions['onchange']))
+ $this->htmlOptions['onchange']="if(this.value!='') {window.location=this.value;};";
+ }
+
+ /**
+ * Executes the widget.
+ * This overrides the parent implementation by displaying the generated page buttons.
+ */
+ public function run()
+ {
+ if(($pageCount=$this->getPageCount())<=1)
+ return;
+ $pages=array();
+ for($i=0;$i<$pageCount;++$i)
+ $pages[$this->createPageUrl($i)]=$this->generatePageText($i);
+ $selection=$this->createPageUrl($this->getCurrentPage());
+ echo $this->header;
+ echo CHtml::dropDownList($this->getId(),$selection,$pages,$this->htmlOptions);
+ echo $this->footer;
+ }
+
+ /**
+ * Generates the list option for the specified page number.
+ * You may override this method to customize the option display.
+ * @param integer $page zero-based page number
+ * @return string the list option for the page number
+ */
+ protected function generatePageText($page)
+ {
+ if($this->pageTextFormat!==null)
+ return sprintf($this->pageTextFormat,$page+1);
+ else
+ return $page+1;
+ }
+} \ No newline at end of file
diff --git a/framework/web/widgets/pagers/pager.css b/framework/web/widgets/pagers/pager.css
new file mode 100644
index 0000000..1c802cc
--- /dev/null
+++ b/framework/web/widgets/pagers/pager.css
@@ -0,0 +1,67 @@
+/**
+ * CSS styles for CLinkPager.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2010 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ * @version $Id: pager.css 1678 2010-01-07 21:02:00Z qiang.xue $
+ * @since 1.0
+ */
+
+ul.yiiPager
+{
+ font-size:11px;
+ border:0;
+ margin:0;
+ padding:0;
+ line-height:100%;
+ display:inline;
+}
+
+ul.yiiPager li
+{
+ display:inline;
+}
+
+ul.yiiPager a:link,
+ul.yiiPager a:visited
+{
+ border:solid 1px #9aafe5;
+ font-weight:bold;
+ color:#0e509e;
+ padding:1px 6px;
+ text-decoration:none;
+}
+
+ul.yiiPager .page a
+{
+ font-weight:normal;
+}
+
+ul.yiiPager a:hover
+{
+ border:solid 1px #0e509e;
+}
+
+ul.yiiPager .selected a
+{
+ background:#2e6ab1;
+ color:#FFFFFF;
+ font-weight:bold;
+}
+
+ul.yiiPager .hidden a
+{
+ border:solid 1px #DEDEDE;
+ color:#888888;
+}
+
+/**
+ * Hide first and last buttons by default.
+ */
+ul.yiiPager .first,
+ul.yiiPager .last
+{
+ display:none;
+} \ No newline at end of file
diff --git a/framework/web/widgets/views/flexWidget.php b/framework/web/widgets/views/flexWidget.php
new file mode 100644
index 0000000..9ee3d43
--- /dev/null
+++ b/framework/web/widgets/views/flexWidget.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * The view file for CFlexWidget.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ * @version $Id: flexWidget.php 2799 2011-01-01 19:31:13Z qiang.xue $
+ * @package system.web.widgets.views
+ * @since 1.0
+ */
+?>
+<script type="text/javascript">
+/*<![CDATA[*/
+// Version check for the Flash Player that has the ability to start Player Product Install (6.0r65)
+var hasProductInstall = DetectFlashVer(6, 0, 65);
+
+// Version check based upon the values defined in globals
+var hasRequestedVersion = DetectFlashVer(9, 0, 0);
+
+// Check to see if a player with Flash Product Install is available and the version does not meet the requirements for playback
+if ( hasProductInstall && !hasRequestedVersion ) {
+ // MMdoctitle is the stored document.title value used by the installation process to close the window that started the process
+ // This is necessary in order to close browser windows that are still utilizing the older version of the player after installation has completed
+ // DO NOT MODIFY THE FOLLOWING FOUR LINES
+ // Location visited after installation is complete if installation is required
+ var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
+ var MMredirectURL = window.location;
+ document.title = document.title.slice(0, 47) + " - Flash Player Installation";
+ var MMdoctitle = document.title;
+
+ AC_FL_RunContent(
+ "src", "<?php echo $this->baseUrl ?>/playerProductInstall",
+ "FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
+ "width", "<?php echo $this->width; ?>",
+ "height", "<?php echo $this->height; ?>",
+ "align", "<?php echo $this->align; ?>",
+ "id", "<?php echo $this->name; ?>",
+ "quality", "<?php echo $this->quality; ?>",
+ "bgcolor", "<?php echo $this->bgColor; ?>",
+ "name", "<?php echo $this->name; ?>",
+ "allowScriptAccess","<?php echo $this->allowScriptAccess ?>",
+ "allowFullScreen","<?php echo $this->allowFullScreen ?>",
+ "type", "application/x-shockwave-flash",
+ "pluginspage", "http://www.adobe.com/go/getflashplayer"
+ );
+} else if (hasRequestedVersion) {
+ // if we've detected an acceptable version
+ // embed the Flash Content SWF when all tests are passed
+ AC_FL_RunContent(
+ "src", "<?php echo $this->baseUrl ?>/<?php echo $this->name ?>",
+ "width", "<?php echo $this->width ?>",
+ "height", "<?php echo $this->height ?>",
+ "align", "<?php echo $this->align ?>",
+ "id", "<?php echo $this->name ?>",
+ "quality", "<?php echo $this->quality ?>",
+ "bgcolor", "<?php echo $this->bgColor ?>",
+ "name", "<?php echo $this->name ?>",
+ "flashvars","<?php echo $this->flashVarsAsString; ?>",
+ "allowScriptAccess","<?php echo $this->allowScriptAccess ?>",
+ "allowFullScreen","<?php echo $this->allowFullScreen ?>",
+ "type", "application/x-shockwave-flash",
+ "pluginspage", "http://www.adobe.com/go/getflashplayer"
+ );
+} else { // flash is too old or we can't detect the plugin
+ var alternateContent = '<?php echo CJavaScript::quote($this->altHtmlContent); ?>';
+ document.write(alternateContent); // insert non-flash content
+}
+/*]]>*/
+</script>
+<noscript>
+ <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+ id="<?php echo $this->name ?>"
+ width="<?php echo $this->width ?>"
+ height="<?php echo $this->height ?>"
+ codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
+ <param name="movie" value="<?php echo $this->baseUrl ?>/<?php echo $this->name ?>.swf" />
+ <param name="quality" value="<?php echo $this->quality ?>" />
+ <param name="bgcolor" value="<?php echo $this->bgColor ?>" />
+ <param name="flashVars" value="<?php echo $this->flashVarsAsString ?>" />
+ <param name="allowScriptAccess" value="<?php echo $this->allowScriptAccess ?>" />
+ <param name="allowFullScreen" value="<?php echo $this->allowFullScreen ?>" />
+ <embed src="<?php echo $this->baseUrl ?>/<?php echo $this->name ?>.swf"
+ quality="<?php echo $this->quality ?>"
+ bgcolor="<?php echo $this->bgColor ?>"
+ width="<?php echo $this->width ?>"
+ height="<?php echo $this->height ?>"
+ name="<?php echo $this->name ?>"
+ align="<?php echo $this->align ?>"
+ play="true"
+ loop="false"
+ quality="<?php echo $this->quality ?>"
+ allowScriptAccess="<?php echo $this->allowScriptAccess ?>"
+ allowFullScreen="<?php echo $this->allowFullScreen ?>"
+ type="application/x-shockwave-flash"
+ pluginspage="http://www.adobe.com/go/getflashplayer">
+ </embed>
+ </object>
+</noscript> \ No newline at end of file