1
0

Added new (clean) yii boilerplate

This commit is contained in:
2014-05-13 12:40:42 +02:00
parent 1d6d975a16
commit 99d29b432b
1983 changed files with 653465 additions and 17 deletions

View File

@@ -0,0 +1,955 @@
<?php
/**
* CActiveForm class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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.
* Since 1.1.14 this defaults to 'errorMessage' defined in {@link CHtml::$errorMessageCss}.
* Individual {@link error} call may override this value by specifying the 'class' HTML option.
*/
public $errorMessageCssClass;
/**
* @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 wrap the value with {@link CJavaScriptExpression} 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 wrap the value with {@link CJavaScriptExpression} 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 wrap the value with {@link CJavaScriptExpression} 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 wrap the value with {@link CJavaScriptExpression} 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;
/**
* @var string[] attribute IDs to be used to display error summary.
* @since 1.1.14
*/
private $_summaryAttributes=array();
/**
* Initializes the widget.
* This renders the form open tag.
*/
public function init()
{
if(!isset($this->htmlOptions['id']))
$this->htmlOptions['id']=$this->id;
else
$this->id=$this->htmlOptions['id'];
if($this->stateful)
echo CHtml::statefulForm($this->action, $this->method, $this->htmlOptions);
else
echo CHtml::beginForm($this->action, $this->method, $this->htmlOptions);
if($this->errorMessageCssClass===null)
$this->errorMessageCssClass=CHtml::$errorMessageCss;
}
/**
* 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)
jQuery('".$this->focus."').focus();
");
}
return;
}
$options=$this->clientOptions;
if(isset($this->clientOptions['validationUrl']) && is_array($this->clientOptions['validationUrl']))
$options['validationUrl']=CHtml::normalizeUrl($this->clientOptions['validationUrl']);
foreach($this->_summaryAttributes as $attribute)
$this->attributes[$attribute]['summary']=true;
$options['attributes']=array_values($this->attributes);
if($this->summaryID!==null)
$options['summaryID']=$this->summaryID;
if($this->focus!==null)
$options['focus']=$this->focus;
if(!empty(CHtml::$errorCss))
$options['errorCss']=CHtml::$errorCss;
$options=CJavaScript::encode($options);
$cs->registerCoreScript('yiiactiveform');
$id=$this->id;
$cs->registerScript(__CLASS__.'#'.$id,"jQuery('#$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.
* <ul>
* <li>inputID</li>
* </ul>
* When an CActiveForm input field uses a custom ID, for ajax/client validation to work properly
* inputID should be set to the same ID
*
* Example:
* <pre>
* <div class="form-element">
* <?php echo $form->labelEx($model,'attribute'); ?>
* <?php echo $form->textField($model,'attribute', array('id'=>'custom-id')); ?>
* <?php echo $form->error($model,'attribute',array('inputID'=>'custom-id')); ?>
* </div>
* </pre>
*
* 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>
* This should NOT be a function but just the code, Yii will enclose the code you provide inside the
* actual JS function.
* @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();
unset($htmlOptions['clientValidation']);
$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']=new CJavaScriptExpression("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(CHtml::$errorContainerTag,$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'];
foreach(is_array($models) ? $models : array($models) as $model)
foreach($model->getSafeAttributeNames() as $attribute)
$this->_summaryAttributes[]=CHtml::activeId($model,$attribute);
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 url field for a model attribute.
* This method is a wrapper of {@link CHtml::activeUrlField}.
* Please check {@link CHtml::activeUrlField} 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
* @since 1.1.11
*/
public function urlField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeUrlField($model,$attribute,$htmlOptions);
}
/**
* Renders an email field for a model attribute.
* This method is a wrapper of {@link CHtml::activeEmailField}.
* Please check {@link CHtml::activeEmailField} 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
* @since 1.1.11
*/
public function emailField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeEmailField($model,$attribute,$htmlOptions);
}
/**
* Renders a number field for a model attribute.
* This method is a wrapper of {@link CHtml::activeNumberField}.
* Please check {@link CHtml::activeNumberField} 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
* @since 1.1.11
*/
public function numberField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeNumberField($model,$attribute,$htmlOptions);
}
/**
* Generates a range field for a model attribute.
* This method is a wrapper of {@link CHtml::activeRangeField}.
* Please check {@link CHtml::activeRangeField} 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
* @since 1.1.11
*/
public function rangeField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeRangeField($model,$attribute,$htmlOptions);
}
/**
* Renders a date field for a model attribute.
* This method is a wrapper of {@link CHtml::activeDateField}.
* Please check {@link CHtml::activeDateField} 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
* @since 1.1.11
*/
public function dateField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeDateField($model,$attribute,$htmlOptions);
}
/**
* Renders a time field for a model attribute.
* This method is a wrapper of {@link CHtml::activeTimeField}.
* Please check {@link CHtml::activeTimeField} 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
* @since 1.1.14
*/
public function timeField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeTimeField($model,$attribute,$htmlOptions);
}
/**
* Renders a time field for a model attribute.
* This method is a wrapper of {@link CHtml::activeTimeField}.
* Please check {@link CHtml::activeTimeField} 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
* @since 1.1.14
*/
public function telField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeTelField($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 search field for a model attribute.
* This method is a wrapper of {@link CHtml::activeSearchField}.
* Please check {@link CHtml::activeSearchField} 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
* @since 1.1.14
*/
public function searchField($model,$attribute,$htmlOptions=array())
{
return CHtml::activeSearchField($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)
{
$modelName=CHtml::modelName($model);
if($loadInput && isset($_POST[$modelName]))
$model->attributes=$_POST[$modelName];
$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)
{
$modelName=CHtml::modelName($model);
if($loadInput && isset($_POST[$modelName][$i]))
$model->attributes=$_POST[$modelName][$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);
}
}

View File

@@ -0,0 +1,294 @@
<?php
/**
* CAutoComplete class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @package system.web.widgets
* @since 1.0
* @deprecated in 1.1.3
*/
class CAutoComplete extends CInputWidget
{
/**
* @var boolean whether to show the autocomplete using a text area. Defaults to false,
* meaning a text field is used.
*/
public $textArea=false;
/**
* @var array data that would be saved as client-side data to provide candidate selections.
* Each array element can be string or an associative array.
* The {@link url} property will be ignored if this property is set.
* @see url
*/
public $data;
/**
* @var string|array the URL that can return the candidate selections.
* A 'q' GET parameter will be sent with the URL which contains what the user has entered so far.
* If the URL is given as an array, it is considered as a route to a controller action and will
* be used to generate a URL using {@link CController::createUrl};
* If the URL is an empty string, the currently requested URL is used.
* This property will be ignored if {@link data} is set.
* @see data
*/
public $url='';
/**
* @var mixed the CSS file used for the widget. Defaults to null, meaning
* using the default CSS file included together with the widget.
* If false, no CSS file will be used. Otherwise, the specified CSS file
* will be included when using this widget.
*/
public $cssFile;
/**
* @var integer the minimum number of characters a user has to type before
* the autocompleter activates. Defaults to 1.
*/
public $minChars;
/**
* @var integer the delay in milliseconds the autocompleter waits after
* a keystroke to activate itself. Defaults to 400.
*/
public $delay;
/**
* @var integer the number of backend query results to store in cache.
* If set to 1 (the current result), no caching will happen. Must be >= 1. Defaults to 10.
*/
public $cacheLength;
/**
* @var boolean whether or not the autocompleter can use a cache for more
* specific queries. This means that all matches of "foot" are a subset
* of all matches for "foo". Usually this is true, and using this options
* decreases server load and increases performance. Only useful with
* cacheLength settings bigger than one, like 10. Defaults to true.
*/
public $matchSubset;
/**
* @var boolean whether or not the comparison is case sensitive. Important
* only if you use caching. Defaults to false.
*/
public $matchCase;
/**
* @var boolean whether or not the comparison looks inside
* (i.e. does "ba" match "foo bar") the search results. Important only if
* you use caching. Don't mix with autofill. Defaults to false.
*/
public $matchContains;
/**
* @var boolean if set to true, the autocompleter will only allow results that
* are presented by the backend. Note that illegal values result in an empty
* input box. Defaults to false.
*/
public $mustMatch;
/**
* @var boolean if this is set to true, the first autocomplete value will
* be automatically selected on tab/return, even if it has not been handpicked
* by keyboard or mouse action. If there is a handpicked (highlighted) result,
* that result will take precedence. Defaults to true.
*/
public $selectFirst;
/**
* @var array extra parameters for the backend. If you were to specify
* array('bar'=>4), the autocompleter would call the backend with a GET
* parameter 'bar' 4. The param can be a function that is called to calculate
* the param before each request.
*/
public $extraParams;
/**
* @var string a javascript function that provides advanced markup for an item.
* For each row of results, this function will be called. The returned value will
* be displayed inside an LI element in the results list. Autocompleter will
* provide 4 parameters: the results row, the position of the row in the list of
* results (starting at 1), the number of items in the list of results and the search term.
* The default behavior assumes that a single row contains a single value.
*/
public $formatItem;
/**
* @var string a javascript function that can be used to limit the data that autocomplete
* searches for matches. For example, there may be items you want displayed to the user,
* but don't want included in the data that's searched. The function is called with the same arguments
* as {@link formatItem}. Defaults to formatItem.
*/
public $formatMatch;
/**
* @var string a javascript function that provides the formatting for the value to be
* put into the input field. Again three arguments: Data, position (starting with one) and
* total number of data. The default behavior assumes either plain data to use as result
* or uses the same value as provided by formatItem.
*/
public $formatResult;
/**
* @var boolean whether to allow more than one autocompleted-value to enter. Defaults to false.
*/
public $multiple;
/**
* @var string seperator to put between values when using multiple option. Defaults to ", ".
*/
public $multipleSeparator;
/**
* @var integer specify a custom width for the select box. Defaults to the width of the input element.
*/
public $width;
/**
* @var boolean fill the textinput while still selecting a value, replacing the value
* if more is typed or something else is selected. Defaults to false.
*/
public $autoFill;
/**
* @var integer limit the number of items in the select box. Is also sent as
* a "limit" parameter with a remote request. Defaults to 10.
*/
public $max;
/**
* @var boolean|string Whether and how to highlight matches in the select box.
* Set to false to disable. Set to a javascript function to customize.
* The function gets the value as the first argument and the search term as the
* second and must return the formatted value. Defaults to Wraps the search term in a &lt;strong&gt; element.
*/
public $highlight;
/**
* @var boolean whether to scroll when more results than configured via scrollHeight are available. Defaults to true.
*/
public $scroll;
/**
* @var integer height of scrolled autocomplete control in pixels. Defaults to 180.
*/
public $scrollHeight;
/**
* @var string the CSS class for the input element. Defaults to "ac_input".
*/
public $inputClass;
/**
* @var string the CSS class for the dropdown list. Defaults to "ac_results".
*/
public $resultsClass;
/**
* @var string the CSS class used when the data is being loaded from backend. Defaults to "ac_loading".
*/
public $loadingClass;
/**
* @var array additional options that can be passed to the constructor of the autocomplete js object.
* This allows you to override existing functions of the autocomplete js class (e.g. the parse() function)
*
* If you want to provide JavaScript native code, you have to wrap the string with {@link CJavaScriptExpression} 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',
'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($this->$func!==null)
{
if($this->$func instanceof CJavaScriptExpression)
$options[$func]=$this->$func;
else
$options[$func]=new CJavaScriptExpression($this->$func);
}
}
return $options;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* CClipWidget class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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);
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* CContentDecorator class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* CFilterWidget class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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();
}
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* CFlexWidget class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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));
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* CHtmlPurifier class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @package system.web.widgets
* @since 1.0
*/
class CHtmlPurifier extends COutputProcessor
{
/**
* @var object the HTML Purifier instance.
*/
private $_purifier;
/**
* @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
*/
private $_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 mixed $content the content to be purified.
* @return mixed the purified content
*/
public function purify($content)
{
if(is_array($content))
$content=array_map(array($this,'purify'),$content);
else
$content=$this->getPurifier()->purify($content);
return $content;
}
/**
* Set the options for HTML Purifier and create a new HTML Purifier instance based on these options.
* @param mixed $options the options for HTML Purifier
* @return CHtmlPurifier
*/
public function setOptions($options)
{
$this->_options=$options;
$this->createNewHtmlPurifierInstance();
return $this;
}
/**
* Get the options for the HTML Purifier instance.
* @return mixed the HTML Purifier instance options
*/
public function getOptions()
{
return $this->_options;
}
/**
* Get the HTML Purifier instance or create a new one if it doesn't exist.
* @return HTMLPurifier
*/
protected function getPurifier()
{
if($this->_purifier!==null)
return $this->_purifier;
return $this->createNewHtmlPurifierInstance();
}
/**
* Create a new HTML Purifier instance.
* @return HTMLPurifier
*/
protected function createNewHtmlPurifierInstance()
{
$this->_purifier=new HTMLPurifier($this->getOptions());
$this->_purifier->config->set('Cache.SerializerPath',Yii::app()->getRuntimePath());
return $this->_purifier;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* CInputWidget class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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.
* @throws CException in case input name and ID cannot be resolved.
*/
protected function resolveNameID()
{
if($this->name!==null)
$name=$this->name;
elseif(isset($this->htmlOptions['name']))
$name=$this->htmlOptions['name'];
elseif($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;
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* CMarkdown class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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;
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* CMaskedTextField class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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($this->completed!==null)
{
if($this->completed instanceof CJavaScriptExpression)
$options['completed']=$this->completed;
else
$options['completed']=new CJavaScriptExpression($this->completed);
}
return $options;
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* CMultiFileUpload class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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]) && !($options[$event] instanceof CJavaScriptExpression))
$options[$event]=new CJavaScriptExpression($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;
}
}

View File

@@ -0,0 +1,364 @@
<?php
/**
* COutputCache class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* <li>{@link varyByLanguage}: this specifies whether the cached content
* should by varied with the user's language. Available since 1.1.14.</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>
* @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.
*
* The PHP expression will be evaluated using {@link evaluateExpression}.
*
* A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
* please refer to the {@link http://www.php.net/manual/en/language.expressions.php php manual}.
*/
public $varyByExpression;
/**
* @var boolean whether the content being cached should be differentiated according to user's language.
* A language is retrieved via Yii::app()->language.
* Defaults to false.
* @since 1.1.14
*/
public $varyByLanguage=false;
/**
* @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();
elseif($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;
}
elseif($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}, {@link varyBySession} and {@link varyByLanguage}.
* @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.='.';
if($this->varyByLanguage)
$key.=Yii::app()->language;
$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;
elseif($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]);
elseif($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])));
}
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* COutputProcessor class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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);
}
}

View File

@@ -0,0 +1,205 @@
<?php
/**
* CStarRating class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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;
foreach(array('focus', 'blur', 'callback') as $event)
{
if($this->$event!==null)
{
if($this->$event instanceof CJavaScriptExpression)
$options[$event]=$this->$event;
else
$options[$event]=new CJavaScriptExpression($this->$event);
}
}
return $options;
}
}

View File

@@ -0,0 +1,222 @@
<?php
/**
* CTabView class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* $this->widget('CTabView', array(
* 'tabs'=>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>
* @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>
* <li>visible: whether this tab is visible. Defaults to true.
* this option is available since version 1.1.11.</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()
{
foreach($this->tabs as $id=>$tab)
if(isset($tab['visible']) && $tab['visible']==false)
unset($this->tabs[$id]);
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;
if(isset($this->htmlOptions['id']))
$this->id=$this->htmlOptions['id'];
else
$htmlOptions['id']=$this->id;
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'];
elseif(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";
}
}
}

View File

@@ -0,0 +1,124 @@
<?php
/**
* CTextHighlighter class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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);
}
}

View File

@@ -0,0 +1,245 @@
<?php
/**
* CTreeView class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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');
elseif($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);
}
}

View File

@@ -0,0 +1,249 @@
<?php
/**
* CWidget class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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;
elseif($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);
$scope=$checkTheme?'theme':'local';
if(isset(self::$_viewPaths[$className][$scope]))
return self::$_viewPaths[$className][$scope];
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]['theme']=$path;
}
$class=new ReflectionClass($className);
return self::$_viewPaths[$className]['local']=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);
elseif($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);
elseif($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)));
}
}

View File

@@ -0,0 +1,188 @@
<?php
/**
* CCaptcha class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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('imagick') || self::checkRequirements('gd'))
{
$this->renderImage();
$this->registerClientScript();
}
else
throw new CException(Yii::t('yii','GD with FreeType or ImageMagick 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)
{
// reserve a place in the registered script so that any enclosing button js code appears after the captcha js
$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(document).on('click', '$selector', 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 specified graphic extension support is loaded.
* @param string $extension name to be checked. Possible values are 'gd', 'imagick' and null.
* Default value is null meaning that both extensions will be checked. This parameter
* is available since 1.1.13.
* @return boolean true if ImageMagick extension with PNG support or GD with FreeType support is loaded,
* otherwise false
* @since 1.1.5
*/
public static function checkRequirements($extension=null)
{
if(extension_loaded('imagick'))
{
$imagick=new Imagick();
$imagickFormats=$imagick->queryFormats('PNG');
}
if(extension_loaded('gd'))
{
$gdInfo=gd_info();
}
if($extension===null)
{
if(isset($imagickFormats) && in_array('PNG',$imagickFormats))
return true;
if(isset($gdInfo) && $gdInfo['FreeType Support'])
return true;
}
elseif($extension=='imagick' && isset($imagickFormats) && in_array('PNG',$imagickFormats))
return true;
elseif($extension=='gd' && isset($gdInfo) && $gdInfo['FreeType Support'])
return true;
return false;
}
}

View File

@@ -0,0 +1,336 @@
<?php
/**
* CCaptchaAction class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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 SpicyRice.ttf which is provided with the Yii release.
* Note that non-free Duality.ttf has been changed to open/free SpicyRice.ttf since 1.1.14.
*/
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;
/**
* @var string the graphic extension that will be used to draw CAPTCHA image. Possible values
* are 'gd', 'imagick' and null. Null value means that fallback mode will be used: ImageMagick
* is preferred over GD. Default value is null.
* @since 1.1.13
*/
public $backend;
/**
* 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)===0;
$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 > $this->maxLength)
$this->maxLength = $this->minLength;
if($this->minLength < 3)
$this->minLength = 3;
if($this->maxLength > 20)
$this->maxLength = 20;
$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 using library specified in the {@link $backend} property.
* @param string $code the verification code
*/
protected function renderImage($code)
{
if($this->backend===null && CCaptcha::checkRequirements('imagick') || $this->backend==='imagick')
$this->renderImageImagick($code);
else if($this->backend===null && CCaptcha::checkRequirements('gd') || $this->backend==='gd')
$this->renderImageGD($code);
}
/**
* Renders the CAPTCHA image based on the code using GD library.
* @param string $code the verification code
* @since 1.1.13
*/
protected function renderImageGD($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__) . '/SpicyRice.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);
}
/**
* Renders the CAPTCHA image based on the code using ImageMagick library.
* @param string $code the verification code
* @since 1.1.13
*/
protected function renderImageImagick($code)
{
$backColor=$this->transparent ? new ImagickPixel('transparent') : new ImagickPixel(sprintf('#%06x',$this->backColor));
$foreColor=new ImagickPixel(sprintf('#%06x',$this->foreColor));
$image=new Imagick();
$image->newImage($this->width,$this->height,$backColor);
if($this->fontFile===null)
$this->fontFile=dirname(__FILE__).'/SpicyRice.ttf';
$draw=new ImagickDraw();
$draw->setFont($this->fontFile);
$draw->setFontSize(30);
$fontMetrics=$image->queryFontMetrics($draw,$code);
$length=strlen($code);
$w=(int)($fontMetrics['textWidth'])-8+$this->offset*($length-1);
$h=(int)($fontMetrics['textHeight'])-8;
$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)
{
$draw=new ImagickDraw();
$draw->setFont($this->fontFile);
$draw->setFontSize((int)(rand(26,32)*$scale*0.8));
$draw->setFillColor($foreColor);
$image->annotateImage($draw,$x,$y,rand(-10,10),$code[$i]);
$fontMetrics=$image->queryFontMetrics($draw,$code[$i]);
$x+=(int)($fontMetrics['textWidth'])+$this->offset;
}
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");
$image->setImageFormat('png');
echo $image;
}
}

View File

@@ -0,0 +1,11 @@
## Spicy Rice font
* **Author:** Brian J. Bonislawsky, Astigmatic (AOETI, Astigmatic One Eye Typographic Institute)
* **License:** SIL Open Font License (OFL), version 1.1, [notes and FAQ](http://scripts.sil.org/OFL)
## Links
* [Astigmatic](http://www.astigmatic.com/)
* [Google WebFonts](http://www.google.com/webfonts/specimen/Spicy+Rice)
* [fontsquirrel.com](http://www.fontsquirrel.com/fonts/spicy-rice)
* [fontspace.com](http://www.fontspace.com/astigmatic-one-eye-typographic-institute/spicy-rice)

Binary file not shown.

View File

@@ -0,0 +1,134 @@
<?php
/**
* CBasePager class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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);
}
}

View File

@@ -0,0 +1,229 @@
<?php
/**
* CLinkPager class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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 string the CSS class for the first page button. Defaults to 'first'.
* @since 1.1.11
*/
public $firstPageCssClass=self::CSS_FIRST_PAGE;
/**
* @var string the CSS class for the last page button. Defaults to 'last'.
* @since 1.1.11
*/
public $lastPageCssClass=self::CSS_LAST_PAGE;
/**
* @var string the CSS class for the previous page button. Defaults to 'previous'.
* @since 1.1.11
*/
public $previousPageCssClass=self::CSS_PREVIOUS_PAGE;
/**
* @var string the CSS class for the next page button. Defaults to 'next'.
* @since 1.1.11
*/
public $nextPageCssClass=self::CSS_NEXT_PAGE;
/**
* @var string the CSS class for the internal page buttons. Defaults to 'page'.
* @since 1.1.11
*/
public $internalPageCssClass=self::CSS_INTERNAL_PAGE;
/**
* @var string the CSS class for the hidden page buttons. Defaults to 'hidden'.
* @since 1.1.11
*/
public $hiddenPageCssClass=self::CSS_HIDDEN_PAGE;
/**
* @var string the CSS class for the selected page buttons. Defaults to 'selected'.
* @since 1.1.11
*/
public $selectedPageCssClass=self::CSS_SELECTED_PAGE;
/**
* @var integer maximum number of page buttons that can be displayed. Defaults to 10.
*/
public $maxButtonCount=10;
/**
* @var string the text label for the next page button. Defaults to 'Next &gt;'.
*/
public $nextPageLabel;
/**
* @var string the text label for the previous page button. Defaults to '&lt; Previous'.
*/
public $prevPageLabel;
/**
* @var string the text label for the first page button. Defaults to '&lt;&lt; First'.
*/
public $firstPageLabel;
/**
* @var string the text label for the last page button. Defaults to 'Last &gt;&gt;'.
*/
public $lastPageLabel;
/**
* @var string the text shown before page buttons. Defaults to 'Go to page: '.
*/
public $header;
/**
* @var string the text shown after page buttons.
*/
public $footer='';
/**
* @var mixed the CSS file used for the widget. Defaults to null, meaning
* using the default CSS file included together with the widget.
* If false, no CSS file will be used. Otherwise, the specified CSS file
* will be included when using this widget.
*/
public $cssFile;
/**
* @var array HTML attributes for the pager container tag.
*/
public $htmlOptions=array();
/**
* Initializes the pager by setting some default property values.
*/
public function init()
{
if($this->nextPageLabel===null)
$this->nextPageLabel=Yii::t('yii','Next &gt;');
if($this->prevPageLabel===null)
$this->prevPageLabel=Yii::t('yii','&lt; Previous');
if($this->firstPageLabel===null)
$this->firstPageLabel=Yii::t('yii','&lt;&lt; First');
if($this->lastPageLabel===null)
$this->lastPageLabel=Yii::t('yii','Last &gt;&gt;');
if($this->header===null)
$this->header=Yii::t('yii','Go to page: ');
if(!isset($this->htmlOptions['id']))
$this->htmlOptions['id']=$this->getId();
if(!isset($this->htmlOptions['class']))
$this->htmlOptions['class']='yiiPager';
}
/**
* Executes the widget.
* This overrides the parent implementation by displaying the generated page buttons.
*/
public function run()
{
$this->registerClientScript();
$buttons=$this->createPageButtons();
if(empty($buttons))
return;
echo $this->header;
echo CHtml::tag('ul',$this->htmlOptions,implode("\n",$buttons));
echo $this->footer;
}
/**
* Creates the page buttons.
* @return array a list of page buttons (in HTML code).
*/
protected function createPageButtons()
{
if(($pageCount=$this->getPageCount())<=1)
return array();
list($beginPage,$endPage)=$this->getPageRange();
$currentPage=$this->getCurrentPage(false); // currentPage is calculated in getPageRange()
$buttons=array();
// first page
$buttons[]=$this->createPageButton($this->firstPageLabel,0,$this->firstPageCssClass,$currentPage<=0,false);
// prev page
if(($page=$currentPage-1)<0)
$page=0;
$buttons[]=$this->createPageButton($this->prevPageLabel,$page,$this->previousPageCssClass,$currentPage<=0,false);
// internal pages
for($i=$beginPage;$i<=$endPage;++$i)
$buttons[]=$this->createPageButton($i+1,$i,$this->internalPageCssClass,false,$i==$currentPage);
// next page
if(($page=$currentPage+1)>=$pageCount-1)
$page=$pageCount-1;
$buttons[]=$this->createPageButton($this->nextPageLabel,$page,$this->nextPageCssClass,$currentPage>=$pageCount-1,false);
// last page
$buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,$this->lastPageCssClass,$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.
* @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 ? $this->hiddenPageCssClass : $this->selectedPageCssClass);
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);
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* CListPager class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 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>
* @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;
}
}

View File

@@ -0,0 +1,66 @@
/**
* CSS styles for CLinkPager.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2010 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @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;
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* The view file for CFlexWidget.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright 2008-2013 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @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>