form_admin_fields.html.twig 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  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_errors -%}
  9. {% if errors|length > 0 %}
  10. {% if not form.parent %}<div class="alert alert-danger">{% endif %}
  11. <ul class="list-unstyled">
  12. {% for error in errors %}
  13. <li><i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ error.message }}</li>
  14. {% endfor %}
  15. </ul>
  16. {% if not form.parent %}</div>{% endif %}
  17. {% endif %}
  18. {%- endblock form_errors %}
  19. {% block sonata_help %}
  20. {% spaceless %}
  21. {% if sonata_help is defined and sonata_help %}
  22. <span class="help-block sonata-ba-field-widget-help">{{ sonata_help|raw }}</span>
  23. {% endif %}
  24. {% endspaceless %}
  25. {% endblock %}
  26. {% block form_widget -%}
  27. {{ parent() }}
  28. {{ block('sonata_help') }}
  29. {%- endblock form_widget %}
  30. {% block form_widget_simple %}
  31. {% set type = type|default('text') %}
  32. {% if type != 'file' %}
  33. {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
  34. {% endif %}
  35. {{ parent() }}
  36. {% endblock form_widget_simple %}
  37. {% block textarea_widget %}
  38. {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
  39. {{ parent() }}
  40. {% endblock textarea_widget %}
  41. {% block money_widget -%}
  42. {% if money_pattern == '{{ widget }}' %}
  43. {{- block('form_widget_simple') -}}
  44. {% else %}
  45. {% set currencySymbol = money_pattern|replace({'{{ widget }}': ''})|trim %}
  46. {% if money_pattern matches '/^{{ widget }}/' %}
  47. <div class="input-group">
  48. {{- block('form_widget_simple') -}}
  49. <span class="input-group-addon">{{ currencySymbol }}</span>
  50. </div>
  51. {% elseif money_pattern matches '/{{ widget }}$/' %}
  52. <div class="input-group">
  53. <span class="input-group-addon">{{ currencySymbol }}</span>
  54. {{- block('form_widget_simple') -}}
  55. </div>
  56. {% endif %}
  57. {% endif %}
  58. {%- endblock money_widget %}
  59. {% block percent_widget %}
  60. {% spaceless %}
  61. {% set type = type|default('text') %}
  62. <div class="input-group">
  63. {{ block('form_widget_simple') }}
  64. <span class="input-group-addon">%</span>
  65. </div>
  66. {% endspaceless %}
  67. {% endblock percent_widget %}
  68. {% block checkbox_widget -%}
  69. {% set parent_label_class = parent_label_class|default('') -%}
  70. {% if 'checkbox-inline' in parent_label_class %}
  71. {{- form_label(form, null, { widget: parent() }) -}}
  72. {% else -%}
  73. <div class="checkbox">
  74. {{- form_label(form, null, { widget: parent() }) -}}
  75. </div>
  76. {%- endif %}
  77. {%- endblock checkbox_widget %}
  78. {% block radio_widget -%}
  79. {%- set parent_label_class = parent_label_class|default('') -%}
  80. {% if 'radio-inline' in parent_label_class %}
  81. {{- form_label(form, null, { widget: parent() }) -}}
  82. {% else -%}
  83. <div class="radio">
  84. {{- form_label(form, null, { widget: parent() }) -}}
  85. </div>
  86. {%- endif %}
  87. {%- endblock radio_widget %}
  88. {# Labels #}
  89. {% block form_label %}
  90. {% spaceless %}
  91. {% if label is not same as(false) and sonata_admin.options['form_type'] == 'horizontal' %}
  92. {% set label_class = 'col-sm-3' %}
  93. {% endif %}
  94. {% set label_class = label_class|default('') ~ ' control-label' %}
  95. {% if label is not same as(false) %}
  96. {% set label_attr = label_attr|merge({'class': label_attr.class|default('') ~ label_class }) %}
  97. {% if not compound %}
  98. {% set label_attr = label_attr|merge({'for': id}) %}
  99. {% endif %}
  100. {% if required %}
  101. {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
  102. {% endif %}
  103. {% if label is empty %}
  104. {%- if label_format is defined and label_format is not empty -%}
  105. {% set label = label_format|replace({
  106. '%name%': name,
  107. '%id%': id,
  108. }) %}
  109. {%- else -%}
  110. {% set label = name|humanize %}
  111. {%- endif -%}
  112. {% endif %}
  113. <label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
  114. {% if translation_domain is same as(false) %}
  115. {{- label -}}
  116. {% elseif not sonata_admin.admin %}
  117. {{- label|trans({}, translation_domain) -}}
  118. {% else %}
  119. {{ label|trans({}, sonata_admin.field_description.translationDomain ?: admin.translationDomain) }}
  120. {% endif %}
  121. </label>
  122. {% endif %}
  123. {% endspaceless %}
  124. {% endblock form_label %}
  125. {% block checkbox_label -%}
  126. {{- block('checkbox_radio_label') -}}
  127. {%- endblock checkbox_label %}
  128. {% block radio_label -%}
  129. {{- block('checkbox_radio_label') -}}
  130. {%- endblock radio_label %}
  131. {% block checkbox_radio_label %}
  132. {% if sonata_admin.admin %}
  133. {% set translation_domain = sonata_admin.field_description.translationDomain %}
  134. {% endif %}
  135. {# Do not display the label if widget is not defined in order to prevent double label rendering #}
  136. {% if widget is defined %}
  137. {% if required %}
  138. {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %}
  139. {% endif %}
  140. {% if parent_label_class is defined %}
  141. {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) %}
  142. {% endif %}
  143. {% if label is not same as(false) and label is empty %}
  144. {% set label = name|humanize %}
  145. {% endif %}
  146. <label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
  147. {{- widget|raw -}}
  148. {%- if label is not same as(false) -%}
  149. <span class="control-label__text">
  150. {{- label|trans({}, translation_domain) -}}
  151. </span>
  152. {%- endif -%}
  153. </label>
  154. {% endif %}
  155. {% endblock checkbox_radio_label %}
  156. {% block choice_widget_expanded %}
  157. {% spaceless %}
  158. {% set attr = attr|merge({'class': attr.class|default('') ~ ' list-unstyled'}) %}
  159. <ul {{ block('widget_container_attributes') }}>
  160. {% for child in form %}
  161. <li>
  162. {{ form_widget(child, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  163. </li>
  164. {% endfor %}
  165. </ul>
  166. {% endspaceless %}
  167. {% endblock choice_widget_expanded %}
  168. {% block choice_widget_collapsed %}
  169. {% spaceless %}
  170. {% if required and placeholder is defined and placeholder is none %}
  171. {% set required = false %}
  172. {% elseif required and empty_value is defined and empty_value_in_choices is defined and empty_value is none and not empty_value_in_choices and not multiple %}
  173. {% set required = false %}
  174. {% endif %}
  175. {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
  176. {% if (sortable is defined) and sortable and multiple %}
  177. {{ block('sonata_type_choice_multiple_sortable') }}
  178. {% else %}
  179. <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %} >
  180. {% if empty_value is defined and empty_value is not none %}
  181. <option value=""{% if required and value is empty %} selected="selected"{% endif %}>
  182. {% if not sonata_admin.admin %}
  183. {{- empty_value|trans({}, translation_domain) -}}
  184. {% else %}
  185. {{- empty_value|trans({}, sonata_admin.field_description.translationDomain) -}}
  186. {% endif%}
  187. </option>
  188. {% elseif placeholder is defined and placeholder is not none %}
  189. <option value=""{% if required and value is empty %} selected="selected"{% endif %}>
  190. {% if not sonata_admin.admin %}
  191. {{- placeholder|trans({}, translation_domain) -}}
  192. {% else %}
  193. {{- placeholder|trans({}, sonata_admin.field_description.translationDomain) -}}
  194. {% endif%}
  195. </option>
  196. {% endif %}
  197. {% if preferred_choices|length > 0 %}
  198. {% set options = preferred_choices %}
  199. {{ block('choice_widget_options') }}
  200. {% if choices|length > 0 %}
  201. <option disabled="disabled">{{ separator }}</option>
  202. {% endif %}
  203. {% endif %}
  204. {% set options = choices %}
  205. {{ block('choice_widget_options') }}
  206. </select>
  207. {% endif %}
  208. {% endspaceless %}
  209. {% endblock choice_widget_collapsed %}
  210. {% block date_widget %}
  211. {% spaceless %}
  212. {% if widget == 'single_text' %}
  213. {{ block('form_widget_simple') }}
  214. {% else %}
  215. {% if row is not defined or row == true %}
  216. {% set attr = attr|merge({'class': attr.class|default('') ~ ' row' }) %}
  217. {% endif %}
  218. {% set input_wrapper_class = input_wrapper_class|default('col-sm-4') %}
  219. <div {{ block('widget_container_attributes') }}>
  220. {{ date_pattern|replace({
  221. '{{ year }}': '<div class="'~ input_wrapper_class ~ '">' ~ form_widget(form.year) ~ '</div>',
  222. '{{ month }}': '<div class="'~ input_wrapper_class ~ '">' ~ form_widget(form.month) ~ '</div>',
  223. '{{ day }}': '<div class="'~ input_wrapper_class ~ '">' ~ form_widget(form.day) ~ '</div>',
  224. })|raw }}
  225. </div>
  226. {% endif %}
  227. {% endspaceless %}
  228. {% endblock date_widget %}
  229. {% block time_widget %}
  230. {% spaceless %}
  231. {% if widget == 'single_text' %}
  232. {{ block('form_widget_simple') }}
  233. {% else %}
  234. {% if row is not defined or row == true %}
  235. {% set attr = attr|merge({'class': attr.class|default('') ~ ' row' }) %}
  236. {% endif %}
  237. {% set input_wrapper_class = input_wrapper_class|default('col-sm-6') %}
  238. <div {{ block('widget_container_attributes') }}>
  239. <div class="{{ input_wrapper_class }}">
  240. {{ form_widget(form.hour) }}
  241. </div>
  242. {% if with_minutes %}
  243. <div class="{{ input_wrapper_class }}">
  244. {{ form_widget(form.minute) }}
  245. </div>
  246. {% endif %}
  247. {% if with_seconds %}
  248. <div class="{{ input_wrapper_class }}">
  249. {{ form_widget(form.second) }}
  250. </div>
  251. {% endif %}
  252. </div>
  253. {% endif %}
  254. {% endspaceless %}
  255. {% endblock time_widget %}
  256. {% block datetime_widget %}
  257. {% spaceless %}
  258. {% if widget == 'single_text' %}
  259. {{ block('form_widget_simple') }}
  260. {% else %}
  261. {% set attr = attr|merge({'class': attr.class|default('') ~ ' row' }) %}
  262. <div {{ block('widget_container_attributes') }}>
  263. {{ form_errors(form.date) }}
  264. {{ form_errors(form.time) }}
  265. {% if form.date.vars.widget == 'single_text' %}
  266. <div class="col-sm-2">
  267. {{ form_widget(form.date) }}
  268. </div>
  269. {% else %}
  270. {{ form_widget(form.date, {'row': false, 'input_wrapper_class': 'col-sm-2'}) }}
  271. {% endif %}
  272. {% if form.time.vars.widget == 'single_text' %}
  273. <div class="col-sm-2">
  274. {{ form_widget(form.time) }}
  275. </div>
  276. {% else %}
  277. {{ form_widget(form.time, {'row': false, 'input_wrapper_class': 'col-sm-2'}) }}
  278. {% endif %}
  279. </div>
  280. {% endif %}
  281. {% endspaceless %}
  282. {% endblock datetime_widget %}
  283. {% block form_row %}
  284. {% set show_label = show_label|default(true) %}
  285. <div class="form-group{% if errors|length > 0 %} has-error{% endif %}" id="sonata-ba-field-container-{{ id }}">
  286. {% if sonata_admin.field_description.options is defined %}
  287. {% set label = sonata_admin.field_description.options.name|default(label) %}
  288. {% endif %}
  289. {% set div_class = 'sonata-ba-field' %}
  290. {% if label is same as(false) %}
  291. {% set div_class = div_class ~ ' sonata-collection-row-without-label' %}
  292. {% endif %}
  293. {% if sonata_admin is defined and sonata_admin.options['form_type'] == 'horizontal' %}
  294. {# Add an offset if no label or is a checkbox/radio #}
  295. {% if label is same as(false) or form.vars.checked is defined %}
  296. {% if 'collection' in form.parent.vars.block_prefixes %}
  297. {% set div_class = div_class ~ ' col-sm-12' %}
  298. {% else %}
  299. {% set div_class = div_class ~ ' col-sm-9 col-sm-offset-3' %}
  300. {% endif %}
  301. {% else %}
  302. {% set div_class = div_class ~ ' col-sm-9' %}
  303. {% endif %}
  304. {% endif %}
  305. {% if show_label %}
  306. {{ form_label(form, label|default(null)) }}
  307. {% endif %}
  308. {% if sonata_admin is defined and sonata_admin_enabled %}
  309. {% set div_class = div_class ~ ' sonata-ba-field-' ~ sonata_admin.edit ~ '-' ~ sonata_admin.inline %}
  310. {% endif %}
  311. {% if errors|length > 0 %}
  312. {% set div_class = div_class ~ ' sonata-ba-field-error' %}
  313. {% endif %}
  314. <div class="{{ div_class }}">
  315. {{ form_widget(form, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  316. {% if errors|length > 0 %}
  317. <div class="help-block sonata-ba-field-error-messages">
  318. {{ form_errors(form) }}
  319. </div>
  320. {% endif %}
  321. {% if sonata_admin is defined and sonata_admin_enabled and sonata_admin.field_description.help|default(false) %}
  322. <span class="help-block sonata-ba-field-help">{{ sonata_admin.field_description.help|trans({}, sonata_admin.field_description.translationDomain ?: admin.translationDomain)|raw }}</span>
  323. {% endif %}
  324. </div>
  325. </div>
  326. {% endblock form_row %}
  327. {% block checkbox_row -%}
  328. {% set show_label = false %}
  329. {{ block('form_row') }}
  330. {%- endblock checkbox_row %}
  331. {% block radio_row -%}
  332. {% set show_label = false %}
  333. {{ block('form_row') }}
  334. {%- endblock radio_row %}
  335. {% block sonata_type_native_collection_widget_row %}
  336. {% spaceless %}
  337. <div class="sonata-collection-row">
  338. {% if allow_delete %}
  339. <div class="row">
  340. <div class="col-xs-1">
  341. <a href="#" class="btn btn-link sonata-collection-delete">
  342. <i class="fa fa-minus-circle" aria-hidden="true"></i>
  343. </a>
  344. </div>
  345. <div class="col-xs-11">
  346. {% endif %}
  347. {{ form_row(child, { label: false }) }}
  348. {% if allow_delete %}
  349. </div>
  350. </div>
  351. {% endif %}
  352. </div>
  353. {% endspaceless %}
  354. {% endblock sonata_type_native_collection_widget_row %}
  355. {% block sonata_type_native_collection_widget %}
  356. {% spaceless %}
  357. {% if prototype is defined %}
  358. {% set child = prototype %}
  359. {% set allow_delete_backup = allow_delete %}
  360. {% set allow_delete = true %}
  361. {% set attr = attr|merge({'data-prototype': block('sonata_type_native_collection_widget_row'), 'data-prototype-name': prototype.vars.name, 'class': attr.class|default('') }) %}
  362. {% set allow_delete = allow_delete_backup %}
  363. {% endif %}
  364. <div {{ block('widget_container_attributes') }}>
  365. {{ form_errors(form) }}
  366. {% for child in form %}
  367. {{ block('sonata_type_native_collection_widget_row') }}
  368. {% endfor %}
  369. {{ form_rest(form) }}
  370. {% if allow_add %}
  371. <div><a href="#" class="btn btn-link sonata-collection-add"><i class="fa fa-plus-circle" aria-hidden="true"></i></a></div>
  372. {% endif %}
  373. </div>
  374. {% endspaceless %}
  375. {% endblock sonata_type_native_collection_widget %}
  376. {% block sonata_type_immutable_array_widget %}
  377. {% spaceless %}
  378. <div {{ block('widget_container_attributes') }}>
  379. {{ block('sonata_help') }}
  380. {{ form_errors(form) }}
  381. {% for key, child in form %}
  382. {{ block('sonata_type_immutable_array_widget_row') }}
  383. {% endfor %}
  384. {{ form_rest(form) }}
  385. </div>
  386. {% endspaceless %}
  387. {% endblock sonata_type_immutable_array_widget %}
  388. {% block sonata_type_immutable_array_widget_row %}
  389. {% spaceless %}
  390. <div class="form-group{% if child.vars.errors|length > 0%} has-error{%endif%}" id="sonata-ba-field-container-{{ id }}-{{ key }}">
  391. {{ form_label(child) }}
  392. {% set div_class = "" %}
  393. {% if sonata_admin.options['form_type'] == 'horizontal' %}
  394. {% set div_class = 'col-sm-9' %}
  395. {% endif%}
  396. <div class="{{ div_class }} sonata-ba-field sonata-ba-field-{{ sonata_admin.edit }}-{{ sonata_admin.inline }} {% if child.vars.errors|length > 0 %}sonata-ba-field-error{% endif %}">
  397. {{ form_widget(child, {'horizontal': false, 'horizontal_input_wrapper_class': ''}) }} {# {'horizontal': false, 'horizontal_input_wrapper_class': ''} needed to avoid MopaBootstrapBundle messing with the DOM #}
  398. {% set sonata_help = child.vars.sonata_help %}
  399. {{ block('sonata_help') }}
  400. </div>
  401. {% if child.vars.errors|length > 0 %}
  402. <div class="help-block sonata-ba-field-error-messages">
  403. {{ form_errors(child) }}
  404. </div>
  405. {% endif %}
  406. </div>
  407. {% endspaceless %}
  408. {% endblock %}
  409. {% block sonata_type_model_autocomplete_widget %}
  410. {% include template %}
  411. {% endblock sonata_type_model_autocomplete_widget %}
  412. {% block sonata_type_choice_field_mask_widget %}
  413. {{ block('choice_widget') }}
  414. {# Taking the form name excluding ending field glue character #}
  415. {% set main_form_name = id|slice(0, (id|length - name|length)-1) %}
  416. <script>
  417. jQuery(document).ready(function() {
  418. var allFields = {{ all_fields|json_encode|raw }};
  419. var map = {{ map|json_encode|raw }};
  420. var showMaskChoiceEl = jQuery('#{{ main_form_name }}_{{ name }}');
  421. showMaskChoiceEl.on('change', function () {
  422. choice_field_mask_show(jQuery(this).val());
  423. });
  424. function choice_field_mask_show(val) {
  425. var controlGroupIdFunc = function (field) {
  426. // Most of fields are named with an underscore
  427. var defaultFieldId = '#sonata-ba-field-container-{{ main_form_name }}_' + field;
  428. // Some fields may be named with a dash (like keys of immutable array form type)
  429. if (jQuery(defaultFieldId).length === 0) {
  430. return '#sonata-ba-field-container-{{ main_form_name }}-' + field;
  431. }
  432. return defaultFieldId;
  433. };
  434. jQuery.each(allFields, function (i, field) {
  435. jQuery(controlGroupIdFunc(field)).hide();
  436. });
  437. if (map[val]) {
  438. jQuery.each(map[val], function (i, field) {
  439. jQuery(controlGroupIdFunc(field)).show();
  440. });
  441. }
  442. }
  443. choice_field_mask_show(showMaskChoiceEl.val());
  444. });
  445. </script>
  446. {% endblock %}
  447. {% block sonata_type_choice_multiple_sortable %}
  448. <input type="hidden" name="{{ full_name }}" id="{{ id }}" value="{{ value|join(',') }}" />
  449. <script>
  450. jQuery(document).ready(function() {
  451. Admin.setup_sortable_select2(jQuery('#{{ id }}'), {{ form.vars.choices|json_encode|raw }});
  452. });
  453. </script>
  454. {% endblock %}