form_admin_fields.html.twig 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. {#
  2. This file is part of the Sonata package.
  3. (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  4. For the full copyright and license information, please view the LICENSE
  5. file that was distributed with this source code.
  6. #}
  7. {% extends 'form_div_layout.html.twig' %}
  8. {% block form_widget_simple %}
  9. {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
  10. {{ parent() }}
  11. {% endblock form_widget_simple %}
  12. {% block textarea_widget %}
  13. {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
  14. {{ parent() }}
  15. {% endblock textarea_widget %}
  16. {# Labels #}
  17. {% block form_label %}
  18. {% spaceless %}
  19. {% set label_class = "" %}
  20. {% if sonata_admin.admin and sonata_admin.admin.getConfigurationPool().getOption('form_type') == 'horizontal' %}
  21. {% set label_class = " control-label col-sm-3" %}
  22. {% else %}
  23. {% set label_class = " control-label" %}
  24. {% endif%}
  25. {#{{ sonata_admin.admin.getConfigurationPool().getOption('form_type') }}#}
  26. {% if label is not sameas(false) %}
  27. {% set label_attr = label_attr|merge({'class': label_attr.class|default('') ~ label_class }) %}
  28. {% if not compound %}
  29. {% set label_attr = label_attr|merge({'for': id}) %}
  30. {% endif %}
  31. {% if required %}
  32. {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
  33. {% endif %}
  34. {% if label is empty %}
  35. {% set label = name|humanize %}
  36. {% endif %}
  37. {% if in_list_checkbox is defined and in_list_checkbox and widget is defined %}
  38. <label{% for attrname,attrvalue in attr %} {{attrname}}="{{attrvalue}}"{% endfor %}>
  39. {{ widget|raw }}
  40. <span>
  41. {% if not sonata_admin.admin %}
  42. {{- label|trans({}, translation_domain) -}}
  43. {% else %}
  44. {{- label|trans({}, sonata_admin.field_description.translationDomain) -}}
  45. {% endif%}
  46. </span>
  47. </label>
  48. {% else %}
  49. <label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
  50. {% if not sonata_admin.admin%}
  51. {{- label|trans({}, translation_domain) -}}
  52. {% else %}
  53. {{ sonata_admin.admin.trans(label, {}, sonata_admin.field_description.translationDomain) }}
  54. {% endif %}
  55. </label>
  56. {% endif %}
  57. {% endif %}
  58. {% endspaceless %}
  59. {% endblock form_label %}
  60. {% block widget_container_attributes_choice_widget %}
  61. {% spaceless %}
  62. id="{{ id }}"
  63. {% for attrname,attrvalue in attr %}{{attrname}}="{% if attrname == 'class' %}list-unstyled {% endif%}{{attrvalue}}" {% endfor %}
  64. {% if "class" not in attr %}class="list-unstyled"{%endif %}
  65. {% endspaceless %}
  66. {% endblock %}
  67. {% block choice_widget_expanded %}
  68. {% spaceless %}
  69. <ul {{ block('widget_container_attributes') }}>
  70. {% for child in form %}
  71. <li>
  72. {{ form_widget(child, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  73. {{ form_label(child) }}
  74. </li>
  75. {% endfor %}
  76. </ul>
  77. {% endspaceless %}
  78. {% endblock choice_widget_expanded %}
  79. {% block choice_widget %}
  80. {% spaceless %}
  81. {% if compound %}
  82. <ul {{ block('widget_container_attributes_choice_widget') }}>
  83. {% for child in form %}
  84. <li>
  85. {% set form_widget_content %}
  86. {{ form_widget(child, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  87. {% endset %}
  88. {{ form_label(child, child.vars.label|default(null), { 'in_list_checkbox' : true, 'widget' : form_widget_content } ) }}
  89. </li>
  90. {% endfor %}
  91. </ul>
  92. {% else %}
  93. {% if sonata_admin.admin and not sonata_admin.admin.getConfigurationPool().getOption('use_select2') %}
  94. {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
  95. {% endif %}
  96. <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
  97. {% if empty_value is not none %}
  98. <option value="">
  99. {% if not sonata_admin.admin %}
  100. {{- empty_value|trans({}, translation_domain) -}}
  101. {% else %}
  102. {{- empty_value|trans({}, sonata_admin.field_description.translationDomain) -}}
  103. {% endif%}
  104. </option>
  105. {% endif %}
  106. {% if preferred_choices|length > 0 %}
  107. {% set options = preferred_choices %}
  108. {{ block('choice_widget_options') }}
  109. {% if choices|length > 0 %}
  110. <option disabled="disabled">{{ separator }}</option>
  111. {% endif %}
  112. {% endif %}
  113. {% set options = choices %}
  114. {{ block('choice_widget_options') }}
  115. </select>
  116. {% endif %}
  117. {% endspaceless %}
  118. {% endblock choice_widget %}
  119. {% block form_row %}
  120. {% set label_class = "" %}
  121. {% set div_class = "" %}
  122. {% if sonata_admin.admin and sonata_admin.admin.getConfigurationPool().getOption('form_type') == 'horizontal' %}
  123. {% set label_class = "control-label col-sm-3" %}
  124. {% set div_class = "col-sm-9 col-md-9" %}
  125. {% else %}
  126. {% set label_class = "control-label" %}
  127. {% endif%}
  128. {% if sonata_admin is not defined or not sonata_admin_enabled or not sonata_admin.field_description %}
  129. <div class="form-group {% if errors|length > 0%} has-error{% endif %}">
  130. {{ form_label(form, label|default(null)) }}
  131. <div class="{% if label is sameas(false) %}sonata-collection-row-without-label{% endif %}">
  132. {{ form_widget(form, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  133. {% if errors|length > 0 %}
  134. <div class="help-block sonata-ba-field-error-messages">
  135. {{ form_errors(form) }}
  136. </div>
  137. {% endif %}
  138. </div>
  139. </div>
  140. {% else %}
  141. <div class="form-group{% if errors|length > 0%} has-error{%endif%}" id="sonata-ba-field-container-{{ id }}">
  142. {% block label %}
  143. {% if sonata_admin.field_description.options.name is defined %}
  144. {{ form_label(form, sonata_admin.field_description.options.name, { 'attr' : {'class' : label_class} }) }}
  145. {% else %}
  146. {{ form_label(form, label|default(null), { 'attr' : {'class' : label_class} }) }}
  147. {% endif %}
  148. {% endblock %}
  149. {% set has_label = sonata_admin.field_description.options.name is defined or label is not sameas(false) %}
  150. <div class="{{ div_class }} sonata-ba-field sonata-ba-field-{{ sonata_admin.edit }}-{{ sonata_admin.inline }} {% if errors|length > 0 %}sonata-ba-field-error{% endif %} {% if not has_label %}sonata-collection-row-without-label{% endif %}">
  151. {{ form_widget(form, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  152. {% if errors|length > 0 %}
  153. <div class="help-block sonata-ba-field-error-messages">
  154. {{ form_errors(form) }}
  155. </div>
  156. {% endif %}
  157. {% if sonata_admin.field_description.help %}
  158. <span class="help-block sonata-ba-field-help">{{ sonata_admin.admin.trans(sonata_admin.field_description.help, {}, sonata_admin.field_description.translationDomain)|raw }}</span>
  159. {% endif %}
  160. </div>
  161. </div>
  162. {% endif %}
  163. {% endblock form_row %}
  164. {% block sonata_type_native_collection_widget_row %}
  165. {% spaceless %}
  166. <div class="sonata-collection-row">
  167. {% if allow_delete %}
  168. <a href="#" class="btn sonata-collection-delete"><i class="fa fa-minus-circle"></i></a>
  169. {% endif %}
  170. {{ form_row(child) }}
  171. </div>
  172. {% endspaceless %}
  173. {% endblock sonata_type_native_collection_widget_row %}
  174. {% block sonata_type_native_collection_widget %}
  175. {% spaceless %}
  176. {% if prototype is defined %}
  177. {% set child = prototype %}
  178. {% set attr = attr|merge({'data-prototype': block('sonata_type_native_collection_widget_row'), 'data-prototype-name': prototype.vars.name, 'class': attr.class|default('') }) %}
  179. {% endif %}
  180. <div {{ block('widget_container_attributes') }}>
  181. {{ form_errors(form) }}
  182. {% for child in form %}
  183. {{ block('sonata_type_native_collection_widget_row') }}
  184. {% endfor %}
  185. {{ form_rest(form) }}
  186. {% if allow_add %}
  187. <div><a href="#" class="btn sonata-collection-add"><i class="fa fa-plus-circle"></i></a></div>
  188. {% endif %}
  189. </div>
  190. {% endspaceless %}
  191. {% endblock sonata_type_native_collection_widget %}
  192. {% block sonata_type_immutable_array_widget %}
  193. {% spaceless %}
  194. <div {{ block('widget_container_attributes') }}>
  195. {{ form_errors(form) }}
  196. {% for key, child in form %}
  197. {{ block('sonata_type_immutable_array_widget_row') }}
  198. {% endfor %}
  199. {{ form_rest(form) }}
  200. </div>
  201. {% endspaceless %}
  202. {% endblock sonata_type_immutable_array_widget %}
  203. {% block sonata_type_immutable_array_widget_row %}
  204. {% spaceless %}
  205. <div class="form-group{% if errors|length > 0%} error{%endif%}" id="sonata-ba-field-container-{{ id }}-{{ key }}">
  206. {{ form_label(child) }}
  207. {% set div_class = "" %}
  208. {% if sonata_admin.admin and sonata_admin.admin.getConfigurationPool().getOption('form_type') == 'horizontal' %}
  209. {% set div_class = "col-sm-9 col-md-9" %}
  210. {% endif%}
  211. <div class="{{ div_class }} sonata-ba-field sonata-ba-field-{{ sonata_admin.edit }}-{{ sonata_admin.inline }} {% if errors|length > 0 %}sonata-ba-field-error{% endif %}">
  212. {{ form_widget(child, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  213. </div>
  214. {% if errors|length > 0 %}
  215. <div class="help-block sonata-ba-field-error-messages">
  216. {{ form_errors(child) }}
  217. </div>
  218. {% endif %}
  219. </div>
  220. {% endspaceless %}
  221. {% endblock %}
  222. {% block sonata_type_model_autocomplete_widget %}
  223. {% spaceless %}
  224. {{ form_widget(form.title) }}
  225. {% for child in form %}
  226. {% if not child.rendered %}
  227. {{ form_widget(child) }}
  228. {% endif %}
  229. {% endfor %}
  230. <script>
  231. (function ($) {
  232. var autocompleteInput = $("#{{ form.title.vars.id }}");
  233. autocompleteInput.select2({
  234. placeholder: "{{ placeholder }}",
  235. allowClear: {{ required ? 'false' : 'true' }},
  236. enable: {{ disabled ? 'false' : 'true' }},
  237. readonly: {{ read_only ? 'true' : 'false' }},
  238. minimumInputLength: {{ minimum_input_length }},
  239. multiple: {{ multiple ? 'true' : 'false' }},
  240. ajax: {
  241. url: "{{ url ?: url(route.name, route.parameters|default([])) }}",
  242. dataType: 'json',
  243. quietMillis: 100,
  244. data: function (term, page) { // page is the one-based page number tracked by Select2
  245. return {
  246. //search term
  247. "{{ req_param_name_search }}": term,
  248. // page size
  249. "{{ req_param_name_items_per_page }}": {{ items_per_page }},
  250. // page number
  251. "{{ req_param_name_page_number }}": page,
  252. // admin
  253. 'uniqid': "{{ sonata_admin.admin.root.uniqid }}",
  254. 'code': "{{ sonata_admin.admin.root.code }}",
  255. 'field': "{{ name }}"
  256. // other parameters
  257. {% if req_params is not empty %},
  258. {%- for key, value in req_params -%}
  259. "{{- key|e('js') -}}": "{{- value|e('js') -}}"
  260. {%- if not loop.last -%}, {% endif -%}
  261. {%- endfor -%}
  262. {% endif %}
  263. };
  264. },
  265. results: function (data, page) {
  266. // notice we return the value of more so Select2 knows if more results can be loaded
  267. return {results: data.items, more: data.more};
  268. }
  269. },
  270. initSelection: function(element, callback) {
  271. //remove initial text from input
  272. $(element).val("");
  273. var data = [];
  274. {% if multiple -%}
  275. data = [
  276. {%- for key, label_text in value.labels -%}
  277. {id:{{ value.identifiers[key]|e('js') }}, label:'{{ label_text|e('js') }}'}
  278. {%- if not loop.last -%}, {% endif -%}
  279. {%- endfor -%}
  280. ];
  281. {%- elseif value.labels[0] is defined -%}
  282. data = {id: {{ value.identifiers[0]|e('js') }}, label:'{{ value.labels[0]|e('js') }}'};
  283. {%- endif %}
  284. callback(data);
  285. },
  286. formatResult: function (item) {
  287. return {% block sonata_type_model_autocomplete_dropdown_item_format %}'<div class="sonata-autocomplete-dropdown-item">'+item.label+'</div>'{% endblock %};// format of one dropdown item
  288. },
  289. formatSelection: function (item) {
  290. return {% block sonata_type_model_autocomplete_selection_format %}item.label{% endblock %};// format selected item '<b>'+item.label+'</b>';
  291. },
  292. dropdownCssClass: "{{ dropdown_css_class }}",
  293. escapeMarkup: function (m) { return m; } // we do not want to escape markup since we are displaying html in results
  294. });
  295. autocompleteInput.on("change", function(e) {
  296. // console.log("change "+JSON.stringify({val:e.val, added:e.added, removed:e.removed}));
  297. // add new input
  298. var el = null;
  299. if (undefined !== e.added) {
  300. var addedItems = e.added;
  301. if(!$.isArray(addedItems)) {
  302. addedItems = [addedItems];
  303. }
  304. var length = addedItems.length;
  305. for (var i = 0; i < length; i++) {
  306. el = addedItems[i];
  307. $("#{{ form.identifiers.vars.id }}").append('<input type="hidden" name="{{ form.identifiers.vars.full_name }}[]" value="'+el.id+'" />');
  308. }
  309. }
  310. // remove input
  311. if (undefined !== e.removed) {
  312. var removedItems = e.removed;
  313. if(!$.isArray(removedItems)) {
  314. removedItems = [removedItems];
  315. }
  316. var length = removedItems.length;
  317. for (var i = 0; i < length; i++) {
  318. el = removedItems[i];
  319. $('#{{ form.identifiers.vars.id }} input:hidden[value="'+el.id+'"]').remove();
  320. }
  321. }
  322. });
  323. })(jQuery);
  324. </script>
  325. {% endspaceless %}
  326. {% endblock sonata_type_model_autocomplete_widget %}