Messenger.php 12.6 KB
Newer Older
1
2
3
<?php
namespace UnicaenApp\View\Helper;

4
use Exception;
5
6
use UnicaenApp\Traits\MessageAwareInterface;
use UnicaenApp\Traits\MessageAwareTrait;
7
use UnicaenApp\Exception\LogicException;
8
9
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerAwareTrait;
10
use Zend\View\Helper\AbstractHelper;
11
use Zend\View\Helper\FlashMessenger;
12
use Zend\View\Helper\HtmlTag;
13
use Zend\View\Renderer\PhpRenderer;
14
15

/**
16
17
18
 * Aide de vue permettant de stocker une liste de messages d'information de différentes sévérités
 * et de générer le code HTML pour les afficher (affublés d'un icône correspondant à leur sévérité).
 *
19
 * Possibilité d'importer les messages du FlashMessenger pour les mettre en forme de la même manière
20
 * (avec support du namespace, ex: 'these/info' ou 'these/*').
21
22
23
 *
 * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr>
 */
24
class Messenger extends AbstractHelper implements MessageAwareInterface, EventManagerAwareInterface
25
{
26
27
28
    const EVENT_RENDER = 'unicaen-app-messenger-render';

    use EventManagerAwareTrait;
29
    use MessageAwareTrait;
30

31
32
33
34
35
36
37
38
    const NAMESPACED_SEVERITY_SEPARATOR = '/';

    protected $uiClasses = [
        // severity   => [ alert class, icon class ]
        self::INFO    => ['info', 'info-sign'],
        self::SUCCESS => ['success', 'ok-sign'],
        self::WARNING => ['warning', 'warning-sign'],
        self::ERROR   => ['danger', 'exclamation-sign'],
39
40
    ];

41
42
43
44
45
46
    protected $severityOrder = [
        // severity   => order
        self::SUCCESS => 1,
        self::ERROR   => 2,
        self::WARNING => 3,
        self::INFO    => 4,
47
48
    ];

49
50
    /**
     * Activation ou non de l'affichage de l'icône
51
     *
52
53
54
     * @var bool
     */
    protected $withIcon = true;
55

56
57
58
59
    /**
     * @var string
     */
    protected $containerInnerTemplate = '%s';
60

61
62
63
64
65
    /**
     * @var array
     */
    protected $containerClassesToAdd = [];

66
67
68
69
    /**
     * @var \Zend\Mvc\Controller\Plugin\FlashMessenger
     */
    protected $pluginFlashMessenger;
70

71
    /**
72
     * Helper entry point.
73
     *
74
     * @return static
75
     */
76
    public function __invoke()
77
78
79
    {
        return $this;
    }
80

81
82
    /**
     * Retourne le code HTML généré par cette aide de vue.
83
     *
84
85
86
87
     * @return string
     */
    public function __toString()
    {
88
89
        try {
            return $this->render();
90
        } catch (Exception $exc) {
91
92
            var_dump($exc->getMessage(), \UnicaenApp\Util::formatTraceString($exc->getTraceAsString()));
            die;
93
        }
94
    }
95
96

    /**
97
     * Génère le code HTML.
98
     *
99
100
     * @return string
     */
101
    protected function render()
102
103
104
105
106
107
    {
        if (!$this->hasMessages()) {
            return '';
        }

        $out = '';
108

109
110
        foreach ($this->getSortedMessages() as $severity => $array) {
            foreach ($array as $priority => $message) {
111
112
113
114
                if ($em = $this->getEventManager()){
                    $em->trigger(self::EVENT_RENDER, null, compact('severity','message'));
                }

115
                $out .= sprintf(
116
117
                    $this->getTemplate(is_string($severity) ? $severity : (is_string($priority) ? $priority : 'info')),
                    implode('<br />', (array)$message)
118
                );;
119
            }
120
121
122
123
124
        }

        return $out;
    }

125
126
    /**
     * Génère le code HTML d'un seul message. Pour usage ponctuel.
127
128
     *
     * @param string $message  Message à afficher
129
     * @param string $severity Ex: MessageAwareInterface::INFO
130
     *
131
132
133
134
     * @return string
     */
    public function renderMessage($message, $severity = null)
    {
135
        if (!$message) {
136
137
138
139
140
141
            return '';
        }

        return sprintf($this->getTemplate($severity ?: 'info'), $message);
    }

142
143
144
    /**
     * @return array
     */
145
    protected function getSortedMessages()
146
    {
147
        $messages = (array)$this->getMessages();
148
        $order    = $this->severityOrder;
149
150

        uksort($messages, function ($s1, $s2) use ($order) {
151
152
153
154
155
156
            if ($order[$s1] < $order[$s2]) {
                return -1;
            }
            if ($order[$s1] > $order[$s2]) {
                return 1;
            }
157

158
159
            return 0;
        });
160

161
162
        return $messages;
    }
163

164
    /**
165
166
     * Importe les messages n-1 du FlashMessenger.
     *
167
     * @param string $namespace
168
     * @return static
169
     */
170
    protected function importFlashMessages($namespace = null)
171
    {
172
        return $this->_importFromFlashMessages($currentMessages = false, $namespace);
173
    }
174
175
176
177

    /**
     * Importe les messages courants du FlashMessenger.
     *
178
     * @param string $namespace
179
     * @return static
180
     */
181
    protected function importCurrentFlashMessages($namespace = null)
182
    {
183
184
185
186
187
188
189
190
191
192
193
194
        return $this->_importFromFlashMessages($currentMessages = true, $namespace);
    }

    /**
     * @param bool   $currentMessages
     * @param string $namespace Facultatif, ex: 'info', 'these/danger', 'these/*', 'these' (i.e. 'these/*')
     * @return static
     */
    private function _importFromFlashMessages($currentMessages, $namespace = null)
    {
        $getMethod = $currentMessages ? 'getCurrentMessagesFromNamespace' : 'getMessagesFromNamespace';

195
196
197
        $fm = $this->getPluginFlashMessenger();

        if ($namespace) {
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
            $severities = [self::ERROR, self::SUCCESS, self::INFO, self::WARNING];
            if (in_array($namespace, $severities)) {
                foreach ($fm->$getMethod($namespace) as $message) {
                    $this->addMessage($message, $namespace);
                }
                if ($currentMessages) {
                    /* Si on importe alors on nettoie pour éviter un deuxième affichage */
                    $fm->clearCurrentMessagesFromNamespace($namespace);
                }
            } else {
                $array = $this->extractSeveritiesAndNamespaces($namespace);
                foreach ($array as $sev => $ns) {
                    foreach ($fm->$getMethod($ns) as $message) {
                        $this->addMessage($message, $sev);
                    }
                    if ($currentMessages) {
                        /* Si on importe alors on nettoie pour éviter un deuxième affichage */
                        $fm->clearCurrentMessagesFromNamespace($ns);
                    }
217
218
                }
            }
219
        }
220
        else {
221
            foreach ($fm->$getMethod('error') as $message) {
222
223
                $this->addMessage($message, self::ERROR);
            }
224
            foreach ($fm->$getMethod('success') as $message) {
225
226
                $this->addMessage($message, self::SUCCESS);
            }
227
            foreach ($fm->$getMethod('info') as $message) {
228
229
                $this->addMessage($message, self::INFO);
            }
230
            foreach ($fm->$getMethod('warning') as $message) {
231
232
233
                $this->addMessage($message, self::WARNING);
            }

234
235
236
            if ($currentMessages) {
                $fm->clearCurrentMessagesFromContainer();
            }
237
238
239
240
241
        }

        return $this;
    }

242
    /**
243
244
     * @param string $namespace Ex: 'these/danger' ou 'these/*'
     * @return array 'severity' => 'namespace'
245
     */
246
    private function extractSeveritiesAndNamespaces($namespace)
247
    {
248
249
250
251
252
        // Normalisation : $namespace doit être de la forme 'namespace/severity' ou 'namespace/*'.
        // Ex: 'these/danger' ou 'these/*'
        $separatorFound = strrpos($namespace, $sep = self::NAMESPACED_SEVERITY_SEPARATOR) !== false;
        if (! $separatorFound) {
            $namespace = $namespace . '/*';
253
        }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

        $parts = explode($sep, $namespace);
        $severity = array_pop($parts);

        // Si $namespace est de la forme 'namespace/*', cela revient à importer les namespaces
        // 'namespace/danger', 'namespace/success', 'namespace/info' et 'namespace/warning'.
        if ($severity === '*') {
            $namespacePrefix = implode(self::NAMESPACED_SEVERITY_SEPARATOR, $parts) . '/';
            $array = [
                $s = self::ERROR   => $namespacePrefix . $s,
                $s = self::SUCCESS => $namespacePrefix . $s,
                $s = self::INFO    => $namespacePrefix . $s,
                $s = self::WARNING => $namespacePrefix . $s,
            ];
        } else {
269
            $array = [
270
                $severity => $namespace,
271
            ];
272
        }
273

274
        return $array;
275
    }
276

277
278
    /**
     * Spécifie l'unique message courant au format Exception.
279
     *
280
281
     * @param Exception $exception Exception
     * @param string    $severity  Ex: Messenger::INFO
282
     *
283
     * @return static
284
     */
285
    public function setException(Exception $exception, $severity = self::ERROR)
286
287
288
289
290
    {
        $message = sprintf("<p><strong>%s</strong></p>", $exception->getMessage());
        if (($previous = $exception->getPrevious())) {
            $message .= sprintf("<p>Cause :<br />%s</p>", $previous->getMessage());
        }
291
292

        return $this->setMessages([$severity => $message]);
293
    }
294

295
296
    /**
     * Importe les messages courants du FlashMessenger (remplaçant les messages existants).
297
     *
298
299
     * @param string $namespace
     * @return static
300
     */
301
    public function setMessagesFromFlashMessenger($namespace = null)
302
    {
303
        $this->messages = [];
304
        $this->importFlashMessages($namespace);
305
306
307
308
309
310
311

        return $this;
    }

    /**
     * Importe les messages courants du FlashMessenger (remplaçant les messages existants).
     *
312
313
     * @param string $namespace
     * @return static
314
     */
315
    public function setCurrentMessagesFromFlashMessenger($namespace = null)
316
317
    {
        $this->messages = [];
318
        $this->importCurrentFlashMessages($namespace);
319

320
321
        return $this;
    }
322

323
    /**
324
     * Ajoute les messages courants du FlashMessenger.
325
     *
326
327
     * @param string $namespace
     * @return static
328
     */
329
    public function addMessagesFromFlashMessenger($namespace = null)
330
    {
331
        $this->importFlashMessages($namespace);
332

333
334
        return $this;
    }
335
336
337
338

    /**
     * Ajoute les messages courants du FlashMessenger.
     *
339
340
     * @param string $namespace
     * @return static
341
     */
342
    public function addCurrentMessagesFromFlashMessenger($namespace = null)
343
    {
344
        $this->importCurrentFlashMessages($namespace);
345
346
347
348

        return $this;
    }

349
350
    /**
     * Active ou non l'affichage de l'icône.
351
     *
352
     * @param bool $withIcon <tt>true</tt> pour activer l'affichage de l'icône.
353
     *
354
     * @return static
355
356
357
     */
    public function withIcon($withIcon = true)
    {
358
359
        $this->withIcon = (bool)$withIcon;

360
361
        return $this;
    }
362
363

    /**
364
     * Retourne le motif utilisé pour générer le conteneur de chaque message à afficher.
365
     *
366
     * @param string $severity    Ex: Messenger::INFO
367
     * @param string $containerId Dom id éventuel à utiliser pour la div englobante
368
369
     *
     * @return string
370
     */
371
    public function getTemplate($severity, $containerId = null)
372
    {
373
        $severity = array_key_exists($severity, $this->uiClasses) ? $severity : self::INFO;
374

375
376
377
        $alertClass = $this->uiClasses[$severity][0];
        $iconClass  = $this->uiClasses[$severity][1];

378
        $innerTemplate = $this->containerInnerTemplate ?: '%s';
379
        $iconMarkup    = $this->withIcon ? "<span class=\"glyphicon glyphicon-$iconClass\"></span>" : null;
380
        $containerId   = $containerId ? sprintf('id="%s"', $containerId) : '';
381

382
        $classesToAdd = $this->containerClassesToAdd ? implode(' ', $this->containerClassesToAdd) : '';
383

384
        $template = <<<EOT
385
<div class="messenger alert alert-$alertClass $classesToAdd" $containerId>
386
    <button type="button" class="close" title="Fermer cette alerte" data-dismiss="alert">&times;</button>
387
    $iconMarkup
388
    $innerTemplate
389
</div>
390
EOT;
391

392
393
        return $template . PHP_EOL;
    }
394

395
396
397
398
399
400
401
402
403
404
405
    /**
     * @param array|string $containerClassesToAdd Ex: ['alert-md']
     * @return static
     */
    public function setContainerClassesToAdd($containerClassesToAdd)
    {
        $this->containerClassesToAdd = (array) $containerClassesToAdd;

        return $this;
    }

406
    /**
407
408
409
     *
     * @param string $containerInnerTemplate
     *
410
     * @return static
411
412
413
414
     */
    public function setContainerInnerTemplate($containerInnerTemplate = '%s')
    {
        $this->containerInnerTemplate = $containerInnerTemplate;
415

416
417
        return $this;
    }
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434

    /**
     * @return \Zend\Mvc\Controller\Plugin\FlashMessenger
     */
    protected function getPluginFlashMessenger()
    {
        if (null === $this->pluginFlashMessenger) {
            /** @var PhpRenderer $view */
            $view = $this->getView();
            /* @var $vh FlashMessenger */
            $vh = $view->getHelperPluginManager()->get('flashMessenger');

            $this->pluginFlashMessenger = $vh->getPluginFlashMessenger();
        }

        return $this->pluginFlashMessenger;
    }
435
}