diff options
| author | Patrick Seeger <pseeger@ccwn.org> | 2012-04-13 23:11:05 +0200 |
|---|---|---|
| committer | Patrick Seeger <pseeger@ccwn.org> | 2012-04-13 23:11:05 +0200 |
| commit | 341cc4dd9c53ffbfb863e026dd58549c1082c7a7 (patch) | |
| tree | 1bbbed20313bafb9b063b6b4d894fe580d8b000f /framework/web/widgets | |
Diffstat (limited to 'framework/web/widgets')
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 © 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 © 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 <strong> 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 Binary files differnew file mode 100644 index 0000000..581d5ce --- /dev/null +++ b/framework/web/widgets/captcha/Duality.ttf 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 © 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 © 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 >'. + */ + public $nextPageLabel; + /** + * @var string the text label for the previous page button. Defaults to '< Previous'. + */ + public $prevPageLabel; + /** + * @var string the text label for the first page button. Defaults to '<< First'. + */ + public $firstPageLabel; + /** + * @var string the text label for the last page button. Defaults to 'Last >>'. + */ + 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 >'); + if($this->prevPageLabel===null) + $this->prevPageLabel=Yii::t('yii','< Previous'); + if($this->firstPageLabel===null) + $this->firstPageLabel=Yii::t('yii','<< First'); + if($this->lastPageLabel===null) + $this->lastPageLabel=Yii::t('yii','Last >>'); + 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 © 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 © 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 © 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 |
