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
introduced
by
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 |