Issues (3627)

DashboardBundle/Controller/DashboardController.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2014 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\DashboardBundle\Controller;
13
14
use Mautic\CoreBundle\Controller\AbstractFormController;
15
use Mautic\CoreBundle\Form\Type\DateRangeType;
16
use Mautic\CoreBundle\Helper\InputHelper;
17
use Mautic\DashboardBundle\Dashboard\Widget as WidgetService;
18
use Mautic\DashboardBundle\Entity\Widget;
19
use Mautic\DashboardBundle\Form\Type\UploadType;
20
use Mautic\DashboardBundle\Model\DashboardModel;
21
use Symfony\Component\Filesystem\Exception\IOException;
22
use Symfony\Component\Form\FormError;
23
use Symfony\Component\HttpFoundation\JsonResponse;
24
use Symfony\Component\HttpFoundation\RedirectResponse;
25
use Symfony\Component\HttpFoundation\Request;
26
use Symfony\Component\HttpFoundation\Response;
27
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
28
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
29
30
class DashboardController extends AbstractFormController
31
{
32
    /**
33
     * Generates the default view.
34
     *
35
     * @return JsonResponse|Response
36
     */
37
    public function indexAction()
38
    {
39
        /** @var DashboardModel $model */
40
        $model   = $this->getModel('dashboard');
41
        $widgets = $model->getWidgets();
42
43
        // Apply the default dashboard if no widget exists
44
        if (!count($widgets) && $this->user->getId()) {
45
            return $this->applyDashboardFileAction('global.default');
46
        }
47
48
        $action          = $this->generateUrl('mautic_dashboard_index');
49
        $dateRangeFilter = $this->request->get('daterange', []);
50
51
        // Set new date range to the session
52
        if ($this->request->isMethod(Request::METHOD_POST)) {
53
            $session = $this->get('session');
54
            if (!empty($dateRangeFilter['date_from'])) {
55
                $from = new \DateTime($dateRangeFilter['date_from']);
56
                $session->set('mautic.daterange.form.from', $from->format(WidgetService::FORMAT_MYSQL));
57
            }
58
59
            if (!empty($dateRangeFilter['date_to'])) {
60
                $to = new \DateTime($dateRangeFilter['date_to']);
61
                $session->set('mautic.daterange.form.to', $to->format(WidgetService::FORMAT_MYSQL.' 23:59:59'));
62
            }
63
64
            $model->clearDashboardCache();
65
        }
66
67
        // Set new date range to the session, if present in POST
68
        $this->get('mautic.dashboard.widget')->setFilter($this->request);
69
70
        // Load date range from session
71
        $filter = $model->getDefaultFilter();
72
73
        // Set the final date range to the form
74
        $dateRangeFilter['date_from'] = $filter['dateFrom']->format(WidgetService::FORMAT_HUMAN);
75
        $dateRangeFilter['date_to']   = $filter['dateTo']->format(WidgetService::FORMAT_HUMAN);
76
        $dateRangeForm                = $this->get('form.factory')->create(DateRangeType::class, $dateRangeFilter, ['action' => $action]);
77
78
        return $this->delegateView([
79
            'viewParameters' => [
80
                'security'      => $this->get('mautic.security'),
81
                'widgets'       => $widgets,
82
                'dateRangeForm' => $dateRangeForm->createView(),
83
            ],
84
            'contentTemplate' => 'MauticDashboardBundle:Dashboard:index.html.php',
85
            'passthroughVars' => [
86
                'activeLink'    => '#mautic_dashboard_index',
87
                'mauticContent' => 'dashboard',
88
                'route'         => $this->generateUrl('mautic_dashboard_index'),
89
            ],
90
        ]);
91
    }
92
93
    /**
94
     * @return JsonResponse|Response
95
     */
96
    public function widgetAction($widgetId)
97
    {
98
        if (!$this->request->isXmlHttpRequest()) {
99
            throw new NotFoundHttpException('Not found.');
100
        }
101
102
        /** @var WidgetService $widgetService */
103
        $widgetService = $this->get('mautic.dashboard.widget');
104
        $widgetService->setFilter($this->request);
105
        $widget        = $widgetService->get((int) $widgetId);
106
107
        if (!$widget) {
0 ignored issues
show
$widget is of type Mautic\DashboardBundle\Entity\Widget, thus it always evaluated to true.
Loading history...
108
            throw new NotFoundHttpException('Not found.');
109
        }
110
111
        $response = $this->render(
112
            'MauticDashboardBundle:Dashboard:widget.html.php',
113
            ['widget' => $widget]
114
        );
115
116
        return new JsonResponse([
117
            'success'      => 1,
118
            'widgetId'     => $widgetId,
119
            'widgetHtml'   => $response->getContent(),
120
            'widgetWidth'  => $widget->getWidth(),
121
            'widgetHeight' => $widget->getHeight(),
122
        ]);
123
    }
124
125
    /**
126
     * Generate new dashboard widget and processes post data.
127
     *
128
     * @return JsonResponse|RedirectResponse|Response
129
     */
130
    public function newAction()
131
    {
132
        //retrieve the entity
133
        $widget = new Widget();
134
135
        $model  = $this->getModel('dashboard');
136
        $action = $this->generateUrl('mautic_dashboard_action', ['objectAction' => 'new']);
137
138
        //get the user form factory
139
        $form       = $model->createForm($widget, $this->get('form.factory'), $action);
140
        $closeModal = false;
141
        $valid      = false;
142
143
        ///Check for a submitted form and process it
144
        if ($this->request->isMethod(Request::METHOD_POST)) {
145
            if (!$cancelled = $this->isFormCancelled($form)) {
146
                if ($valid = $this->isFormValid($form)) {
147
                    $closeModal = true;
148
149
                    //form is valid so process the data
150
                    $model->saveEntity($widget);
151
                }
152
            } else {
153
                $closeModal = true;
154
            }
155
        }
156
157
        if ($closeModal) {
158
            //just close the modal
159
            $passthroughVars = [
160
                'closeModal'    => 1,
161
                'mauticContent' => 'widget',
162
            ];
163
164
            $filter = $model->getDefaultFilter();
165
            $model->populateWidgetContent($widget, $filter);
166
167
            if ($valid && !$cancelled) {
168
                $passthroughVars['upWidgetCount'] = 1;
169
                $passthroughVars['widgetHtml']    = $this->renderView('MauticDashboardBundle:Widget:detail.html.php', [
170
                    'widget' => $widget,
171
                ]);
172
                $passthroughVars['widgetId']     = $widget->getId();
173
                $passthroughVars['widgetWidth']  = $widget->getWidth();
174
                $passthroughVars['widgetHeight'] = $widget->getHeight();
175
            }
176
177
            return new JsonResponse($passthroughVars);
178
        } else {
179
            return $this->delegateView([
180
                'viewParameters' => [
181
                    'form' => $form->createView(),
182
                ],
183
                'contentTemplate' => 'MauticDashboardBundle:Widget:form.html.php',
184
            ]);
185
        }
186
    }
187
188
    /**
189
     * edit widget and processes post data.
190
     *
191
     * @param $objectId
192
     *
193
     * @return JsonResponse|RedirectResponse|Response
194
     */
195
    public function editAction($objectId)
196
    {
197
        $model  = $this->getModel('dashboard');
198
        $widget = $model->getEntity($objectId);
199
        $action = $this->generateUrl('mautic_dashboard_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
200
201
        //get the user form factory
202
        $form       = $model->createForm($widget, $this->get('form.factory'), $action);
203
        $closeModal = false;
204
        $valid      = false;
205
        ///Check for a submitted form and process it
206
        if ($this->request->isMethod(Request::METHOD_POST)) {
207
            if (!$cancelled = $this->isFormCancelled($form)) {
208
                if ($valid = $this->isFormValid($form)) {
209
                    $closeModal = true;
210
211
                    //form is valid so process the data
212
                    $model->saveEntity($widget);
213
                }
214
            } else {
215
                $closeModal = true;
216
            }
217
        }
218
219
        if ($closeModal) {
220
            //just close the modal
221
            $passthroughVars = [
222
                'closeModal'    => 1,
223
                'mauticContent' => 'widget',
224
            ];
225
226
            $filter = $model->getDefaultFilter();
227
            $model->populateWidgetContent($widget, $filter);
228
229
            if ($valid && !$cancelled) {
230
                $passthroughVars['upWidgetCount'] = 1;
231
                $passthroughVars['widgetHtml']    = $this->renderView('MauticDashboardBundle:Widget:detail.html.php', [
232
                    'widget' => $widget,
233
                ]);
234
                $passthroughVars['widgetId']     = $widget->getId();
235
                $passthroughVars['widgetWidth']  = $widget->getWidth();
236
                $passthroughVars['widgetHeight'] = $widget->getHeight();
237
            }
238
239
            return new JsonResponse($passthroughVars);
240
        } else {
241
            return $this->delegateView([
242
                'viewParameters' => [
243
                    'form' => $form->createView(),
244
                ],
245
                'contentTemplate' => 'MauticDashboardBundle:Widget:form.html.php',
246
            ]);
247
        }
248
    }
249
250
    /**
251
     * Deletes entity if exists.
252
     *
253
     * @param int $objectId
254
     *
255
     * @return JsonResponse|RedirectResponse
256
     */
257
    public function deleteAction($objectId)
258
    {
259
        /** @var Request $request */
260
        $request = $this->get('request_stack')->getCurrentRequest();
261
262
        if (!$request->isXmlHttpRequest()) {
263
            throw new BadRequestHttpException();
264
        }
265
266
        $flashes = [];
267
        $success = 0;
268
269
        /** @var DashboardModel $model */
270
        $model  = $this->getModel('dashboard');
271
        $entity = $model->getEntity($objectId);
272
273
        if ($entity) {
274
            $model->deleteEntity($entity);
275
            $name      = $entity->getName();
276
            $flashes[] = [
277
                'type'    => 'notice',
278
                'msg'     => 'mautic.core.notice.deleted',
279
                'msgVars' => [
280
                    '%name%' => $name,
281
                    '%id%'   => $objectId,
282
                ],
283
            ];
284
            $success = 1;
285
        } else {
286
            $flashes[] = [
287
                'type'    => 'error',
288
                'msg'     => 'mautic.api.client.error.notfound',
289
                'msgVars' => ['%id%' => $objectId],
290
            ];
291
        }
292
293
        return $this->postActionRedirect(
294
            [
295
                'success' => $success,
296
                'flashes' => $flashes,
297
            ]
298
        );
299
    }
300
301
    /**
302
     * Saves the widgets of current user into a json and stores it for later as a file.
303
     *
304
     * @return JsonResponse
305
     */
306
    public function saveAction()
307
    {
308
        // Accept only AJAX POST requests because those are check for CSRF tokens
309
        if (!$this->request->isMethod(Request::METHOD_POST) || !$this->request->isXmlHttpRequest()) {
310
            return $this->accessDenied();
311
        }
312
313
        $name = $this->getNameFromRequest();
314
        try {
315
            $this->getModel('dashboard')->saveSnapshot($name);
316
            $type = 'notice';
317
            $msg  = $this->translator->trans('mautic.dashboard.notice.save', [
318
                '%name%'    => $name,
319
                '%viewUrl%' => $this->generateUrl(
320
                    'mautic_dashboard_action',
321
                    [
322
                        'objectAction' => 'import',
323
                    ]
324
                ),
325
            ], 'flashes');
326
        } catch (IOException $e) {
327
            $type = 'error';
328
            $msg  = $this->translator->trans('mautic.dashboard.error.save', [
329
                '%msg%' => $e->getMessage(),
330
            ], 'flashes');
331
        }
332
333
        return $this->postActionRedirect(
334
            [
335
                'flashes' => [
336
                    [
337
                        'type' => $type,
338
                        'msg'  => $msg,
339
                    ],
340
                ],
341
            ]
342
        );
343
    }
344
345
    /**
346
     * Exports the widgets of current user into a json file and downloads it.
347
     *
348
     * @return JsonResponse
349
     */
350
    public function exportAction()
351
    {
352
        $filename = InputHelper::filename($this->getNameFromRequest(), 'json');
353
        $response = new JsonResponse($this->getModel('dashboard')->toArray($filename));
354
        $response->setEncodingOptions($response->getEncodingOptions() | JSON_PRETTY_PRINT);
355
        $response->headers->set('Content-Type', 'application/force-download');
356
        $response->headers->set('Content-Type', 'application/octet-stream');
357
        $response->headers->set('Content-Disposition', 'attachment; filename="'.$filename.'"');
358
        $response->headers->set('Expires', 0);
359
        $response->headers->set('Cache-Control', 'must-revalidate');
360
        $response->headers->set('Pragma', 'public');
361
362
        return $response;
363
    }
364
365
    /**
366
     * Exports the widgets of current user into a json file.
367
     *
368
     * @return JsonResponse|Response
369
     */
370
    public function deleteDashboardFileAction()
371
    {
372
        $file = $this->request->get('file');
373
374
        $parts = explode('.', $file);
375
        $type  = array_shift($parts);
376
        $name  = implode('.', $parts);
377
378
        $dir  = $this->container->get('mautic.helper.paths')->getSystemPath("dashboard.$type");
379
        $path = $dir.'/'.$name.'.json';
380
381
        if (file_exists($path) && is_writable($path)) {
382
            unlink($path);
383
        }
384
385
        return $this->redirect($this->generateUrl('mautic_dashboard_action', ['objectAction' => 'import']));
386
    }
387
388
    /**
389
     * Applies dashboard layout.
390
     *
391
     * @param null $file
392
     *
393
     * @return JsonResponse|Response
394
     */
395
    public function applyDashboardFileAction($file = null)
396
    {
397
        if (!$file) {
398
            $file = $this->request->get('file');
399
        }
400
401
        $parts = explode('.', $file);
402
        $type  = array_shift($parts);
403
        $name  = implode('.', $parts);
404
405
        $dir  = $this->container->get('mautic.helper.paths')->getSystemPath("dashboard.$type");
406
        $path = $dir.'/'.$name.'.json';
407
408
        if (!file_exists($path) || !is_readable($path)) {
409
            $this->addFlash('mautic.dashboard.upload.filenotfound', [], 'error', 'validators');
410
411
            return $this->redirect($this->generateUrl('mautic_dashboard_action', ['objectAction' => 'import']));
412
        }
413
414
        $widgets = json_decode(file_get_contents($path), true);
415
        if (isset($widgets['widgets'])) {
416
            $widgets = $widgets['widgets'];
417
        }
418
419
        if ($widgets) {
420
            /** @var DashboardModel $model */
421
            $model = $this->getModel('dashboard');
422
423
            $model->clearDashboardCache();
424
425
            $currentWidgets = $model->getWidgets();
426
427
            if (count($currentWidgets)) {
428
                foreach ($currentWidgets as $widget) {
429
                    $model->deleteEntity($widget);
430
                }
431
            }
432
433
            $filter = $model->getDefaultFilter();
434
            foreach ($widgets as $widget) {
435
                $widget = $model->populateWidgetEntity($widget, $filter);
436
                $model->saveEntity($widget);
437
            }
438
        }
439
440
        return $this->redirect($this->get('router')->generate('mautic_dashboard_index'));
441
    }
442
443
    /**
444
     * @return JsonResponse|Response
445
     */
446
    public function importAction()
447
    {
448
        $preview = $this->request->get('preview');
449
450
        /** @var DashboardModel $model */
451
        $model = $this->getModel('dashboard');
452
453
        $directories = [
454
            'user'   => $this->container->get('mautic.helper.paths')->getSystemPath('dashboard.user'),
455
            'global' => $this->container->get('mautic.helper.paths')->getSystemPath('dashboard.global'),
456
        ];
457
458
        $action = $this->generateUrl('mautic_dashboard_action', ['objectAction' => 'import']);
459
        $form   = $this->get('form.factory')->create(UploadType::class, [], ['action' => $action]);
460
461
        if ($this->request->isMethod(Request::METHOD_POST)) {
462
            if (isset($form) && !$cancelled = $this->isFormCancelled($form)) {
463
                if ($this->isFormValid($form)) {
464
                    $fileData = $form['file']->getData();
465
                    if (!empty($fileData)) {
466
                        $extension = pathinfo($fileData->getClientOriginalName(), PATHINFO_EXTENSION);
467
                        if ('json' === $extension) {
468
                            $fileData->move($directories['user'], $fileData->getClientOriginalName());
469
                        } else {
470
                            $form->addError(
471
                                new FormError(
472
                                    $this->translator->trans('mautic.core.not.allowed.file.extension', ['%extension%' => $extension], 'validators')
473
                                )
474
                            );
475
                        }
476
                    } else {
477
                        $form->addError(
478
                            new FormError(
479
                                $this->translator->trans('mautic.dashboard.upload.filenotfound', [], 'validators')
480
                            )
481
                        );
482
                    }
483
                }
484
            }
485
        }
486
487
        $dashboardFiles = ['user' => [], 'gobal' => []];
488
        $dashboards     = [];
489
490
        if (is_readable($directories['user'])) {
491
            // User specific layouts
492
            chdir($directories['user']);
493
            $dashboardFiles['user'] = glob('*.json');
494
        }
495
496
        if (is_readable($directories['global'])) {
497
            // Global dashboards
498
            chdir($directories['global']);
499
            $dashboardFiles['global'] = glob('*.json');
500
        }
501
502
        foreach ($dashboardFiles as $type => $dirDashboardFiles) {
503
            $tempDashboard = [];
504
            foreach ($dirDashboardFiles as $dashId => $dashboard) {
505
                $dashboard = str_replace('.json', '', $dashboard);
506
                $config    = json_decode(
507
                    file_get_contents($directories[$type].'/'.$dirDashboardFiles[$dashId]),
508
                    true
509
                );
510
511
                // Check for name, description, etc
512
                $tempDashboard[$dashboard] = [
513
                    'type'        => $type,
514
                    'name'        => (isset($config['name'])) ? $config['name'] : $dashboard,
515
                    'description' => (isset($config['description'])) ? $config['description'] : '',
516
                    'widgets'     => (isset($config['widgets'])) ? $config['widgets'] : $config,
517
                ];
518
            }
519
520
            // Sort by name
521
            uasort($tempDashboard,
522
                function ($a, $b) {
523
                    return strnatcasecmp($a['name'], $b['name']);
524
                }
525
            );
526
527
            $dashboards = array_merge(
528
                $dashboards,
529
                $tempDashboard
530
            );
531
        }
532
533
        if ($preview && isset($dashboards[$preview])) {
534
            // @todo check is_writable
535
            $widgets = $dashboards[$preview]['widgets'];
536
            $filter  = $model->getDefaultFilter();
537
            $model->populateWidgetsContent($widgets, $filter);
538
        } else {
539
            $widgets = [];
540
        }
541
542
        return $this->delegateView(
543
            [
544
                'viewParameters' => [
545
                    'form'       => $form->createView(),
546
                    'dashboards' => $dashboards,
547
                    'widgets'    => $widgets,
548
                    'preview'    => $preview,
549
                ],
550
                'contentTemplate' => 'MauticDashboardBundle:Dashboard:import.html.php',
551
                'passthroughVars' => [
552
                    'activeLink'    => '#mautic_dashboard_index',
553
                    'mauticContent' => 'dashboardImport',
554
                    'route'         => $this->generateUrl(
555
                        'mautic_dashboard_action',
556
                        [
557
                            'objectAction' => 'import',
558
                        ]
559
                    ),
560
                ],
561
            ]
562
        );
563
    }
564
565
    /**
566
     * Gets name from request and defaults it to the timestamp if not provided.
567
     *
568
     * @return string
569
     */
570
    private function getNameFromRequest()
571
    {
572
        return $this->request->get('name', (new \DateTime())->format('Y-m-dTH:i:s'));
573
    }
574
}
575