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);
}