Skip to content

Commit fab86a7

Browse files
committed
Merge pull request #263 from calvera/modals
Use bootstrap modals instead of javascript confirm
2 parents edd5681 + c411f73 commit fab86a7

16 files changed

+269
-41
lines changed

Generator/Action.php

+15
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class Action
3232

3333
protected $confirmMessage;
3434

35+
protected $confirmModal;
36+
3537
protected $csrfProtected = false;
3638

3739
protected $forceIntermediate = false;
@@ -152,6 +154,19 @@ public function getConfirm()
152154
return $this->confirmMessage;
153155
}
154156

157+
/**
158+
* @param string $confirmModal
159+
*/
160+
public function setConfirmModal($confirmModal)
161+
{
162+
$this->confirmModal = $confirmModal;
163+
}
164+
165+
public function getConfirmModal()
166+
{
167+
return $this->confirmModal;
168+
}
169+
155170
/**
156171
* @param boolean $csrfProtected
157172
*/
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Modals with additional fields
2+
3+
[go back to Table of contents][back-to-index]
4+
5+
-----
6+
7+
Sometimes, you may need to add a field to modal dialog.
8+
You can simply do it by adding modal with the field:
9+
10+
```twig
11+
{% extends_admingenerated "AdmingeneratorDoctrineOrmDemoBundle:PostList:index.html.twig" %}
12+
13+
{% block modals %}
14+
{{ parent() }}
15+
<div id="confirmObjectModalPostpone" class="modal confirm-object-modal fade" role="dialog">
16+
<div class="modal-dialog">
17+
<div class="modal-content">
18+
<form role="form" action="#" method="post" data-ajax="false">
19+
<div class="modal-header">
20+
<button type="button" class="close" data-dismiss="modal">&times;</button>
21+
<h4 class="modal-title"></h4>
22+
</div>
23+
<div class="modal-body">
24+
<div class="form-group">
25+
<label for="postpone">Postpone:</label>
26+
<input type="date" name="postpone"/>
27+
</div>
28+
</div>
29+
<div class="modal-footer">
30+
<button type="button" class="btn btn-default cancel" data-dismiss="modal">{{ 'action.custom.cancel'|trans({}, "Admingenerator")|raw }}</button>
31+
<button type="submit" class="btn btn-primary confirm">{{ 'action.custom.confirm'|trans({}, "Admingenerator")|raw }}</button>
32+
</div>
33+
</form>
34+
</div>
35+
</div>
36+
</div>
37+
{% endblock %}
38+
```
39+
40+
In your `YourBundleNameBundle/Resources/config/yourPrefix-generator.yml`
41+
42+
```yaml
43+
params:
44+
object_actions:
45+
postpone:
46+
confirm: Do you really want to postpone?
47+
confirmModal: confirmObjectModalPostpone
48+
```
49+
50+
Now you will have the postpone date in POST parameter
51+
52+
```php
53+
$request->request->get('postpone')
54+
```
55+
56+
Result
57+
58+
![modal with field](images/modal-with-field.jpg)
59+
60+
[back-to-index]: ../documentation.md

Resources/doc/customization/actions.md

+12
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ protected function errorObjectToggleisvalid() { ... }
264264

265265
* [Class](#class)
266266
* [Confirm](#confirm)
267+
* [ConfirmModal](#confirmModal)
267268
* [Credentials](#credentials)
268269
* [CsrfProtected](#csrf-protected)
269270
* [ForceIntermediate](#force-intermediate)
@@ -287,6 +288,17 @@ Add any css class(es) to the rendered button.
287288
Used to set a confirm message. When set, the action will first use a javascript popup with your confirm message to ask
288289
for confirmation from the user.
289290

291+
##### ConfirmModal
292+
293+
`confirmModal` __type__: `string`
294+
295+
Used to set an id of modal confirm dialog. Use when you want to use different id than default:
296+
* `confirmGenericModal`: Used for generic-actions
297+
* `confirmBatchModal`: Used for batch-actions
298+
* `confirmObjectModal`: Used for object-actions
299+
300+
This is not needed unless you want to use a dialog with special field(s). For example action with a parameter.
301+
290302
##### Credentials
291303

292304
`credentials` __type__: `string`

Resources/doc/documentation.md

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ we encourage you to update to latest build. If you're getting errors or have tro
3232
- [KnpMenuBundle][cookbook-menu]
3333
- [Multiple entity managers (Doctrine)][cookbook-em]
3434
- [View parameters][cookbook-view-parameters]
35+
- [Modal with fields][modal-with-fields]
3536
5. Support and contribution
3637
- [Submitting issues][support-issues]
3738
- [Contributing code][support-contributing]
@@ -90,3 +91,4 @@ Below are some screenshots of our very simple demo project.
9091
[cookbook-em]: cookbook/multiple-entity-managers.md
9192
[cookbook-menu]: cookbook/knp-menu.md
9293
[cookbook-view-parameters]: cookbook/view-parameters.md
94+
[modal-with-fields]: cookbook/modal-with-fields.md

Resources/public/js/main.js

+70-32
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,26 @@
1313
},
1414
options
1515
);
16-
$(this.options.containerSelector).on('click', this.options.buttonSelector, this.clickHandler.bind(this));
16+
$(this.options.containerSelector).on('click',this.options.buttonSelector,this.clickHandler.bind(this));
1717
};
1818

1919
S2A.singleActionsManager.prototype = {
2020
clickHandler: function(evt){
2121
var $elt = $(evt.currentTarget);
22-
23-
if (!this.isConfirmed($elt)) {
24-
evt.preventDefault();
25-
return;
26-
}
27-
28-
if (this.isProtected($elt)) {
22+
if (this.isProtected($elt) && !this.needConfirmation($elt)) {
2923
evt.preventDefault();
3024
this.sendSecured($elt);
3125
}
3226
},
3327

34-
isConfirmed: function($elt){
35-
// TODO: move confirm() to custom popin
36-
return !$elt.data('confirm') || confirm($elt.data('confirm'));
37-
},
38-
3928
isProtected: function($elt){
4029
return !!$elt.data('csrf-token');
4130
},
4231

32+
needConfirmation: function($elt){
33+
return !!$elt.data('confirm');
34+
},
35+
4336
sendSecured: function($elt){
4437
// Transform in POST request
4538
var $form = $('<form />').attr({
@@ -53,12 +46,9 @@
5346
name: '_csrf_token',
5447
value: $elt.data('csrf-token')
5548
}).appendTo($form);
56-
// TODO: add pre-submit trigger
5749
$form.submit();
58-
// TODO: add post-submit trigger
5950
}
6051
};
61-
6252
S2A.batchActionsManager = function(options){
6353
this.options = $.extend( {}, {
6454
containerSelector: 'document',
@@ -87,19 +77,18 @@
8777
}
8878

8979
if (!this.hasElementsSelected()) {
90-
// TODO: move this to a popin or dynamic message displayer
91-
alert(this.options.noElementSelectedMessage);
80+
evt.preventDefault();
9281
$elt.val(this.options.noActionValue);
82+
$('#alertModal').find('.modal-title').text(this.options.noElementSelectedMessage);
83+
$('#alertModal').modal('show');
9384
return;
9485
}
9586

96-
if (!this.isConfirmed($elt)) {
97-
$elt.val(this.options.noActionValue);
87+
if (this.needConfirmation($elt)) {
88+
$(this.selectedOption($elt).data('confirm-modal')).modal('show', $elt);
9889
return;
9990
}
10091

101-
// TODO: pre-submit trigger
102-
// Send the form
10392
$elt[0].form.submit();
10493
},
10594

@@ -119,15 +108,14 @@
119108
return 0 !== $(this.options.containerSelector + ' ' + this.options.elementSelector).filter(':checked').length;
120109
},
121110

122-
isConfirmed: function($elt){
123-
var $selectedOption = $(':selected', $elt);
124-
125-
if (0 == $selectedOption.length) {
126-
return false;
127-
}
111+
needConfirmation: function($elt){
112+
return !!this.selectedOption($elt).data('confirm');
113+
},
128114

129-
return !$selectedOption.data('confirm') || confirm($selectedOption.data('confirm'));
115+
selectedOption: function ($elt) {
116+
return $(':selected', $elt);
130117
}
118+
131119
};
132120

133121
S2A.nestedListManager = function(options){
@@ -158,7 +146,7 @@
158146

159147
// Force first tab to be displayed
160148
$('.nav-tabs *[data-toggle="tab"]:first').click();
161-
149+
162150
// Display number of errors on tabs
163151
$('.nav.nav-tabs li').each(function(i){
164152
$(this).find('a span.label-danger').remove();
@@ -167,9 +155,59 @@
167155
$(this).find('a:first').append('<span class="label label-danger">'+invalid_items.length+'</span>');
168156
}
169157
});
170-
158+
171159
// Display object actions tooltips
172-
$('a.object-action[data-toggle="tooltip"]').tooltip();
160+
$('a.object-action').tooltip();
161+
162+
// Save action for modals
163+
$('.object-action, .generic-action, select[name=action] option').each(function(index, item) {
164+
$item = $(item);
165+
$item.data('action', $item.attr('href'));
166+
$item.attr('href', $item.data('confirmModal'));
167+
});
168+
169+
// hookup on submit button
170+
$('button[type=submit].generic-action').click(function(event) {
171+
if ($(this).data('confirm')) {
172+
event.preventDefault();
173+
}
174+
});
175+
176+
$('.confirm-object-modal, .confirm-generic-modal').on('show.bs.modal', function (event) {
177+
var $elt = $(event.relatedTarget);
178+
var $form = $(this).find('form');
179+
var action = $elt.data('action');
180+
var confirm = $elt.data('confirm');
181+
var csrf_token = $elt.data('csrf-token');
182+
$form.attr('action', action);
183+
$(this).find('.modal-title').text(confirm);
184+
// submit button confirmation
185+
if ($elt.is('button[type=submit]')) {
186+
$form.submit(function(event) {
187+
event.preventDefault();
188+
$elt.closest('form').submit();
189+
});
190+
}
191+
if (csrf_token) {
192+
$('<input />').attr({
193+
type: 'hidden',
194+
name: '_csrf_token',
195+
value: csrf_token
196+
}).appendTo($form);
197+
}
198+
});
199+
200+
$('.confirm-batch-modal').on('show.bs.modal', function (event) {
201+
var $elt = $(event.relatedTarget);
202+
var confirm = $(':selected', $elt).data('confirm');
203+
$(this).find('.modal-title').text(confirm);
204+
$(this).find('.confirm').click(function() {
205+
$elt[0].form.submit();
206+
})
207+
$(this).find('.cancel').click(function() {
208+
$elt.val(S2A.batchActionsAdminOptions.noActionValue);
209+
})
210+
});
173211

174212
// Object actions
175213
if (S2A.hasOwnProperty('singleActionsAdminOptions')) {

Resources/templates/CommonAdmin/EditTemplate/EditBuilderTemplate.php.twig

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{% use '../CommonAdmin/tabs.php.twig' %}
55
{% use '../CommonAdmin/generic_actions.php.twig' %}
66
{% use '../CommonAdmin/object_actions.php.twig' %}
7+
{% use '../CommonAdmin/modals.php.twig' %}
78

89
{{ echo_extends( builder.getBaseAdminTemplate ) }}
910

@@ -75,3 +76,5 @@
7576
</div>
7677
</div>
7778
{{ echo_endblock() }}
79+
80+
{{- block('modals') -}}

Resources/templates/CommonAdmin/ExcelAction/ExcelBuilderAction.php.twig

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{% use '../CommonAdmin/security_action.php.twig' %}
2+
{% use '../CommonAdmin/csrf_protection.php.twig' %}
23
<?php
34
45
namespace Admingenerated\{{ namespace_prefix }}{{ bundle_name }}\{{ builder.generator.GeneratedControllerFolder }};
@@ -28,8 +29,9 @@ class ExcelController extends \{{ namespace_prefix }}\{{ bundle_name }}\Controll
2829
public function excelAction(Request $request)
2930
{
3031
$this->request = $request;
32+
3133
{{ block('security_action') }}
32-
34+
3335
// Create the PHPExcel object with some standard values
3436
try {
3537
$phpexcel = $this->get('phpexcel');
@@ -189,4 +191,6 @@ class ExcelController extends \{{ namespace_prefix }}\{{ bundle_name }}\Controll
189191
}
190192
191193
{% endfor %}
194+
195+
{{- block('csrf_check_token') -}}
192196
}

Resources/templates/CommonAdmin/ListTemplate/ListBuilderTemplate.php.twig

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{% use '../CommonAdmin/stylesheets.php.twig' %}
33
{% use '../CommonAdmin/javascripts.php.twig' %}
44
{% use '../CommonAdmin/title.php.twig' %}
5+
{% use '../CommonAdmin/modals.php.twig' %}
56

67
{{ echo_extends( builder.getBaseAdminTemplate ) }}
78

@@ -24,3 +25,5 @@
2425
{{ echo_include(builder.namespacePrefixForTemplate ~ bundle_name ~ ':' ~ builder.BaseGeneratorName ~ 'List:results.html.twig') }}
2526
</div>
2627
{{ echo_endblock() }}
28+
29+
{{- block('modals') -}}

Resources/templates/CommonAdmin/NestedListTemplate/NestedListBuilderTemplate.php.twig

+2
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@
2121
{{ echo_include(builder.namespacePrefixForTemplate ~ bundle_name ~ ':' ~ builder.BaseGeneratorName ~ 'List:results.html.twig') }}
2222
</div>
2323
{{ echo_endblock() }}
24+
25+
{% use '../CommonAdmin/modals.php.twig' %}

Resources/templates/CommonAdmin/ShowTemplate/ShowBuilderTemplate.php.twig

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
{% use '../CommonAdmin/tabs.php.twig' %}
77
{% use '../CommonAdmin/generic_actions.php.twig' %}
88
{% use '../CommonAdmin/object_actions.php.twig' %}
9+
{% use '../CommonAdmin/modals.php.twig' %}
910

1011
{{ echo_extends( builder.getBaseAdminTemplate ) }}
1112

@@ -75,3 +76,5 @@
7576
{% endif %}
7677
</div>
7778
{{ echo_endblock() }}
79+
80+
{{- block('modals') -}}

Resources/templates/CommonAdmin/batch_actions.php.twig

+5-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@
3939
{% set translationDomain = action.type is same as('custom') ? i18n_catalog|default("Admin") : 'Admingenerator' %}
4040
<option
4141
value="{{ action.name }}"
42-
{%- if action.confirm %} data-confirm="{{ echo_trans(action.confirm, {}, translationDomain, 'html_attr') }}" {% endif -%}
42+
{%- if action.confirm and not action.forceIntermediate %}
43+
data-confirm="{{ echo_trans(action.confirm, {}, translationDomain, 'html_attr') }}"
44+
data-confirm-modal="{{ action.confirmModal|default('#confirmBatchModal') }}"
45+
data-toggle="modal"
46+
{% endif -%}
4347
>
4448
{% if action.icon %}<i class="{% if action.icon is defined and action.icon|length > 0 %}fa {{ action.icon }}{% endif %}"></i> {% endif %}
4549
{{ echo_trans(action.label, {}, translationDomain) }}

0 commit comments

Comments
 (0)