diff --git a/src/BulkButtonWidget.php b/src/BulkButtonWidget.php
index cd2ed5e..ae44be8 100644
--- a/src/BulkButtonWidget.php
+++ b/src/BulkButtonWidget.php
@@ -1,24 +1,196 @@
'bulk-actions' ];
+
+ /**
+ * @var string the ID of the controller that should handle the actions specified here.
+ * If not set, it will use the currently active controller. This property is mainly used by
+ * [[urlCreator]] to create URLs for different actions. The value of this property will be prefixed
+ * to each action name to form the route of the action.
+ */
+ public $controller;
+
+ /**
+ * @var string the template used for composing each cell in the action column.
+ * Tokens enclosed within curly brackets are treated as controller action IDs (also called *button names*
+ * in the context of action column). They will be replaced by the corresponding button rendering callbacks
+ * specified in [[buttons]]. For example, the token `{view}` will be replaced by the result of
+ * the callback `buttons['view']`. If a callback cannot be found, the token will be replaced with an empty string.
+ *
+ * @see buttons
+ */
+ public $template = '{bulk-delete}';
+
+ /**
+ * @var array button rendering callbacks. The array keys are the button names (without curly brackets),
+ * and the values are the corresponding button rendering callbacks. The callbacks should use the following
+ * signature:
+ *
+ * ```php
+ * function ($url) {
+ * // return the button HTML code
+ * }
+ * ```
+ *
+ * where `$url` is the URL that the column creates for the button.
+ *
+ * ```php
+ * [
+ * 'update' => function ($url) {
+ * return Html::a('Update', $url);
+ * },
+ * ],
+ * ```
+ */
+ public $buttons = [];
-class BulkButtonWidget extends Widget{
+ /** @var array visibility conditions for each button. The array keys are the button names (without curly brackets),
+ * and the values are the boolean true/false or the anonymous function. When the button name is not specified in
+ * this array it will be shown by default.
+ * The callbacks must use the following signature:
+ *
+ * Pass a boolean value:
+ *
+ * ```php
+ * [
+ * 'update' => \Yii::$app->user->can('update'),
+ * ],
+ * ```
+ * @since 2.0.7
+ */
- public $buttons;
-
- public function init(){
+ public $visibleButtons = [];
+
+ /**
+ * @var callable a callback that creates a button URL using the specified model information.
+ * The signature of the callback should be the same as that of [[createUrl()]]
+ * Since 2.0.10 it can accept additional parameter, which refers to the column instance itself:
+ *
+ * ```php
+ * function (string $action) {
+ * //return string;
+ * }
+ * ```
+ *
+ * If this property is not set, button URLs will be created using [[createUrl()]].
+ */
+ public $urlCreator;
+
+ /**
+ * @var array html options to be applied to the [[initDefaultButton()|default button]].
+ * @since 2.0.4
+ */
+ public $buttonOptions = [];
+
+
+ public function init() {
parent::init();
-
+ $this->initDefaultButtons();
+ }
+
+ /**
+ * Initializes the default button rendering callbacks.
+ */
+ protected function initDefaultButtons() {
+ $this->initDefaultButton( 'bulk-delete', 'trash', [
+ 'class' => 'btn btn-danger btn-xs',
+ 'role' => 'modal-remote-bulk',
+ 'data-confirm' => false,
+ 'data-method' => false,// for overide yii data api
+ 'data-request-method' => 'post',
+ 'data-confirm-title' => yii::t( 'app', 'Are you sure?' ),
+ 'data-confirm-message' => yii::t( 'app', 'Are you sure want to delete these items' ),
+ ] );
}
-
- public function run(){
- $content = '
'.
- ' With selected '.
- $this->buttons.
- '
';
- return $content;
+
+ /**
+ * Initializes the default button rendering callback for single button
+ *
+ * @param string $name Button name as it's written in template
+ * @param string $iconName The part of Bootstrap glyphicon class that makes it unique
+ * @param array $additionalOptions Array of additional options
+ *
+ * @since 2.0.11
+ */
+ protected function initDefaultButton( $name, $iconName, $additionalOptions = [] ) {
+ if ( ! isset( $this->buttons[ $name ] ) && strpos( $this->template, '{' . $name . '}' ) !== false ) {
+ $this->buttons[ $name ] = function ( $url ) use ( $name, $iconName, $additionalOptions ) {
+ switch ( $name ) {
+ case 'bulk-delete':
+ $title = Yii::t( 'yii', 'Delete' );
+ break;
+ default:
+ $title = ucfirst( $name );
+ }
+ $options = array_merge( [
+ 'title' => $title,
+ 'aria-label' => $title,
+ ], $additionalOptions, $this->buttonOptions );
+ $icon = Html::tag( 'span', '', [ 'class' => "glyphicon glyphicon-$iconName" ] );
+
+ return Html::a( $icon . ' ' . $title, $url, $options );
+ };
+ }
+ }
+
+ public function run() {
+ $content = preg_replace_callback( '/\\{([\w\-\/]+)\\}/', function ( $matches ) {
+ $name = $matches[1];
+
+ if ( isset( $this->visibleButtons[ $name ] ) ) {
+ $isVisible = $this->visibleButtons[ $name ] instanceof \Closure
+ ? call_user_func( $this->visibleButtons[ $name ] )
+ : $this->visibleButtons[ $name ];
+ } else {
+ $isVisible = true;
+ }
+
+ if ( $isVisible && isset( $this->buttons[ $name ] ) ) {
+ $url = $this->createUrl( $name );
+
+ return call_user_func( $this->buttons[ $name ], $url );
+ } else {
+ return '';
+ }
+ }, $this->template );
+
+ return ( ! empty( $content ) ?
+ '' .
+ ' ' .
+ Yii::t( 'app', 'With selected' ) . ' ' .
+ $content .
+ '
' :
+ ''
+ );
+ }
+
+ /**
+ * Creates a URL for the given action and model.
+ * This method is called for each button and each row.
+ *
+ * @param string $action the button name (or action ID)
+ *
+ * @return string the created URL
+ */
+ public function createUrl( $action ) {
+ if ( is_callable( $this->urlCreator ) ) {
+ return call_user_func( $this->urlCreator, $action, $this );
+ } else {
+ $params = [ $this->controller ? $this->controller . '/' . $action : $action ];
+
+ return Url::toRoute( $params );
+ }
}
-}
-?>
+}
\ No newline at end of file
diff --git a/src/assets/ModalRemote.js b/src/assets/ModalRemote.js
index 0b7b616..1a655d0 100644
--- a/src/assets/ModalRemote.js
+++ b/src/assets/ModalRemote.js
@@ -213,13 +213,24 @@ function ModalRemote(modalId) {
*/
function successRemoteResponse(response) {
+ // Close modal then redirect to a new url if response contains forceRedirect field
+ if (response.forceRedirect !== undefined && response.forceRedirect) {
+ this.hide();
+ window.location.href = response.forceRedirect;
+ return;
+ }
+
// Reload datatable if response contain forceReload field
if (response.forceReload !== undefined && response.forceReload) {
if (response.forceReload == 'true') {
// Backwards compatible reload of fixed crud-datatable-pjax
- $.pjax.reload({container: '#crud-datatable-pjax'});
+ if(document.querySelector('#crud-datatable-pjax') !== null) {
+ $.pjax.reload({container: '#crud-datatable-pjax'});
+ }
} else {
- $.pjax.reload({container: response.forceReload});
+ if(document.querySelector(response.forceReload) !== null) {
+ $.pjax.reload({container: response.forceReload});
+ }
}
}
diff --git a/src/assets/ModalRemote.min.js b/src/assets/ModalRemote.min.js
index 1acddb2..d75fbea 100644
--- a/src/assets/ModalRemote.min.js
+++ b/src/assets/ModalRemote.min.js
@@ -1 +1 @@
-function ModalRemote(t){function o(){this.show(),this.displayLoading()}function e(t){this.setTitle(t.status+t.statusText),this.setContent(t.responseText),this.addFooterButton("Close","button","btn btn-default",function(t,o){this.hide()})}function i(t){return void 0!==t.forceReload&&t.forceReload&&("true"==t.forceReload?$.pjax.reload({container:"#crud-datatable-pjax"}):$.pjax.reload({container:t.forceReload})),void 0!==t.forceClose&&t.forceClose?void this.hide():(void 0!==t.size&&this.setSize(t.size),void 0!==t.title&&this.setTitle(t.title),void 0!==t.content&&this.setContent(t.content),void 0!==t.footer&&this.setFooter(t.footer),void(void 0!==$(this.content).find("form")[0]&&this.setupFormSubmit($(this.content).find("form")[0],$(this.footer).find('[type="submit"]')[0])))}this.defaults={okLabel:"OK",executeLabel:"Execute",cancelLabel:"Cancel",loadingTitle:"Loading"},this.modal=$(t),this.dialog=$(t).find(".modal-dialog"),this.header=$(t).find(".modal-header"),this.content=$(t).find(".modal-body"),this.footer=$(t).find(".modal-footer"),this.loadingContent='',this.show=function(){this.clear(),$(this.modal).modal("show")},this.hide=function(){$(this.modal).modal("hide")},this.toggle=function(){$(this.modal).modal("toggle")},this.clear=function(){$(this.modal).find(".modal-title").remove(),$(this.content).html(""),$(this.footer).html("")},this.setSize=function(t){$(this.dialog).removeClass("modal-lg"),$(this.dialog).removeClass("modal-sm"),"large"==t?$(this.dialog).addClass("modal-lg"):"small"==t?$(this.dialog).addClass("modal-sm"):"normal"!==t&&console.warn("Undefined size "+t)},this.setHeader=function(t){$(this.header).html(t)},this.setContent=function(t){$(this.content).html(t)},this.setFooter=function(t){$(this.footer).html(t)},this.setTitle=function(t){$(this.header).find("h4.modal-title").remove(),$(this.header).append(''+t+"
")},this.hidenCloseButton=function(){$(this.header).find("button.close").hide()},this.showCloseButton=function(){$(this.header).find("button.close").show()},this.displayLoading=function(){this.setContent(this.loadingContent),this.setTitle(this.defaults.loadingTitle)},this.addFooterButton=function(t,o,e,i){buttonElm=document.createElement("button"),buttonElm.setAttribute("type",null===o?"button":o),buttonElm.setAttribute("class",null===e?"btn btn-primary":e),buttonElm.innerHTML=t;var a=this;$(this.footer).append(buttonElm),null!==i&&$(buttonElm).click(function(t){i.call(a,this,t)})},this.doRemote=function(t,a,n){var s=this;$.ajax({url:t,method:a,data:n,async:!1,beforeSend:function(){o.call(s)},error:function(t){e.call(s,t)},success:function(t){i.call(s,t)},contentType:!1,cache:!1,processData:!1})},this.setupFormSubmit=function(t,o){if(void 0===o)console.warn("Modal has form but does not have a submit button");else{var e=this;$(o).click(function(o){var i;i=window.FormData?new FormData($(t)[0]):$(t).serializeArray(),e.doRemote($(t).attr("action"),$(t).hasAttr("method")?$(t).attr("method"):"GET",i)})}},this.confirmModal=function(t,o,e,i,a,n,s,d){this.show(),this.setSize(a),void 0!==t&&this.setTitle(t),this.setContent('