1: <?php
2: namespace TIC\FormBundle\Form\Type;
3:
4: #use Symfony\Component\Form\AbstractType as BaseType;
5: use TIC\FormBundle\Base\TICWidgetType as BaseType;
6:
7: use Symfony\Component\OptionsResolver\OptionsResolver;
8: use Symfony\Component\Form\FormBuilderInterface;
9: use Symfony\Component\Form\FormInterface;
10: use Symfony\Component\Form\FormView;
11:
12: use Symfony\Component\Form\CallbackTransformer;
13:
14: /**
15: * Saisie d'un texte sur plusieurs lignes.
16: * Mise en forme HTML avec éditeur WYSIWYG optionnelle (intégration du composant JS summernote)
17: */
18: class EditorType extends BaseType
19: {
20: # protected $form_parent = Symfony\Component\Form\Extension\Core\Type\TextareaType::class;
21:
22:
23: /**
24: * {@inheritdoc}
25: */
26: public function configureOptions(OptionsResolver $resolver): void
27: {
28: parent::configureOptions($resolver);
29:
30: $resolver->setDefaults(array(
31: 'compound' => false,
32: 'wysiwyg' => false,
33: 'rows' => 2,
34: 'height' => null,
35: 'focus' => false,
36: 'allowLinks' => true,
37: 'allowTable' => true,
38: 'allowImage' => true,
39: 'allowVideo' => true,
40: 'toolbars' => true, // history,style,font,fontstyle,fontstyles,fontmore,stylefull,fontname,fontsize,color,fontfull,para,parafull,height,table,insert,media,image,video,link,view,fullscreen,source,help
41: 'language' => \Locale::getPrimaryLanguage(\Locale::getDefault()),
42: ));
43:
44: $resolver->setAllowedTypes('wysiwyg', array('bool'));
45: $resolver->setAllowedTypes('rows', array('int'));
46: $resolver->setAllowedTypes('height', array('int', 'null'));
47: $resolver->setAllowedTypes('focus', array('bool'));
48: $resolver->setAllowedTypes('allowLinks', array('bool'));
49: $resolver->setAllowedTypes('allowTable', array('bool'));
50: $resolver->setAllowedTypes('allowImage', array('bool'));
51: $resolver->setAllowedTypes('allowVideo', array('bool'));
52: $resolver->setAllowedTypes('toolbars', array('array', 'string', 'bool', 'null'));
53: $resolver->setAllowedTypes('language', array('string', 'null'));
54: }
55:
56:
57: /**
58: * {@inheritdoc}
59: */
60: public function buildForm(FormBuilderInterface $builder, array $options): void
61: {
62: $builder->addModelTransformer(new CallbackTransformer(
63: function ($populatedText) {
64: return $populatedText;
65: },
66: function ($submittedText) {
67: // vérifie la présence d'un contenu réel dans le code HTML
68: $content = strip_tags(str_replace('&nbsp;', '', $submittedText), '<img>');
69: // renvoie NULL si aucun contenu (ie que des balises telles que '<p><br></p>')
70: return strlen(trim($content)) ? $submittedText : NULL;
71: }
72: ));
73: }
74:
75:
76: /**
77: * {@inheritdoc}
78: */
79: public function buildView(FormView $view, FormInterface $form, array $options): void
80: {
81: parent::buildView($view, $form, $options);
82:
83: // paramètres pour textarea html5
84: $view->vars['wysiwyg'] = false;
85: if ($options['focus']) $view->vars['attr']['autofocus'] = true;
86: if ($options['height']) $view->vars['attr']['rows'] = max(2, round($options['height'] / 22));
87: elseif ($options['rows']) $view->vars['attr']['rows'] = max(2, intval($options['rows']));
88:
89: // paramètres pour summernote.js
90: if ($options['wysiwyg'] && ! $options['disabled']) {
91: $view->vars['wysiwyg'] = true;
92: $view->vars['focus'] = $options['focus'];
93: $view->vars['lang'] = $this->getLang($options['language']);
94: $view->vars['toolbars'] = $this->makeToolbars($options);
95: if ($options['height']) $view->vars['height'] = max(50, intval($options['height']));
96: elseif ($options['rows']) $view->vars['height'] = max(50, intval($options['rows'])*22);
97: }
98: }
99:
100:
101: /**
102: * Retourne la locale disponible avec summernote.js en fonction de la langue Symfony.
103: */
104: private function getLang($language): ?string
105: {
106: if (! is_string($language)) return null;
107: list($code) = explode('_', strtolower($language) . '_');
108: switch ($code) {
109: case 'en' : return null;
110: case 'fr' : return 'fr-FR';
111: case 'de' : return 'de-DE';
112: case 'es' : return 'es-ES';
113: case 'it' : return 'it-IT';
114: case 'pt' : return 'pt-PT';
115: case 'ru' : return 'ru-RU';
116: case 'ja' : return 'ja-JP';
117: case 'zh' : return 'zh-CN';
118: case 'ar' : return 'ar-AR';
119: case 'bg' : return 'bg-BG';
120: case 'ca' : return 'ca-ES';
121: case 'cs' : return 'cs-CZ';
122: case 'da' : return 'da-DK';
123: case 'eu' : return 'es-EU';
124: case 'fa' : return 'fa-IR';
125: case 'fu' : return 'fi-FI';
126: case 'gl' : return 'gl-ES';
127: case 'he' : return 'he-IL';
128: case 'hr' : return 'hr-HR';
129: case 'hu' : return 'hu-HU';
130: case 'id' : return 'id-ID';
131: case 'ko' : return 'ko-KR';
132: case 'lt' : return 'lt-LT';
133: case 'lv' : return 'lt-LV';
134: case 'nb' : return 'nb-NO';
135: case 'no' : return 'nb-NO';
136: case 'nl' : return 'nl-NL';
137: case 'pl' : return 'pl-PL';
138: # case 'pt' : return 'pt-BR';
139: case 'ro' : return 'ro-RO';
140: case 'sk' : return 'sk-SK';
141: case 'sl' : return 'sl-SI';
142: case 'sr' : return 'sr-RS';
143: case 'sv' : return 'sv-SE';
144: case 'th' : return 'th-TH';
145: case 'tr' : return 'tr-TR';
146: case 'ok' : return 'uk-UA';
147: case 'vi' : return 'vi-VN';
148: # case 'zh' : return 'zh-TW';
149: }
150: }
151:
152:
153: /**
154: * Contruction des éléments de la barre d'outils (d'après des blocs d'éléments préconfigurés).
155: */
156: private function makeToolbars($options)
157: {
158: $bars = isset($options['toolbars']) ? $options['toolbars'] : null;
159:
160: if ($bars === null) return;
161: if ($bars === false) return array();
162: # if ($bars === true) $bars = array('style','font','fontname','color','para','table','insert','view');
163: if ($bars === true) $bars = array('history','fontfull','stylefull','parafull','table','insert','view');
164: elseif (is_string($bars)) $bars = explode(',', $bars);
165: elseif (! is_array($bars)) return;
166:
167: $presets = array(
168: 'history' => array('undo', 'redo'),
169:
170: 'style' => array('style'), // default 1/8
171: 'font' => array('bold', 'underline', 'clear'), // default 2/8
172: 'fontstyle' => array('bold', 'italic', 'underline', 'clear'),
173: 'fontstyles' => array('bold', 'italic', 'underline', 'strikethrough', 'clear'),
174: 'fontmore' => array('strikethrough', 'superscript', 'subscript'),
175: 'stylefull' => array('style', 'bold', 'italic', 'underline', 'strikethrough', 'clear'),
176:
177: 'fontname' => array('fontname'), // default 3/8
178: 'fontsize' => array('fontsize'),
179: 'color' => array('color'), // default 4/8
180: 'fontfull' => array('fontname', 'fontsize', 'color'),
181:
182: 'para' => array('ul', 'ol', 'paragraph'), // default 5/8
183: 'parafull' => array('paragraph', 'height', 'ol', 'ul', 'listStyles', 'hr'),
184: 'paragraph' => array('paragraph'),
185: 'height' => array('height'),
186: 'list' => array('ol', 'ul', 'listStyles'),
187:
188: 'table' => array('table'), // default 6/8
189:
190: 'insert' => array('link', 'picture', 'video'), // default 7/8
191: 'media' => array('picture', 'video'),
192: 'image' => array('picture'),
193: 'video' => array('video'),
194: 'link' => array('link'),
195:
196: 'view' => array('fullscreen', 'codeview', 'help'), // default 8/8
197: 'fullscreen' => array('fullscreen'),
198: 'source' => array('codeview'),
199: 'help' => array('help'),
200: );
201:
202: $excludes = array();
203: if (! $options['allowLinks']) $excludes[] = 'link';
204: if (! $options['allowTable']) $excludes[] = 'table';
205: if (! $options['allowImage']) $excludes[] = 'picture';
206: if (! $options['allowVideo']) $excludes[] = 'video';
207:
208: $toolbars = array();
209: foreach ($bars as $bar) {
210: $key = strtolower(trim($bar));
211: if (! isset($presets[$key])) continue;
212: $tools = array_values(array_diff($presets[$key], $excludes));
213: if (count($tools)) $toolbars[] = array($key, $tools);
214: }
215: return $toolbars;
216: }
217:
218:
219: }
220: