Skip to content

Commit

Permalink
Limited dashboard layout uploads to a user specific folder to keep fr…
Browse files Browse the repository at this point in the history
…om showing everyone's custom dashboards. Restructured the json file to include name and description for distributable dashboard layouts. Prompts for a name on export.
  • Loading branch information
alanhartless committed May 3, 2016
1 parent 625c229 commit b764e25
Show file tree
Hide file tree
Showing 10 changed files with 434 additions and 326 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@
!/media/images/avatar.png
!/media/images/favicon.ico
!/media/images/mautic_logo*
!/media/images/.htaccess
!/media/images/.htaccess
/media/dashboards/*
!/media/dashboards/*.json
13 changes: 13 additions & 0 deletions app/bundles/CoreBundle/Factory/MauticFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,19 @@ public function getSystemPath($name, $fullPath = false)
if (substr($path, -1) === '/') {
$path = substr($path, 0, -1);
}
} elseif ($name == 'dashboard') {
//these are absolute regardless as they are configurable
$path = $this->getParameter('dashboard_import_dir');
if (substr($path, -1) !== '/') {
$path .= '/';
}

$user = $this->getUser();
$path .= $user->getId();

if (!file_exists($path)) {
mkdir($path, 0755);
}
} elseif (isset($paths[$name])) {
$path = $paths[$name];
} elseif (strpos($name, '_root') !== false) {
Expand Down
16 changes: 13 additions & 3 deletions app/bundles/DashboardBundle/Assets/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ Mautic.saveWidgetSorting = function () {
var widgetsWrapper = mQuery('#dashboard-widgets');
var widgets = widgetsWrapper.children();
var ordering = [];
widgets.each(function(index, value) {
ordering.push(mQuery(this).attr('data-widget-id'));
widgets.each(function(index, value) {
ordering.push(mQuery(this).attr('data-widget-id'));
});

Mautic.ajaxActionRequest('dashboard:updateWidgetOrdering', {'ordering': ordering}, function(response) {
Expand Down Expand Up @@ -100,5 +100,15 @@ Mautic.initWidgetRemoveButtons = function (scope) {
}
});
});

};

Mautic.exportDashboardLayout = function(text, baseUrl) {
var name = prompt(text, "");

if (name) {
baseUrl = baseUrl + "?name=" + encodeURIComponent(name);
}

window.location = baseUrl;
};
135 changes: 99 additions & 36 deletions app/bundles/DashboardBundle/Controller/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
namespace Mautic\DashboardBundle\Controller;

use Mautic\CoreBundle\Controller\FormController;
use Mautic\CoreBundle\Helper\InputHelper;
use Symfony\Component\HttpFoundation\JsonResponse;
use Mautic\CoreBundle\Event\IconEvent;
use Mautic\CoreBundle\CoreEvents;
use Mautic\DashboardBundle\Entity\Widget;

/**
Expand Down Expand Up @@ -98,7 +97,7 @@ public function newAction()
$form = $model->createForm($widget, $this->get('form.factory'), $action);
$closeModal = false;
$valid = false;

///Check for a submitted form and process it
if ($this->request->getMethod() == 'POST') {
if (!$cancelled = $this->isFormCancelled($form)) {
Expand Down Expand Up @@ -277,31 +276,52 @@ public function deleteAction($objectId)
*/
public function exportAction()
{
/** @var \Mautic\DashBundle\Model\DashboardModel $model */
$model = $this->factory->getModel('dashboard');
/** @var \Mautic\DashboardBundle\Model\DashboardModel $model */
$model = $this->factory->getModel('dashboard');
$widgetsPaginator = $model->getWidgets();
$widgets = array();
$usersName = $this->factory->getUser()->getName();
$dateTime = new \DateTime;
$dateStamp = $dateTime->format('Y-m-dTH:i:s');
$name = $this->request->get(
'name',
'dashboard-of-'.str_replace(' ', '-', $usersName).'-'. $dateStamp
);

$description = $this->get('translator')->trans(
'mautic.dashboard.generated_by',
array(
'%name%' => $usersName,
'%date%' => $dateStamp
)
);

$dashboard = array(
'name' => $name,
'description' => $description,
'widgets' => array()
);

foreach ($widgetsPaginator as $widget) {
$widgets[] = array(
'name' => $widget->getName(),
'width' => $widget->getWidth(),
'height' => $widget->getHeight(),
'ordering' => $widget->getOrdering(),
'type' => $widget->getType(),
'params' => $widget->getParams(),
'template' => $widget->getTemplate(),
$dashboard['widgets'][] = array(
'name' => $widget->getName(),
'width' => $widget->getWidth(),
'height' => $widget->getHeight(),
'ordering' => $widget->getOrdering(),
'type' => $widget->getType(),
'params' => $widget->getParams(),
'template' => $widget->getTemplate(),
);
}

$name = 'dashboard-of-' . str_replace(' ', '-', $this->factory->getUser()->getName()) . '-' . (new \DateTime)->format('Y-m-dTH:i:s');
// Make the filename safe
$filename = InputHelper::alphanum($name, false, '_');

$response = new JsonResponse($widgets);
$response = new JsonResponse($dashboard);
$response->setEncodingOptions($response->getEncodingOptions() | JSON_PRETTY_PRINT);
$response->headers->set('Content-Length', strlen($response->getContent()));
$response->headers->set('Content-Type', 'application/force-download');
$response->headers->set('Content-Type', 'application/octet-stream');
$response->headers->set('Content-Disposition', 'attachment; filename="' . $name . '.json"');
$response->headers->set('Content-Disposition', 'attachment; filename="'.$filename.'.json"');
$response->headers->set('Expires', 0);
$response->headers->set('Cache-Control', 'must-revalidate');
$response->headers->set('Pragma', 'public');
Expand All @@ -317,7 +337,7 @@ public function exportAction()
public function deleteDashboardFileAction()
{
$file = $this->request->get('file');
$dir = $this->factory->getParameter('dashboard_import_dir');
$dir = $this->factory->getSystemPath('dashboard');
$path = $dir . '/' . $file;

if (file_exists($path) && is_writable($path)) {
Expand All @@ -328,24 +348,29 @@ public function deleteDashboardFileAction()
}

/**
* Exports the widgets of current user into a json file
* Applies dashboard layout
*
* @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\Response
* @param null $file
*
* @return JsonResponse|\Symfony\Component\HttpFoundation\Response
*/
public function applyDashboardFileAction($file = null)
{
if (!$file) {
$file = $this->request->get('file');
}

$dir = $this->factory->getParameter('dashboard_import_dir');
$dir = $this->factory->getSystemPath('dashboard');
$path = $dir . '/' . $file;

if (file_exists($path) && is_writable($path)) {
$widgets = json_decode(file_get_contents($path), true);
if (isset($widgets['widgets'])) {
$widgets = $widgets['widgets'];
}

if ($widgets) {
/** @var \Mautic\DashBundle\Model\DashboardModel $model */
/** @var \Mautic\DashboardBundle\Model\DashboardModel $model */
$model = $this->factory->getModel('dashboard');

$currentWidgets = $model->getWidgets();
Expand All @@ -370,28 +395,25 @@ public function applyDashboardFileAction($file = null)
}

/**
* @param int $objectId
*
* @return JsonResponse|\Symfony\Component\HttpFoundation\Response
*/
public function importAction()
{
$preview = $this->request->get('preview');

/** @var \Mautic\DashBundle\Model\DashboardModel $model */
/** @var \Mautic\DashboardBundle\Model\DashboardModel $model */
$model = $this->factory->getModel('dashboard');
$dir = $this->factory->getParameter('dashboard_import_dir');
$session = $this->factory->getSession();
$dir = $this->factory->getSystemPath('dashboard');

$action = $this->generateUrl('mautic_dashboard_action', array('objectAction' => 'import'));
$form = $this->get('form.factory')->create('dashboard_upload', array(), array('action' => $action));
$action = $this->generateUrl('mautic_dashboard_action', array('objectAction' => 'import'));
$form = $this->get('form.factory')->create('dashboard_upload', array(), array('action' => $action));

if ($this->request->getMethod() == 'POST') {
if (isset($form) && !$cancelled = $this->isFormCancelled($form)) {
if ($this->isFormValid($form)) {
$fileData = $form['file']->getData();
if (!empty($fileData)) {

// @todo check is_writable
if (!is_dir($dir) && !file_exists($dir)) {
mkdir($dir);
Expand All @@ -409,16 +431,57 @@ public function importAction()
}
}

$dashboards = array_diff(scandir($dir), array('..', '.'));
$dashboardFiles = array();
$dashboards = array();

$directories = array(
'user' => $dir,
'global' => $dir . '/../'
);

// User specific layouts
chdir($directories['user']);
$dashboardFiles['user'] = glob('*.json');

// Global dashboards
chdir($directories['global']);
$dashboardFiles['global'] = glob('*.json');

foreach ($dashboardFiles as $type => $dirDashboardFiles) {
$tempDashboard = array();
foreach ($dirDashboardFiles as $dashId => $dashboard) {
$config = json_decode(
file_get_contents($directories[$type].'/'.$dirDashboardFiles[$dashId]),
true
);

// Check for name, description, etc
$tempDashboard[$dashboard] = array(
'type' => $type,
'name' => (isset($config['name'])) ? $config['name'] : $dashboard,
'description' => (isset($config['description'])) ? $config['description'] : '',
'widgets' => (isset($config['widgets'])) ? $config['widgets'] : $config
);
}

// Sort by name
uasort($tempDashboard,
function($a, $b) {

if (!$dashboards) {
$dashboards = array();
return strnatcasecmp($a['name'], $b['name']);
}
);

$dashboards = array_merge(
$dashboards,
$tempDashboard
);
}

if ($preview && ($dashId = array_search($preview, $dashboards))) {
if ($preview && isset($dashboards[$preview])) {
// @todo check is_writable
$widgets = json_decode(file_get_contents($dir . '/' . $dashboards[$dashId]), true);
$filter = $model->getDefaultFilter();
$widgets = $dashboards[$preview]['widgets'];
$filter = $model->getDefaultFilter();
$model->populateWidgetsContent($widgets, $filter);
} else {
$widgets = array();
Expand Down
2 changes: 2 additions & 0 deletions app/bundles/DashboardBundle/Translations/en_US/messages.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mautic.dashboard.confirmation_layout_name="Enter a name for this dashboard:"
mautic.dashboard.create.past.tense="created"
mautic.dashboard.delete.past.tense="deleted"
mautic.dashboard.generated_by="Generated by %name% on %date%"
mautic.dashboard.header.index="Dashboard"
mautic.dashboard.identified.past.tense="identified"
mautic.dashboard.ipadded.past.tense="added IP"
Expand Down
11 changes: 7 additions & 4 deletions app/bundles/DashboardBundle/Views/Dashboard/import.html.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@
</div>
<div class="panel-body">
<div class="list-group">
<?php foreach ($dashboards as $dashboard) : ?>
<?php foreach ($dashboards as $dashboard => $config) : ?>
<div class="list-group-item mt-md <?php echo ($dashboard == $preview) ? 'active' : ''; ?>">
<h4 class="list-group-item-heading"><?php echo $dashboard; ?></h4>
<h4 class="list-group-item-heading"><?php echo $config['name']; ?></h4>
<?php if (!empty($config['description'])):?>
<p class="small"><?php echo $config['description']; ?></p>
<?php endif; ?>
<p class="list-group-item-text">
<a href="<?php echo $view['router']->generate('mautic_dashboard_action', array('objectAction' => 'import', 'preview' => $dashboard)); ?>">
<?php echo $view['translator']->trans('mautic.dashboard.preview'); ?>
</a>&#183;
<a href="<?php echo $view['router']->generate('mautic_dashboard_action', array('objectAction' => 'applyDashboardFile', 'file' => $dashboard)); ?>">
<?php echo $view['translator']->trans('mautic.core.form.apply'); ?>
</a>&#183;
</a><?php if ($config['type'] == 'user'): ?>&#183;
<a href="<?php echo $view['router']->generate('mautic_dashboard_action', array('objectAction' => 'deleteDashboardFile', 'file' => $dashboard)); ?>">
<?php echo $view['translator']->trans('mautic.core.form.delete'); ?>
</a>
</a><?php endif; ?>
</p>
</div>
<?php endforeach; ?>
Expand Down
3 changes: 2 additions & 1 deletion app/bundles/DashboardBundle/Views/Dashboard/index.html.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
$buttons[] = array(
'attr' => array(
'class' => 'btn btn-default btn-nospin',
'href' => $view['router']->generate('mautic_dashboard_action', array('objectAction' => 'export')),
'href' => 'javascript:void()',
'onclick' => "Mautic.exportDashboardLayout('{$view['translator']->trans('mautic.dashboard.confirmation_layout_name')}', '{$view['router']->generate('mautic_dashboard_action', array('objectAction' => 'export'))}');",
'data-toggle' => ''
),
'iconClass' => 'fa fa-cloud-download',
Expand Down
Loading

0 comments on commit b764e25

Please sign in to comment.