diff --git a/apps/basic/controllers/SiteController.php b/apps/basic/controllers/SiteController.php index 785eddf..1196280 100644 --- a/apps/basic/controllers/SiteController.php +++ b/apps/basic/controllers/SiteController.php @@ -3,7 +3,9 @@ namespace app\controllers; use Yii; +use yii\web\AccessControl; use yii\web\Controller; +use yii\web\VerbFilter; use app\models\LoginForm; use app\models\ContactForm; @@ -13,7 +15,7 @@ class SiteController extends Controller { return array( 'access' => array( - 'class' => \yii\web\AccessControl::className(), + 'class' => AccessControl::className(), 'only' => array('login', 'logout'), 'rules' => array( array( @@ -28,6 +30,12 @@ class SiteController extends Controller ), ), ), + 'verbs' => array( + 'class' => VerbFilter::className(), + 'actions' => array( + 'logout' => array('post'), + ), + ), ); } diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php index 240c2a3..1b7083d 100644 --- a/apps/basic/views/layouts/main.php +++ b/apps/basic/views/layouts/main.php @@ -36,7 +36,9 @@ app\config\AppAsset::register($this); array('label' => 'Contact', 'url' => array('/site/contact')), Yii::$app->user->isGuest ? array('label' => 'Login', 'url' => array('/site/login')) : - array('label' => 'Logout (' . Html::encode(Yii::$app->user->identity->username) .')' , 'url' => array('/site/logout')), + array('label' => 'Logout (' . Yii::$app->user->identity->username .')' , + 'url' => array('/site/logout'), + 'linkOptions' => array('data-method' => 'post')), ), )); NavBar::end(); diff --git a/framework/yii/assets/yii.js b/framework/yii/assets/yii.js index eb5ecf6..b1fb0cb 100644 --- a/framework/yii/assets/yii.js +++ b/framework/yii/assets/yii.js @@ -37,26 +37,103 @@ * * Using this structure, you can define public and private functions/properties for a module. * Private functions/properties are only visible within the module, while public functions/properties - * may be accessed outside of the module. For example, you can access "yii.sample.init()". + * may be accessed outside of the module. For example, you can access "yii.sample.isActive". * * You must call "yii.initModule()" once for the root module of all your modules. */ yii = (function ($) { var pub = { /** + * The selector for links that support confirmation and form submission. + */ + linkClickSelector: 'a[data-confirm], a[data-method]', + + /** * @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled. */ - getCsrfVar: function() { + getCsrfVar: function () { return $('meta[name=csrf-var]').prop('content'); }, /** * @return string|undefined the CSRF token. Undefined is returned is CSRF validation is not enabled. */ - getCsrfToken: function() { + getCsrfToken: function () { return $('meta[name=csrf-token]').prop('content'); }, + /** + * Displays a confirmation dialog. + * The default implementation simply displays a js confirmation dialog. + * You may override this by setting `yii.confirm`. + * @param message the confirmation message. + * @return boolean whether the user confirms with the message in the dialog + */ + confirm: function (message) { + return confirm(message); + }, + + /** + * Returns a value indicating whether to allow executing the action defined for the specified element. + * @param $e the jQuery representation of the element + * @return boolean whether to allow executing the action defined for the specified element. + */ + allowAction: function ($e) { + var message = $e.data('confirm'); + if (!message) { + return true; + } + return pub.confirm(message); + }, + + /** + * Handles form submission triggered by elements with "method" data attribute. + * If the element is enclosed within an existing form, the form will be submitted. + * Otherwise, a new form will be created and submitted. The new form's method and action + * are determined using the element's "method" and "action" data attributes, respectively. + * If the "action" data attribute is not specified, it will try the "href" property and + * the current URL. + * @param $e the jQuery representation of the element + */ + handleSubmit: function ($e) { + var method = $e.data('method'); + if (method === undefined) { + return; + } + + var $form = $e.closest('form'); + if (!$form.length) { + var action = $e.data('action'); + if (action === undefined) { + action = $e.prop('href'); + if (action === undefined) { + action = window.location.href; + } + } + $form = $('
'); + var target = $e.prop('target'); + if (target) { + $form.attr('target', target); + } + if (!method.match(/(get|post)/i)) { + $form.append(''); + } + var csrfVar = pub.getCsrfVar(); + if (csrfVar) { + $form.append(''); + } + $form.hide().appendTo('body'); + } + + var activeFormData = $form.data('yiiActiveForm'); + if (activeFormData) { + // remember who triggers the form submission. This is used by yii.activeForm.js + activeFormData.submitObject = $e; + } + + $form.trigger('submit'); + }, + initModule: function (module) { if (module.isActive === undefined || module.isActive) { if ($.isFunction(module.init)) { @@ -68,6 +145,23 @@ yii = (function ($) { } }); } + }, + + init: function () { + var $document = $(document); + + $document.on('click.yii', pub.linkClickSelector, function () { + var $this = $(this); + if (!pub.allowAction($this)) { + $this.stopImmediatePropagation(); + return false; + } else { + if ($this.data('method')) { + pub.handleSubmit($this); + return false; + } + } + }); } }; return pub;