diff --git a/app/protected/config/main.php b/app/protected/config/main.php index 7722331..b982506 100644 --- a/app/protected/config/main.php +++ b/app/protected/config/main.php @@ -15,4 +15,7 @@ return array( 'bundles' => require(__DIR__ . '/assets.php'), ), ), + 'params' => array( + 'adminEmail' => 'admin@example.com', + ), ); \ No newline at end of file diff --git a/app/protected/controllers/SiteController.php b/app/protected/controllers/SiteController.php index fb5e0ce..33a87c9 100644 --- a/app/protected/controllers/SiteController.php +++ b/app/protected/controllers/SiteController.php @@ -2,6 +2,7 @@ use yii\web\Controller; use app\models\LoginForm; +use app\models\ContactForm; class SiteController extends Controller { @@ -14,7 +15,7 @@ class SiteController extends Controller { $model = new LoginForm(); if ($this->populate($_POST, $model) && $model->login()) { - Yii::$app->getResponse()->redirect(array('site/index')); + Yii::$app->response->redirect(array('site/index')); } else { echo $this->render('login', array( 'model' => $model, @@ -30,7 +31,15 @@ class SiteController extends Controller public function actionContact() { - echo $this->render('contact'); + $model = new ContactForm; + if ($this->populate($_POST, $model) && $model->contact(Yii::$app->params['adminEmail'])) { + Yii::$app->session->setFlash('contact', 'Thank you for contacting us. We will respond to you as soon as possible.'); + Yii::$app->response->refresh(); + } else { + echo $this->render('contact', array( + 'model' => $model, + )); + } } public function actionAbout() diff --git a/app/protected/models/ContactForm.php b/app/protected/models/ContactForm.php new file mode 100644 index 0000000..8e8f831 --- /dev/null +++ b/app/protected/models/ContactForm.php @@ -0,0 +1,63 @@ + !Captcha::checkRequirements()), + ); + } + + /** + * @return array customized attribute labels + */ + public function attributeLabels() + { + return array( + 'verifyCode' => 'Verification Code', + ); + } + + /** + * Sends an email to the specified email address using the information collected by this model. + * @param string $email the target email address + * @return boolean whether the model passes validation + */ + public function contact($email) + { + if ($this->validate()) { + $name = '=?UTF-8?B?' . base64_encode($this->name) . '?='; + $subject = '=?UTF-8?B?' . base64_encode($this->subject) . '?='; + $headers = "From: $name <{$this->email}>\r\n" . + "Reply-To: {$this->email}\r\n" . + "MIME-Version: 1.0\r\n" . + "Content-type: text/plain; charset=UTF-8"; + mail($email, $subject, $this->body, $headers); + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/app/protected/models/LoginForm.php b/app/protected/models/LoginForm.php index b68e146..23e8c92 100644 --- a/app/protected/models/LoginForm.php +++ b/app/protected/models/LoginForm.php @@ -1,9 +1,4 @@ - * @since 2.0 + * LoginForm is the model behind the login form. */ class LoginForm extends Model { @@ -20,16 +14,25 @@ class LoginForm extends Model public $password; public $rememberMe = true; + /** + * @return array the validation rules. + */ public function rules() { return array( - array('username', 'required'), - array('password', 'required'), + // username and password are both required + array('username, password', 'required'), + // password is validated by validatePassword() array('password', 'validatePassword'), + // rememberMe must be a boolean value array('rememberMe', 'boolean'), ); } + /** + * Validates the password. + * This method serves as the inline validation for password. + */ public function validatePassword() { $user = User::findByUsername($this->username); @@ -38,11 +41,15 @@ class LoginForm extends Model } } + /** + * Logs in a user using the provided username and password. + * @return boolean whether the user is logged in successfully + */ public function login() { if ($this->validate()) { $user = User::findByUsername($this->username); - Yii::$app->getUser()->login($user, $this->rememberMe ? 3600*24*30 : 0); + Yii::$app->user->login($user, $this->rememberMe ? 3600*24*30 : 0); return true; } else { return false; diff --git a/app/protected/views/layouts/main.php b/app/protected/views/layouts/main.php index a09b610..a455f20 100644 --- a/app/protected/views/layouts/main.php +++ b/app/protected/views/layouts/main.php @@ -41,6 +41,8 @@ $this->registerAssetBundle('app'); +
+ -
- diff --git a/framework/assets/yii.activeForm.js b/framework/assets/yii.activeForm.js index 922bc92..158ea74 100644 --- a/framework/assets/yii.activeForm.js +++ b/framework/assets/yii.activeForm.js @@ -83,7 +83,7 @@ settings.validationUrl = $form.attr('action'); } $.each(attributes, function (i) { - attributes[i] = $.extend({}, attributeDefaults, this); + attributes[i] = $.extend({value: getValue($form, this)}, attributeDefaults, this); }); $form.data('yiiActiveForm', { settings: settings, @@ -121,7 +121,7 @@ }, submitForm: function () { - var $form = this, + var $form = $(this), data = $form.data('yiiActiveForm'); if (data.validated) { // continue submitting the form since validation passes @@ -163,7 +163,7 @@ }, resetForm: function () { - var $form = this; + var $form = $(this); var data = $form.data('yiiActiveForm'); // Because we bind directly to a form reset event instead of a reset button (that may not exist), // when this function is executed form input values have not been reset yet. diff --git a/framework/helpers/base/Html.php b/framework/helpers/base/Html.php index 6c875bb..5b8e7db 100644 --- a/framework/helpers/base/Html.php +++ b/framework/helpers/base/Html.php @@ -95,9 +95,9 @@ class Html public static $attributeOrder = array( 'type', 'id', - 'class', 'name', 'value', + 'class', 'href', 'src', diff --git a/framework/web/Controller.php b/framework/web/Controller.php index 8049299..099bf96 100644 --- a/framework/web/Controller.php +++ b/framework/web/Controller.php @@ -8,7 +8,6 @@ namespace yii\web; use Yii; -use yii\helpers\Html; /** * Controller is the base class of Web controllers. diff --git a/framework/web/Response.php b/framework/web/Response.php index 1d604e9..0503755 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -115,6 +115,7 @@ class Response extends \yii\base\Response *
  • forceDownload: specifies whether the file will be downloaded or shown inline, defaults to true
  • *
  • addHeaders: an array of additional http headers in header-value pairs
  • * + * @todo */ public function xSendFile($filePath, $options = array()) { @@ -196,6 +197,19 @@ class Response extends \yii\base\Response } /** + * Refreshes the current page. + * The effect of this method call is the same as the user pressing the refresh button of his browser + * (without re-posting data). + * @param boolean $terminate whether to terminate the current application after calling this method + * @param string $anchor the anchor that should be appended to the redirection URL. + * Defaults to empty. Make sure the anchor starts with '#' if you want to specify it. + */ + public function refresh($terminate = true, $anchor = '') + { + $this->redirect(Yii::$app->getRequest()->getUrl() . $anchor, $terminate); + } + + /** * Returns the cookie collection. * Through the returned cookie collection, you add or remove cookies as follows, * diff --git a/framework/widgets/ActiveField.php b/framework/widgets/ActiveField.php index 55ff8e2..da17012 100644 --- a/framework/widgets/ActiveField.php +++ b/framework/widgets/ActiveField.php @@ -48,13 +48,18 @@ class ActiveField extends Component */ public $template = "{label}\n
    \n{input}\n{error}\n
    "; /** - * @var array the default options for the error message. This property is used when calling [[error()]] - * without the `$options` parameter. + * @var array the default options for the input tags. The parameter passed to individual input methods + * (e.g. [[textInput()]]) will be merged with this property when rendering the input tag. + */ + public $inputOptions = array(); + /** + * @var array the default options for the error tags. The parameter passed to [[error()]] will be + * merged with this property when rendering the error tag. */ public $errorOptions = array('tag' => 'span', 'class' => 'help-inline'); /** - * @var array the default options for the label. This property is used when calling [[label()]] - * without the `$options` parameter. + * @var array the default options for the label tags. The parameter passed to [[label()]] will be + * merged with this property when rendering the label tag. */ public $labelOptions = array('class' => 'control-label'); /** @@ -174,7 +179,7 @@ class ActiveField extends Component /** * Generates a label tag for [[attribute]]. * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]]. - * @param array $options the tag options in terms of name-value pairs. If this is null, [[labelOptions]] will be used. + * @param array $options the tag options in terms of name-value pairs. It will be merged with [[labelOptions]]. * The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * @@ -186,18 +191,16 @@ class ActiveField extends Component * * @return string the generated label tag */ - public function label($options = null) + public function label($options = array()) { - if ($options === null) { - $options = $this->labelOptions; - } + $options = array_merge($this->labelOptions, $options); return Html::activeLabel($this->model, $this->attribute, $options); } /** * Generates a tag that contains the first validation error of [[attribute]]. * Note that even if there is no validation error, this method will still return an empty error tag. - * @param array $options the tag options in terms of name-value pairs. If this is null, [[errorOptions]] will be used. + * @param array $options the tag options in terms of name-value pairs. It will be merged with [[errorOptions]]. * The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * @@ -207,11 +210,9 @@ class ActiveField extends Component * * @return string the generated label tag */ - public function error($options = null) + public function error($options = array()) { - if ($options === null) { - $options = $this->errorOptions; - } + $options = array_merge($this->errorOptions, $options); $attribute = Html::getAttributeName($this->attribute); $error = $this->model->getFirstError($attribute); $tag = isset($options['tag']) ? $options['tag'] : 'span'; @@ -244,6 +245,7 @@ class ActiveField extends Component */ public function input($type, $options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activeInput($type, $this->model, $this->attribute, $options)); } @@ -257,6 +259,7 @@ class ActiveField extends Component */ public function textInput($options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activeTextInput($this->model, $this->attribute, $options)); } @@ -270,6 +273,7 @@ class ActiveField extends Component */ public function hiddenInput($options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activeHiddenInput($this->model, $this->attribute, $options)); } @@ -283,6 +287,7 @@ class ActiveField extends Component */ public function passwordInput($options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activePasswordInput($this->model, $this->attribute, $options)); } @@ -296,6 +301,7 @@ class ActiveField extends Component */ public function fileInput($options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activeFileInput($this->model, $this->attribute, $options)); } @@ -308,6 +314,7 @@ class ActiveField extends Component */ public function textarea($options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activeTextarea($this->model, $this->attribute, $options)); } @@ -331,6 +338,7 @@ class ActiveField extends Component */ public function radio($options = array(), $enclosedByLabel = true) { + $options = array_merge($this->inputOptions, $options); if ($enclosedByLabel) { $hidden = ''; $radio = Html::activeRadio($this->model, $this->attribute, $options); @@ -369,6 +377,7 @@ class ActiveField extends Component */ public function checkbox($options = array(), $enclosedByLabel = true) { + $options = array_merge($this->inputOptions, $options); if ($enclosedByLabel) { $hidden = ''; $checkbox = Html::activeCheckbox($this->model, $this->attribute, $options); @@ -421,6 +430,7 @@ class ActiveField extends Component */ public function dropDownList($items, $options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activeDropDownList($this->model, $this->attribute, $items, $options)); } @@ -461,6 +471,7 @@ class ActiveField extends Component */ public function listBox($items, $options = array()) { + $options = array_merge($this->inputOptions, $options); return $this->render(Html::activeListBox($this->model, $this->attribute, $items, $options)); } diff --git a/framework/widgets/ActiveForm.php b/framework/widgets/ActiveForm.php index 7e284ba..c11bceb 100644 --- a/framework/widgets/ActiveForm.php +++ b/framework/widgets/ActiveForm.php @@ -40,9 +40,7 @@ class ActiveForm extends Widget /** * @var array the default configuration used by [[field()]] when creating a new field object. */ - public $fieldConfig = array( - 'class' => 'yii\widgets\ActiveField', - ); + public $fieldConfig; /** * @var string the default CSS class for the error summary container. * @see errorSummary() @@ -121,6 +119,9 @@ class ActiveForm extends Widget if (!isset($this->options['id'])) { $this->options['id'] = $this->getId(); } + if (!isset($this->fieldConfig['class'])) { + $this->fieldConfig['class'] = 'yii\widgets\ActiveField'; + } echo Html::beginForm($this->action, $this->method, $this->options); }