浏览代码

Dashboard v2

idleman 2 年之前
父节点
当前提交
f9d67bd0ee

+ 21 - 0
dashboard/Dashboard.class.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * Define a dashboard.
+ * @author Administrateur
+ * @category Plugin
+ * @license copyright
+ */
+class Dashboard extends Entity{
+	public $id,$user,$label,$icon,$default,$mandatory;
+	protected $TABLE_NAME = 'dashboard_dashboard';
+	public $fields =
+	array(
+		'id' => 'key',
+		'user' => 'string',
+		'label' => 'string',
+		'icon' => 'string',
+		'mandatory' => 'boolean',
+		'default' => 'boolean'
+	);
+}
+?>

+ 93 - 0
dashboard/DashboardWidget.class.php

@@ -0,0 +1,93 @@
+<?php
+/**
+ * Define a dashboardwidget.
+ * @author Administrateur
+ * @category Plugin
+ * @license copyright
+ */
+class DashboardWidget extends Entity{
+	public $id,$minified,$position,$model,$dashboard,$title,$icon,$background,$width,$load,$configure,$configure_callback,$configure_init,$move,$delete,$options,$js,$css,$content,$data,$description,$defaultWidth;
+	protected $TABLE_NAME = 'dashboard_dashboard_widget';
+	public $fields =
+	array(
+		'id' => 'key',
+		'model' => 'string',
+		'data' => 'longstring',
+		'position' => 'int',
+		'minified' => 'boolean',
+		'width' => 'int',
+		'dashboard' => 'int'
+	);
+
+	function __construct(){
+ 		parent::__construct();
+ 		$this->options = array();
+ 		$this->icon = 'fa-caret-right';
+ 		$this->title = 'Sans titre';
+ 		$this->defaultWidth = 4;
+ 		$this->width = 0;
+ 	}
+
+ 	public function toArray($decoded= false){
+		$fields = parent::toArray($decoded);
+		$fields['options'] = $this->options;
+		$fields['icon'] =  $this->icon;
+		$fields['background'] =  $this->background;
+		$fields['load'] =  $this->load;
+		$fields['configure'] =  $this->configure;
+		$fields['configure_callback'] =  $this->configure_callback;
+		$fields['configure_init'] =  $this->configure_init;
+		$fields['js'] =  $this->js;
+		$fields['css'] =  $this->css;
+		$fields['description'] =  $this->description;
+		$fields['content'] =  $this->content;
+		$fields['title'] =  $this->title;
+		return $fields;
+ 	}
+
+ 	function data($key=null,$value=null){
+ 		$data = json_decode($this->data,true);
+ 		if($key==null) return $data;
+ 		if(is_array($key) && $value==null){
+ 			foreach ($key as $k => $v) {
+ 				$data[$k] = $v;
+ 				$this->data = json_encode($data);
+ 			}
+ 			return true;
+ 		}
+ 	
+ 		if($value===null) return isset($data[$key])?$data[$key]:'';
+ 		
+ 		$data[$key] = $value;
+
+ 		$this->data = json_encode($data);
+ 		return true;
+ 	}
+
+ 	public static function current(){
+ 		global $_;
+ 		$dbWidget = self::getById($_['id']);
+
+ 		$widget = DashboardWidget::models($dbWidget->model);
+ 		$widget->id = $dbWidget->id;
+ 		$widget->position =  $dbWidget->position;
+ 		$widget->minified =  $dbWidget->minified;
+ 		$widget->width = $dbWidget->width;
+ 		$widget->dashboard = $dbWidget->dashboard;
+ 		$widget->data = $dbWidget->data;
+
+ 		return $widget;
+ 	}
+
+ 	public static function models($modelUid = null){
+ 		Plugin::callHook('widget',array(&$models));
+
+		foreach($models as $model)
+			$models[$model->model] = $model;
+
+		if(!isset($modelUid)) return $models;
+		
+		return isset($models[$modelUid]) ? $models[$modelUid] : array();
+ 	}
+}
+?>

+ 0 - 0
plugin/dashboard/DashboardWidgetShare.class.php → dashboard/DashboardWidgetShare.class.php


+ 381 - 0
dashboard/action.php

@@ -0,0 +1,381 @@
+<?php
+
+
+	/** DASHBOARD **/
+	//Récuperation d'une liste de dashboard
+	Action::register('dashboard_dashboard_search',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+
+		$filters = array();
+		if(!$myUser->can('dashboard','configure')) $filters['creator'] = $myUser->login;
+
+		foreach(Dashboard::loadAll($filters,array('label','creator')) as $dashboard){
+			$userName = User::byLogin($dashboard->user)->fullName();
+			$dashboard->user = !empty($userName) ? $userName : $dashboard->user;
+			$dashboard->mandatory = $dashboard->mandatory == 1;
+			$dashboard->default = $dashboard->default == 1;
+			$response['rows'][] = $dashboard;
+		}
+	});
+
+	//Ajout ou modification d'élément dashboard
+	Action::register('dashboard_dashboard_save',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		if($_['mandatory']==1 && !$myUser->can('dashboard','configure'))  throw new Exception("Vous n'avez pas les droits pour rendre ce dashboard obigatoire",403);
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+
+		$item = Dashboard::provide();
+		if(!isset($_['user']) || empty($_['user'])) $_['user'] = $myUser->login;
+
+		if($myUser->login!=$item->creator && !$myUser->can('dashboard','configure') && $item->id!=0)  throw new Exception("Vous n'avez pas les droits pour éditer le dashboard d'un autre utilisateur",403);
+		if($myUser->login!=$_['user'] && !$myUser->can('dashboard','configure')) throw new Exception("Vous n'avez pas les droits pour créer un dashboard à un autre utilisateur");
+
+		$item->user = $_['user'];
+		$item->label = $_['label'];
+		$item->icon = $_['icon'];
+		$item->default = $_['default'];
+		if($item->default) Dashboard::change(array('default'=>0), array('user'=>$item->user));
+		$item->mandatory = $_['mandatory'];
+		if($item->mandatory) Dashboard::change(array('mandatory'=>0));
+		$item->save();
+	});
+
+	//Récuperation ou edition d'élément dashboard
+	Action::register('dashboard_dashboard_edit',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		$response = Dashboard::getById($_['id']);
+	});
+
+	//Suppression d'élement dashboard
+	Action::register('dashboard_dashboard_delete',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','delete');
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		$item = Dashboard::provide();
+		if($myUser->login!=$item->creator && !$myUser->can('dashboard','configure'))  throw new Exception("Vous n'avez pas les droits pour supprimer le dashboard d'un autre utilisateur",403);
+		Dashboard::deleteById($_['id']);
+	});
+
+	//Sauvegarde des configurations de dashboard
+	Action::register('dashboard_setting_save',function(&$response){
+		global $myUser,$_,$conf;
+		User::check_access('dashboard','configure');
+		foreach(Configuration::setting('dashboard') as $key=>$value){
+			if(!is_array($value)) continue;
+			$allowed[] = $key;
+		}
+		foreach ($_['fields'] as $key => $value)
+			if(in_array($key, $allowed)) $conf->put($key,$value);
+	});
+
+	/** DASHBOARDWIDGET **/
+	//Récuperation d'une liste de dashboardwidget
+	Action::register('dashboard_dashboardwidget_search',function(&$response){
+	
+		global $myUser,$myFirm,$_;
+		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
+
+		$models = DashboardWidget::models();
+		$ranksId = $myUser->getRanksId($myFirm->id);
+		$widgetsQry = "SELECT {{table}}.*,ds.mandatory,ds.sort as sort
+			FROM {{table}}
+			LEFT JOIN ".DashboardWidgetShare::tableName()." ds ON ds.widget={{table}}.id
+			WHERE dashboard=?
+				OR {{table}}.id IN (
+					SELECT widget FROM ".DashboardWidgetShare::tableName()." WHERE (entity=? AND uid=?) OR (entity=? and uid IN (".str_repeat('?,', count($ranksId) - 1) . '?'.")) OR (entity IS NULL)
+				)
+				ORDER BY sort, position DESC";
+		$widgets = DashboardWidget::staticQuery($widgetsQry,array_merge(array($_['dashboard'],'user',$myUser->login,'rank'),$ranksId),true);
+
+		foreach($widgets as $widget){
+			if(!isset($models[$widget->model])) continue;
+			$model = clone $models[$widget->model];
+
+			$row = $model->toArray();
+			$row['id'] = $widget->id;
+			$row['width'] = !empty($widget->width) && $widget->width>0 ? $widget->width : $model->defaultWidth;
+			$row['position'] = $widget->position;
+			$row['minified'] = $widget->minified;
+			$row['dashboard'] = $widget->dashboard;
+
+			if(!empty($widget->foreign('mandatory'))) $row['mandatory'] = $widget->foreign('mandatory');
+			if(!empty($widget->foreign('sort'))) $row['position'] = $widget->foreign('sort');
+
+			$response['rows'][] = $row;
+		}
+
+		if(isset($response['rows'])){
+			usort($response['rows'],function($a,$b){
+				return $a['position'] - $b['position'];
+			});
+		}
+	});
+
+	//Ajout ou modification d'élément dashboardwidget
+	Action::register('dashboard_dashboardwidget_save',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$item = DashboardWidget::provide();
+		$item->model = $_['model'];
+		$item->data = $_['data'];
+		$item->position = $_['position'];
+		$item->minified = $_['minified'];
+		$item->dashboard = $_['dashboard'];
+		$item->save();
+	});
+
+	//Récuperation ou edition d'élément dashboardwidget
+	Action::register('dashboard_dashboardwidget_edit',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$response = DashboardWidget::getById($_['id']);
+	});
+
+	//Suppression d'élement dashboardwidget
+	Action::register('dashboard_dashboardwidget_delete',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','delete');
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$widget = DashboardWidget::getById($_['widget']);
+		if(!$widget) return;
+
+		$dashboard = Dashboard::getById($_['dashboard']);
+		if($widget->dashboard!=$dashboard->id || $dashboard->user!=$myUser->login)
+			throw new Exception("Vous ne pouvez supprimer que vos propres widgets");
+
+		$widget->deleteById($widget->id);
+		$response['message'] = 'Widget supprimé';
+	});
+
+	//Resize largeur d'élement dashboardwidget
+	Action::register('dashboard_dashboardwidget_resize',function(&$response){
+		global $myUser,$_;
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		User::check_access('dashboard','edit');
+
+		$widget = DashboardWidget::getById($_['widget']);
+		$dashboard = Dashboard::getById($widget->dashboard);
+		if($widget->dashboard!=$dashboard->id || $dashboard->user!=$myUser->login)
+			throw new Exception("Vous ne pouvez redimenssioner que vos propres widgets");
+
+		$widget->width = $_['width'];
+		$widget->save();
+	});
+
+	Action::register('dashboard_dashboardwidget_refresh',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','read');
+		$widgets = array();
+		Plugin::callHook('widget_refresh',array(&$widgets));
+		$response['rows'] = $widgets;
+	});
+
+	Action::register('dashboard_dashboardwidget_add',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$widget = new DashboardWidget();
+		$widget->model = $_['widget'];
+		$widget->position = 666;
+		$widget->minified = false;
+		$widget->width = $widget->width> 0 ? $widget->width: $widget->defaultWidth ;
+		$widget->dashboard = $_['dashboard'];
+		$widget->save();
+		$response['message'] = 'Widget ajouté';
+	});
+
+	Action::register('dashboard_dashboardwidget_save_position',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$dashboard = Dashboard::getById($_['dashboard']);
+
+		if($dashboard->user!=$myUser->login) throw new Exception("Vous ne pouvez modifier que vos propres widgets");
+		$dashboard_widgets = DashboardWidget::loadAll( array('dashboard' => $dashboard->id ) );
+
+		foreach($_['positions'] as $move){
+			foreach($dashboard_widgets as $dashboard_widget){
+				if($dashboard_widget->id!=$move['id']) continue;
+				$dashboard_widget->position = $move['position'];
+				$dashboard_widget->save();
+			}
+		}
+	});
+
+	/* CLOCK */
+	Action::register('dashboard_widget_clock_load',function(&$response){
+		global $myUser;
+		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$widget = DashboardWidget::current();
+		ob_start();
+		require_once(__DIR__.SLASH.'widget.clock.php');
+		$widget->content = ob_get_clean();
+		echo json_encode($widget);
+		exit;
+	});
+
+	/* LOGS */
+	Action::register('dashboard_widget_log_load',function(&$response){
+		global $myUser;
+		require_once('DashboardWidget.class.php');
+		User::check_access('log','read');
+
+		$widget = DashboardWidget::current();
+		$widget->title = '30 derniers logs';
+
+		ob_start();
+		require_once(__DIR__.SLASH.'widget.logs.php');
+		$widget->content = ob_get_clean();
+		echo json_encode($widget);
+		exit;
+	});
+
+	/* PROFILE */
+	Action::register('dashboard_widget_profile_load',function(&$response){
+		global $myUser;
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		User::check_access('dashboard','read');
+		$widget = DashboardWidget::current();
+		if(!empty($widget->data('background-color'))) $widget->background = $widget->data('background-color');
+		ob_start();
+		require_once(__DIR__.SLASH.'widget.profile.php');
+		$widget->content = ob_get_clean();
+		echo json_encode($widget);
+		exit;
+	});
+
+	Action::register('dashboard_widget_profile_configure',function(&$response){
+		global $myUser;
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		User::check_access('dashboard','read');
+		$widget = DashboardWidget::current();
+		ob_start();
+		require_once(__DIR__.SLASH.'widget.profile.configure.php');
+		$content = ob_get_clean();
+
+		echo $content ;
+		exit;
+	});
+
+	Action::register('dashboard_widget_profile_configure_save',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$widget = DashboardWidget::getById($_['id']);
+		$widget->data('background-color',$_['widget-profile-background-color']);
+		$widget->save();
+	});
+
+
+	/* HTML */
+	Action::register('dashboard_widget_html_load',function(&$response){
+		global $myUser;
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		User::check_access('dashboard','read');
+		$widget = DashboardWidget::current();
+		$widget->title = $widget->data('title') != "" ? $widget->data('title') :  'Bloc HTML';
+		if($widget->data('color') != "") $widget->background = $widget->data('color');
+		ob_start();
+		require_once(__DIR__.SLASH.'widget.html.php');
+		$widget->content = ob_get_clean();
+		echo json_encode($widget);
+		exit;
+	});
+
+	Action::register( 'dashboard_widget_html_configure',function(&$response){
+		global $myUser;
+		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$widget = DashboardWidget::current();
+		ob_start();
+		require_once(__DIR__.SLASH.'widget.html.configure.php');
+		$content = ob_get_clean();
+		echo $content ;
+		exit;
+	});
+
+	Action::register('dashboard_widget_html_configure_save',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$widget = DashboardWidget::getById($_['id']);
+		$widget->data('html',html_entity_decode($_['widget-html-content']));
+		$widget->data('title',$_['widget-html-title']);
+		$widget->data('color',$_['widget-html-color']);
+		$widget->save();
+	});
+
+	/* DASHBOARD SHARE */
+	/** DASHBOARDWIDGETSHARE **/
+	//Récuperation d'une liste de dashboardwidgetshare
+	Action::register('dashboard_widget_share_search',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+
+		$dashboardwidgetshares = DashboardWidgetShare::loadAll(array(), null,  null,array('*'), 1);
+		foreach($dashboardwidgetshares as $dashboardwidgetshare){
+			$row = $dashboardwidgetshare->toArray();
+
+			$row['widget'] = $dashboardwidgetshare->join('widget')->toArray();
+			$row['for'] = 'Tout le monde';
+			if($row['entity'] == 'rank' ) $row['for'] = 'Rang: '. Rank::getById($row['uid'] )->label;
+			if($row['entity'] == 'user' ) $row['for'] ='Utilisateur: '. User::byLogin($row['uid'] )->fullName();
+			$response['rows'][] = $row;
+		}
+	});
+
+	//Ajout ou modification d'élément dashboardwidgetshare
+	Action::register('dashboard_widget_share_save',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
+		//DashboardWidgetShare::create();
+		$item = DashboardWidgetShare::provide();
+
+		if(!isset( $_['widget']) || !is_numeric($_['widget'])) throw new Exception("Widget non spécifié ou invalide");
+
+		$item->widget = $_['widget'];
+		$item->mandatory = 1;//$_['mandatory'];
+
+		if(isset($_['entity'])){
+			$item->entity = $_['entity'];
+			$item->uid = $_['uid'];
+		}
+		$item->sort = !isset($_['sort']) ? 0 : $_['sort'];
+		$item->save();
+	});
+
+
+	//Récuperation ou edition d'élément dashboardwidgetshare
+	Action::register('dashboard_widget_share_edit',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','edit');
+		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+		$response = DashboardWidgetShare::getById($_['id'],1)->toArray();
+	});
+
+	//Suppression d'élement dashboardwidgetshare
+	Action::register('dashboard_widget_share_delete',function(&$response){
+		global $myUser,$_;
+		User::check_access('dashboard','delete');
+		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
+		DashboardWidgetShare::deleteById($_['id']);
+	});
+
+?>

+ 13 - 0
dashboard/app.json

@@ -0,0 +1,13 @@
+{
+	"id": "fr.core.dashboard",
+	"name": "Dashboard",
+	"author" : {
+		"name" : "Valentin CARRUESCO",
+		"mail" : ""
+	},
+	"version": "2.0",
+	"url": "http://no-url.com",
+	"licence": {"name": "MIT","url" : ""},
+	"description": "Dashboard avec widgets dynamiques",
+	"require" : {}
+}

+ 315 - 0
dashboard/css/main.css

@@ -0,0 +1,315 @@
+/** DASHBOARD **/
+.module-index body{
+    background-color: #f5f5f5;
+}
+/* Conteneur principal du plugin dashboard */
+.dashboard {
+	
+}
+.dashboard-widget-log,.dashboard-widget-log li{
+	list-style-type: none;
+	margin: 0;
+	padding: 5px;
+	border-bottom: 1px solid #f1f1f1;
+}
+.dashboard-widget-log{
+	padding:0 15px 15px 15px;
+}
+.dashboard-widget-log li {
+	font-size: 11px;
+}
+.dashboard-widget-log li >h2{
+	font-size: 14px;
+	margin-top: 15px;
+	padding: 3px 0 3px 0;
+	border-bottom: 2px solid #cecece;
+}
+
+/** DASHBOARD **/
+.widget[data-mandatory] {
+    border-left: 0px;
+}
+
+.widget .readonly-veil{
+	width:100%;
+	height:100%;
+	position:absolute;
+	top:0;
+	left:0;
+	opacity: 0.5;
+	background-color: #ffffff;
+	border-radius: 10px;
+	z-index: 100;
+}
+
+.widget[data-mandatory="1"] .widget_resize,.widget[data-mandatory="1"] .widget_header{
+	cursor: default;
+}
+.widget[data-mandatory="1"] .widget_options{
+	display: none;
+}
+.dashboard-container.readonly .dashboard-widget-menu, .dashboard-container.readonly .widget_options,.dashboard-container.readonly .widget_resize{
+	display: none;
+}
+.dashboard-container.readonly .widget_header{
+	cursor:default;
+}
+#dashboardForm #icon{
+	font-family:"Font Awesome 5 Free";
+}
+#widget-list{
+	width: 100%;
+    display: block;
+    overflow-y: auto;
+    padding: 5px;
+}
+#dashboard{
+	list-style-type:none;
+	margin:0;
+	padding:0;
+}
+#dashboard > li{
+	float:left;
+}
+#dashboard-view{
+	width: 50px;
+    background-color: #343a40;
+    list-style-type: none;
+    color: #505050;
+    margin: 0;
+    padding: 0;
+    min-height: 100%;
+    position: fixed;
+    left: 0;
+    top: 50px;
+    border-top: 4px solid #454a4e;
+}
+#dashboard-view li{
+	cursor:pointer;
+	text-align: center;
+    font-size: 1.3em;
+	transition:background 0.2s ease-in-out;
+}
+#dashboard-view li.dashboard-view-title{
+	color: #cecece;
+    font-size: 11px;
+    cursor: default;
+    background-color: #475058;
+    font-weight: bold;
+}
+#dashboard-view li.dashboard-item{
+	width: 50px;
+	position: relative;
+	padding: 10px;
+	transition: padding-bottom 0.2s ease-in-out;
+}
+#dashboard-view li.dashboard-item > div{
+	padding-top: 0.15rem;
+	display: flex;
+	width: 30px;
+	height: 30px;
+	z-index: 10;
+	background-color: #fafafa;
+	border-radius: 100%;
+	text-align: center;
+	border: 2px solid #fafafa;
+	transition: all 0.2s ease-in-out;
+}
+#dashboard-view li.dashboard-item i {
+	margin: auto;
+    font-size: 0.7em;
+    transition: all 0.2s ease-in-out;
+}
+#dashboard-view li.dashboard-item:hover{
+	padding-bottom: 20px;
+}
+#dashboard-view li.dashboard-item span {
+	transform: translateY(-25px);
+	font-size: 9px;
+	position: absolute;
+	left: 0;
+	bottom: 5px;
+	color: #808a94;
+	text-transform: uppercase;
+	display: block;
+	width: 100%;
+	text-align: center;
+	font-weight: bold;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	opacity: 0;
+	z-index: 1;
+	transition: all 0.2s ease-in-out;
+}
+#dashboard-view li.dashboard-item:hover span {
+	transform: translateX(0px);
+	opacity: 1;
+}
+#dashboard-view li:hover{
+	background-color: #475058;
+	transition:
+		background-color 0.2s ease-in-out,
+		padding-bottom 0.2s ease-in-out;
+}
+#dashboard-view li[data-selected] > div{
+	background-color: #1d1d1d;
+    border-color: #1d1d1d;
+    border-top: 2px solid #dc3545;
+    color: #fafafa;
+}
+.widget_placeholder{
+	display: block;
+	width: auto;
+	background: rgba(0,0,0,0.1);
+	height: 335px;
+	margin-top: 20px;
+	border-radius: 10px;
+    border: 2px dashed #cecece;
+    transition: all 0.2s linear 0s;
+}
+.widget{
+	transition: width 0.2s linear,height 0.2s linear,opacity 0.2s linear;
+	/*display: table;*/
+	padding:20px;
+	transition: transform 0.2s ease-in-out,max-width 0.2s ease-in-out;
+}
+.widget.ui-sortable-helper{
+	transform: scale(1.05);
+}
+.widget_window{
+	border-radius: 10px;
+    box-shadow: 0 0 20px 0 #cecece!important;
+	padding: 0;
+	text-align: left;
+	transition: opacity 0.2s linear 0s;
+	z-index: 100;
+	position: relative;
+}
+.widget_resize{
+	position: absolute;
+	height: 100%;
+	cursor: ew-resize;
+	width:5px;
+	top:0px;
+	right:0px;
+	z-index: 100;
+}
+.widget_header{
+	background-color: #50597b;
+	padding: 5px;
+	border-radius: 10px 10px 0 0;
+	box-sizing: border-box;
+	color: #ffffff;
+	cursor: move;
+	transition:background 0.2s linear;
+}
+.widget_header > i {
+	display: inline-block;
+}
+.widget_options{
+	float: right;
+	margin: -5px 0 0 10px;
+	padding: 0;
+}
+.widget_options li{
+	cursor: pointer;
+	display: inline-block;
+	opacity: 0.5;
+	padding: 5px;
+	transition: opacity 0.2s linear 0s;
+	vertical-align: top;
+}
+.widget_options li:hover{
+	opacity: 1;
+}
+.widget_content{
+	background-color: #ffffff;
+	border-top:0;
+	overflow:auto;
+	border-radius: 0 0 10px 10px;
+	width:100%;
+	height:300px;
+	padding: 0px;
+}
+.widget_content::-webkit-scrollbar {
+	width: 6px;
+	height: 6px;
+}
+.widget_content::-webkit-scrollbar-button {
+	width: 0px;
+	height: 0px;
+}
+.widget_content::-webkit-scrollbar-thumb {
+	background: #cecece;
+	border: 0px none #ffffff;
+	border-radius: 50px;
+}
+.widget_content::-webkit-scrollbar-thumb:hover {
+	background: #707070;
+}
+.widget_content::-webkit-scrollbar-thumb:active {
+	background: #949494;
+}
+.widget_content::-webkit-scrollbar-track {
+	background: transparent;
+	border: 0px none #ffffff;
+	border-radius: 50px;
+}
+.widget_content::-webkit-scrollbar-track:hover {
+	background: #d4d4d4;
+}
+.widget_content::-webkit-scrollbar-track:active {
+	background: #d3d3d3;
+}
+.widget_content::-webkit-scrollbar-corner {
+	background: transparent;
+}
+.dashboard-container{
+	padding: 10px 0 0 0;
+}
+.dashboard-widget-menu{
+	margin: 0 0 0 15px;
+	padding: 0;
+}
+.dashboard-widget-menu li{
+	list-style-type: none;
+	color:#cecece;
+	display: inline-block;
+	cursor: pointer;
+	padding:5px;
+}
+.dashboard-widget-menu li {
+	opacity:0.5;
+	display: inline-block;
+	transition : all 0.2s ease-in-out;
+}
+.dashboard-widget-menu li:hover {
+	opacity:1;
+}
+.widgetDescription{
+	
+}
+.widgetColor{
+	display: inline-block;
+	vertical-align: top;
+	padding-right: 5px;
+	color:#333333;
+}
+.widgetColor small{
+	width:15px;
+	height:15px;
+	border-radius: 100%;
+	display: inline-block;
+	margin-right: 5px;
+	margin-top: 4px;
+	vertical-align: top;
+}
+@media only screen and (max-width: 500px) {
+	.widget_dropper{
+		display: none;
+	}
+	.widget,.widget_window{
+		display: block;
+		width:100%;
+	}
+}

+ 0 - 0
plugin/dashboard/css/widget.css → dashboard/css/widget.css


+ 134 - 0
dashboard/dashboard.plugin.php

@@ -0,0 +1,134 @@
+<?php
+//Cette fonction va generer une page quand on clique sur dashboard dans menu
+function dashboard_page(){
+	global $_,$myUser,$conf;
+	if(isset($_['module']) || !$myUser->connected()) return;
+	$page = !isset($_['page']) ? 'home' : $_['page'];
+	$file = __DIR__.SLASH.'page.'.$page.'.php';
+	if(!file_exists($file)) throw new Exception("Page ".$page." inexistante");
+	require_once($file);
+}
+
+//Fonction executée lors de l'activation du plugin
+function dashboard_install($id){
+	if($id != 'fr.core.dashboard') return;
+	global $conf;
+	Entity::install(__DIR__);
+	$conf->put('dashboard_enable_sidebar',1);
+}
+
+//Fonction executée lors de la désactivation du plugin
+function dashboard_uninstall($id){
+	if($id != 'fr.core.dashboard') return;
+	Entity::uninstall(__DIR__);
+}
+
+//Déclaration des sections de droits du plugin
+//Déclaration des sections de droits du plugin
+Right::register('dashboard',array('label'=>'Gestion des droits sur le plugin dashboard'));
+
+
+//Comprends toutes les actions du plugin qui ne nécessitent pas de vue html
+require_once(__DIR__.SLASH.'action.php');
+
+
+//Déclaration du menu de réglages
+function dashboard_menu_setting(&$settingMenu){
+	global  $myUser;
+	
+	if(!$myUser->can('dashboard','configure')) return;
+	$settingMenu[] = array(
+		'sort' =>1,
+		'url' => 'setting.php?section=dashboard',
+		'icon' => 'fas fa-angle-right',
+		'label' => 'Dashboard'
+	);
+}
+
+//Déclaration des pages de réglages
+function dashboard_content_setting(){
+	global $_;
+	$_['section'] = str_replace('..', '', $_['section']);
+
+	if(file_exists(__DIR__.SLASH.'setting.'.$_['section'].'.php'))
+		require_once(__DIR__.SLASH.'setting.'.$_['section'].'.php');
+}
+
+
+
+function dashboard_default_widget(&$widgets){
+	global $myUser;
+	require_once(__DIR__.SLASH.'DashboardWidget.class.php');
+
+	$modelWidget = new DashboardWidget();
+	$modelWidget->model = 'clock';
+	$modelWidget->title = 'Horloge';
+	$modelWidget->icon = 'far fa-clock';
+	$modelWidget->background = '#212529';
+	$modelWidget->load = 'action.php?action=dashboard_widget_clock_load';
+	$modelWidget->js = [Plugin::url().'/js/progressbar.min.js?v=2',Plugin::url().'/js/widget.js?v=2'];
+	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
+	$modelWidget->description = "Affiche l'heure en temps réel";
+	$widgets[] = $modelWidget;
+
+	$modelWidget = new DashboardWidget();
+	$modelWidget->model = 'profile';
+	$modelWidget->title = 'Profil';
+	$modelWidget->minWidth = 2;
+	$modelWidget->maxWidth = 4;
+	$modelWidget->icon = 'far fa-user';
+	$modelWidget->background = '#007bff';
+	$modelWidget->load = 'action.php?action=dashboard_widget_profile_load';
+	$modelWidget->js = [Plugin::url().'/js/widget.js?v='.time()];
+	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
+	$modelWidget->configure = 'action.php?action=dashboard_widget_profile_configure';
+	$modelWidget->configure_callback = 'dashboard_widget_profile_configure_save';
+	$modelWidget->description = "Affiche les informations de profil";
+	$widgets[] = $modelWidget;
+
+	$modelWidget = new DashboardWidget();
+	$modelWidget->model = 'html';
+	$modelWidget->title = 'Texte / Code HTML';
+	$modelWidget->icon = 'fas fa-code';
+	$modelWidget->background = '#686de0';
+	$modelWidget->load = 'action.php?action=dashboard_widget_html_load';
+	$modelWidget->js = [Plugin::url().'/js/widget.js?v='.time()];
+	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
+	$modelWidget->configure = 'action.php?action=dashboard_widget_html_configure';
+	$modelWidget->configure_callback = 'dashboard_widget_html_configure_save';
+	$modelWidget->description = "Affiche un texte ou un morceau de code html intégré";
+	$widgets[] = $modelWidget;
+
+	$modelWidget = new DashboardWidget();
+	$modelWidget->model = 'log';
+	$modelWidget->title = 'Logs';
+	$modelWidget->defaultWidth = 8;
+	$modelWidget->options[] = array('function'=>'window.location = \'setting.php?section=log\';','icon'=>'fa-eye','title'=>'Voir tous les logs');
+	$modelWidget->icon = 'far fa-comment-dots';
+	$modelWidget->background = '#28a745';
+	$modelWidget->load = 'action.php?action=dashboard_widget_log_load';
+	$modelWidget->js = [Plugin::url().'/js/widget.js?v='.time()];
+	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
+	$modelWidget->description = "Affiche les informations des 30 derniers logs";
+	if($myUser->can('log','read')) 
+		$widgets[] = $modelWidget;
+}
+
+
+
+//Déclation des assets
+Plugin::addCss("/css/main.css"); 
+Plugin::addJs("/js/main.js"); 
+
+//Mapping hook / fonctions
+Plugin::addHook("install", "dashboard_install");
+Plugin::addHook("uninstall", "dashboard_uninstall"); 
+
+
+Plugin::addHook("page", "dashboard_page");  
+
+Plugin::addHook("menu_setting", "dashboard_menu_setting");    
+Plugin::addHook("content_setting", "dashboard_content_setting");   
+Plugin::addHook("widget", "dashboard_default_widget");
+
+?>

+ 0 - 0
plugin/dashboard/img/default-background.jpg → dashboard/img/default-background.jpg


+ 525 - 0
dashboard/js/main.js

@@ -0,0 +1,525 @@
+//handle target
+var target = false;
+var refreshInterval = null;
+var resizeTarget = false;
+
+//CHARGEMENT DE LA PAGE
+$(function(){
+
+	if($.urlParam('section') == 'list.widget.share') dashboard_widget_share_search();
+	if(($.page()!='' && $.page()!='index') || $.urlParam('module')!=null) return;
+	dashboard_dashboardwidget_search();
+		
+	$('#dashboard-view li').click(function(){
+		$('#dashboard-view li').removeAttr('data-selected');
+		$(this).attr('data-selected',1);
+		dashboard_dashboardwidget_search();
+	});
+	$('#widget-list').change(function(){
+		var option = $('#widget-list option:selected').data();
+		option.text = $('#widget-list option:selected').text();
+
+		var description = $('.widgetDescription');
+		description.removeClass('hidden');
+		$('h3 span', description).text($(this).val() == '' ? 'Sélectionnez un widget' : option.text);
+		$('h3 i', description).attr('class',option.icon);
+		$('p', description).text(option.description);
+		$('.widgetWidth', description).html(option.width=='' ? '' : '<span class="text-muted font-weight-bold mr-2">LARGEUR</span> '+Math.round(option.width*100 /12)+'%');
+		$('.widgetColor', description).html(option.background=='' ? '' : '<span class="text-muted font-weight-bold  mr-2">COULEUR</span> <small style="background-color:'+option.background+'"></small><span>'+option.background+'</span>   ');
+	});
+
+	if(!$('.dashboard-container').hasClass('readonly')){
+		$('#dashboard').sortable({
+			distance: 15,
+			cancel :'[data-mandatory="1"]',
+			tolerance: "pointer",
+			handle: ".widget_header",
+			opacity: 0.8,
+			placeholder: {
+				element: function(clone, ui) {
+					var classes = clone.attr('class').split(' ');
+					for(var key in classes){
+						var number = classes[key].match(/col-xl-([0-9])/i);
+						if(number != null)
+							colSize = number[1] > 9 ? 9 : number[1];
+					}
+	                return $('<div class="ui_sortable_placeholder widget_placeholder col-xl-'+colSize+' col-md-'+colSize*2+' col-lg-'+colSize*2+'"></div>');
+	            },
+	            update: function() {
+	                return;
+	            }
+			},
+			update:function(event,ui){
+				var data = dashboard_dashboardwidget_save_position();
+			}
+		});
+		$( ".widget" ).disableSelection();
+	}
+});
+
+
+function init_setting_dashboard(){
+	dashboard_dashboard_search();
+	dashboard_widget_share_search();
+}
+
+//Enregistrement des configurations
+function dashboard_setting_save(){
+	$.action({ 
+		action : 'dashboard_setting_save', 
+		fields :  $('#dashboard-setting-form').toJson() 
+	},function(){
+		$.message('success','Enregistré');
+	});
+}
+
+
+/** DASHBOARD **/
+//Récuperation d'une liste de dashboard dans le tableau #dashboards
+function dashboard_dashboard_search(callback){
+	$('#dashboards').fill({
+		action:'dashboard_dashboard_search'
+	},function(){
+		if(callback!=null) callback();
+	});
+}
+
+//Ajout ou modification d'élément dashboard
+function dashboard_dashboard_save(){
+	var form = $('#dashboard-form');
+	var data = form.toJson();
+
+	$.action(data,function(r){
+		form.attr('data-id','');
+		reset_inputs(form);
+		$('#icon').val('far fa-bookmark');
+		init_components(form);
+		
+		$.message('success','Enregistré');
+		
+		dashboard_dashboard_search();
+	});
+}
+
+//Récuperation ou edition d'élément dashboard
+function dashboard_dashboard_edit(element){
+	var line = $(element).closest('tr');
+
+	$.action({
+		action:'dashboard_dashboard_edit',
+		id:line.attr('data-id')
+	},function(r){
+		$.setForm('#dashboard-form',r);
+		$('#dashboard-form').attr('data-id',r.id);
+		init_components('#dashboard-form');
+	});
+}
+
+//Suppression d'élement dashboard
+function dashboard_dashboard_delete(element){
+	if(!confirm('Êtes-vous sûr de vouloir supprimer ce dashboard ?')) return;
+	var line = $(element).closest('tr');
+
+	$.action({
+		action : 'dashboard_dashboard_delete',
+		id : line.attr('data-id')
+	},function(r){
+		line.remove();
+		$.message('info','Dashboard supprimé');
+	});
+}
+
+/** DASHBOARDWIDGET **/
+function configure_widget(element){
+	var widget = $(element).closest('.widget');
+	var configureUrl = widget.attr('data-configure');
+	
+	if(configureUrl.substring(0,11) == 'setting.php'){
+		window.open(configureUrl); 
+	}else{
+		var modal = $('#configureWidgetModal');
+		modal.attr('data-widget',widget.attr('data-id')).modal('show');
+		$('.pluginContent', modal).load(widget.attr('data-configure'),{
+			id : widget.attr('data-id')
+		},function(){
+			init_components();
+			var callback = widget.attr('data-configure-init');
+			if(window[callback]!=null) window[callback](widget,modal);
+		});
+	}
+}
+
+function dashboard_dashboardwidget_save_configuration(){
+	var modal = $('#configureWidgetModal');
+	var widget = $('.widget[data-id="'+modal.attr('data-widget')+'"]');
+
+	modal.modal('hide');
+	
+	var callback = widget.attr('data-configure-callback');
+	if(window[callback]!=null) window[callback](widget,modal);
+}
+
+
+//Récuperation d'une liste de dashboardwidget dans le tableau #dashboardwidgets
+function dashboard_dashboardwidget_search(callback){
+	var dashboard = $('#dashboard-view li[data-selected]').attr('data-id');
+	if(!dashboard || dashboard=='') return;
+	$.action({
+		action : 'dashboard_dashboardwidget_search',
+		dashboard : dashboard
+	},function(r){
+		$('#dashboard .widget:visible').remove();
+
+		for(var i in r.rows){
+			var widget = r.rows[i];
+			dashboard_dashboardwidget_append(widget,function(widget){
+				dashboard_dashboardwidget_load(widget);
+			});
+		}
+		clearInterval(refreshInterval);
+		refreshInterval = setInterval(function(){
+			dashboard_dashboardwidget_refresh();
+		},3000);
+
+		if(!$('.dashboard-container').hasClass('readonly')){
+			//Gestion du resize de largeur
+			$('.widget_resize').draggable({
+				axis: "x",
+				cancel :'[data-mandatory="1"]',
+				start : function(event,ui){
+					var widget = ui.helper.closest('.widget');
+					$('.widget_content',widget).append('<div class="readonly-veil"></div>');
+				},
+				drag: function(event,ui) {
+					var widget = ui.helper.closest('.widget');
+				    var width = widget.attr('data-width');
+				    var percent = ui.position.left * 100 / ui.originalPosition.left;
+				    var newWidth = Math.round(width * percent/100);
+				    newWidth = newWidth<1 ? 1 : newWidth;
+				    newWidth = newWidth>12 ? 12 : newWidth;
+
+				    //Gestion taille widget max
+				    if(widget.attr('data-maxWidth') && widget.attr('data-maxWidth').length){
+				    	maxWidth = widget.attr('data-maxWidth');
+					    newWidth = newWidth>maxWidth ? maxWidth : newWidth;
+				    }
+				    //Gestion taille widget min
+			        if(widget.attr('data-minWidth') && widget.attr('data-minWidth').length){
+			        	minWidth = widget.attr('data-minWidth');
+					    newWidth = newWidth<minWidth ? minWidth : newWidth;
+			        }
+	
+				    widget.removeClass('col-xl-1 col-xl-2 col-xl-3 col-xl-4 col-xl-5 col-xl-6 col-xl-7 col-xl-8 col-xl-9 col-xl-10 col-xl-11 col-xl-12');
+				    widget.addClass('col-xl-'+newWidth);
+
+				    widget.removeClass('col-lg-1 col-lg-2 col-lg-3 col-lg-4 col-lg-5 col-lg-6 col-lg-7 col-lg-8 col-lg-9 col-lg-10 col-lg-11 col-lg-12');
+				    widget.addClass('col-lg-'+newWidth*2);
+
+				    widget.removeClass('col-md-1 col-md-2 col-md-3 col-md-4 col-md-5 col-md-6 col-md-7 col-md-8 col-md-9 col-md-10 col-md-11 col-md-12');
+				    widget.addClass('col-md-'+newWidth*2);
+	
+				},
+				stop: function(event,ui){
+					var resizebar = ui.helper;
+					var widget = resizebar.closest('.widget');
+					$('.widget .readonly-veil').remove();
+					var width = widget.attr('data-width');
+					var newWidth = widget.attr('class').match(/col-xl-([0-9]*)/)
+					newWidth = newWidth[1];
+					
+					if(newWidth!=width){
+					    $.action({
+							action : 'dashboard_dashboardwidget_resize',
+							widget : widget.attr('data-id'),
+							width: newWidth
+						},function(r){
+							widget.attr('data-width',newWidth);
+							
+						});
+					}
+					resizebar.removeAttr('style');
+				}
+			});
+		}
+	});
+}
+
+function dashboard_dashboardwidget_refresh(){
+	$.action({
+		action : 'dashboard_dashboardwidget_refresh',
+		dashboard : $('#dashboard-view li[data-selected]').attr('data-id')
+	},function(r){
+
+		for(var id in r.rows){
+			var widget = r.rows[id];
+
+			if(widget.widget){
+				var header = $('.widget[data-id="'+id+'"]').find('.widget_header'); 
+				if(widget.widget.title) header.find('span').text(widget.widget.title);
+				if(widget.widget.icon) header.find('i').attr('class','fa '+widget.widget.icon);
+				if(widget.widget.background) header.css('backgroundColor',widget.widget.background);
+			}
+
+			if(widget.callback){
+				if(window[widget.callback]!=null) window[widget.callback]($('.widget[data-id="'+id+'"]'),widget.data);
+			}
+			
+		}
+	});
+}
+
+//Mise à jour des infos d'un élement widget à partir d'un object data
+function dashboard_dashboardwidget_render(widget,data){
+	widget.attr('data-id',data.id);
+	widget.removeClass (function (index, css) {
+		return (css.match (/(^|\s)col-xl-\S+/g) || []).join(' ');
+	});
+	var width = data.width!=0 ? data.width : data.defaultWidth; 
+
+	widget.attr('data-id',data.id)
+		.attr('data-load',data.load)
+		.attr('data-configure',data.configure)
+		.attr('data-configure-callback',data.configure_callback)
+		.attr('data-configure-init',data.configure_init)
+		.attr('data-delete',data.delete)
+		.attr('data-mandatory',data.mandatory && data.mandatory==1 ?1:0)
+		.attr('data-model',data.model)
+		.attr('data-width',width)
+		.addClass('col-xl-'+width)
+		.addClass('col-lg-'+width*2)
+		.addClass('col-md-'+width*2)
+		.find('.widget_header')
+		.css('background',data.background)
+		.find('i:eq(0)').attr('class',data.icon);
+
+	if(data.minWidth && data.minWidth!=0) widget.attr('data-minWidth', data.minWidth);
+	if(data.maxWidth && data.maxWidth!=0) widget.attr('data-maxWidth', data.maxWidth);
+
+	widget.find('.widget_header span:eq(0)').html(data.title);
+	widget.find('.widget_content').html(data.content);
+		
+	var options = '';
+	for(var k in data.options){
+		var option = data.options[k];
+		options+='<li onclick="'+option.function+'" '+(option.title ? 'title="'+option.title+'"':'')+'><i class="fa '+option.icon+'"></i> '+(option.label ? option.label:'')+'</li>';
+	}
+	if(data.configure) options+="<li title='Configurer' onclick='configure_widget(this);'><i class='fa fa-wrench'></i></li>";
+	options+="<li title='Supprimer' onclick='dashboard_dashboardwidget_delete(this);'><i class='fa fa-times'></i></li>";
+
+	widget.find('.widget_options').html(options);
+	widget.data('data',data);
+	widget.removeClass('hidden');
+
+	return widget;
+}
+
+//Modification d'un widget existant
+function dashboard_dashboardwidget_update(data){
+	var widget = $('.widget[data-id="'+data.id+'"]');
+	var data = $.extend(widget.data('data'), data);
+	dashboard_dashboardwidget_render(widget,data);
+}
+
+//Ajout ou modification d'élément dashboardwidget
+function dashboard_dashboardwidget_save(){
+	var data = $('#dashboardwidget-form').toJson();
+	$.action(data,function(r){
+		$('#dashboardwidget-form').attr('data-id','');
+		dashboard_dashboardwidget_search();
+		$.message('success','Enregistré');
+	});
+}
+
+//Chargement du contenu php du widget
+function dashboard_dashboardwidget_load(widget){
+	
+	$.getJSON(widget.load,{
+		id : widget.id,
+		model : widget.model,
+		content:'',
+	},function(r){
+		if(r.error && r.error.code == 403){
+			$('.widget[data-id="'+widget.id+'"]').remove();
+			return;
+		}
+		dashboard_dashboardwidget_update(r);
+		var data = $.extend($('.widget[data-id="'+widget.id+'"]').data('data'), r.widget);
+
+		var init = 'widget_'+data.model.replace(/[^a-z0-9]/i,'_')+'_init';
+		if(window[init]!=null) window[init]();
+	});
+}
+
+//Ajout (manuel par l'user) d'un widget
+function dashboard_dashboardwidget_add(){
+	$.action({
+		action : 'dashboard_dashboardwidget_add',
+		dashboard : $('#dashboard-view li[data-selected]').attr('data-id'),
+		widget : $('#widget-list').val()
+	},function(r){
+		if(r.message) $.message('info',r.message);
+		$('#dashboard_dashboardwidget_appendModal').modal('hide');
+		dashboard_dashboardwidget_search();
+	});
+}
+
+//Ajout (depuis le code) d'un widget
+function dashboard_dashboardwidget_append(data,callback){
+	var tpl = $('#dashboard .widget:hidden').get(0).outerHTML;
+	var widget = $(tpl);
+	$('#dashboard').append(widget);
+	if(data.css!=null){
+		for(k in data.css){
+			var css = data.css[k];
+			if($('link[href="'+css+'"]').length!=0) continue;
+
+			//on supprime tout autre css ayant la même base mais des versions plus vielles
+			var baseCss = css.replace(/\?v=.*/gm,'');
+			$('link[href^="'+baseCss+'"]').remove();
+
+			var cssFile = document.createElement('link');
+			cssFile.setAttribute("rel","stylesheet");
+			cssFile.setAttribute("type","text/css");
+			cssFile.setAttribute("href", css);
+			document.getElementsByTagName("body")[0].appendChild(cssFile);
+		}
+	}
+	dashboard_dashboardwidget_render(widget,data);
+	if(data.js!=null){
+		dashboard_load_js(data.js,0,function(){
+			if(callback) callback(data);
+		});
+	}else{
+		if(callback) callback(data);
+	}
+	
+	
+}
+
+function dashboard_load_js(files,iteration,callback){
+	var js = files[iteration];
+
+	if($('script[src="'+js+'"]').length!=0 || js==null) {
+		if(files.length > iteration) dashboard_load_js(files,iteration+1,callback);
+		if((files.length-1) == iteration) if(callback) callback();
+		return;
+	}
+	//on supprime tout autre js ayant la même base mais des versions plus vielles
+	var baseJs = js.replace(/\?v=.*/gm,'');
+	$('script[src^="'+baseJs+'?"]').remove();
+
+	var jsFile = document.createElement('script');
+	jsFile.setAttribute("type","text/javascript");
+	document.getElementsByTagName("body")[0].appendChild(jsFile);
+	jsFile.onload = function() {
+		if(files.length > iteration) dashboard_load_js(files,iteration+1,callback);
+		if((files.length-1) == iteration) if(callback) callback();
+	};
+	jsFile.src =  js;
+}
+
+//Récuperation ou edition d'élément dashboardwidget
+function dashboard_dashboardwidget_edit(widget){
+	var line = $(element).closest('tr');
+	$.action({action:'dashboard_dashboardwidget_edit',id:line.attr('data-id')},function(r){
+		$.setForm('#dashboardwidget-form',r);
+		$('#dashboardwidget-form').attr('data-id',r.id);
+	});
+}
+
+//Suppression d'élement dashboardwidget
+function dashboard_dashboardwidget_delete(element){
+	var element = $(element).closest('.widget');
+	var data = element.data('data');
+	
+	$.action({
+		action : 'dashboard_dashboardwidget_delete',
+		dashboard : $('#dashboard-view li[data-selected]').attr('data-id'),
+		widget : data.id,
+	},function(r){
+		element.remove();
+		if(r.message) $.message('info',r.message);
+		else $.message('info', 'Widget supprimé');
+		if(data.delete != null){
+			$.getJSON(data.delete,$.extend(data,{content:''}));
+		}
+	});
+}
+
+
+
+//Enregistrement de toutes les positions de widget
+function dashboard_dashboardwidget_save_position(){
+	var positions = [];
+	$('.widget:visible').each(function(i,element){
+		positions.push({id:$(element).attr('data-id'),position:$(element).index()});
+	});
+	
+	$.action({
+		action : 'dashboard_dashboardwidget_save_position',
+		dashboard : $('#dashboard-view li[data-selected]').attr('data-id'),
+		positions : positions,
+	},function(r){
+		
+	});
+
+}
+
+
+/** DASHBOARDWIDGETSHARE **/
+//Récuperation d'une liste de dashboardwidgetshare dans le tableau #dashboardwidgetshares
+function dashboard_widget_share_search(callback){
+	$('#dashboard-widget-shares').fill({
+		action: 'dashboard_widget_share_search'
+	},function(response){
+		if(callback!=null) callback();
+	});
+}
+
+//Ajout ou modification d'élément dashboardwidgetshare
+function dashboard_widget_share_save(){
+	var data = $('#dashboard-widget-share-form').toJson();
+	var target = $('#uid').data('values');
+
+	if(target){
+		data.entity = target[0].entity;
+		data.uid = target[0].uid;
+	}
+
+	$.action(data,function(r){
+		$('#dashboard-widget-share-form').attr('data-id','');
+		$('#dashboard-widget-share-form').clear();
+		dashboard_widget_share_search();
+		
+		$.message('success','Enregistré');
+	});
+}
+
+
+//Récuperation ou edition d'élément dashboardwidgetshare
+function dashboard_widget_share_edit(element){
+	var line = $(element).closest('tr');
+
+	$.action({
+		action: 'dashboard_widget_share_edit',
+		id: line.attr('data-id')
+	},function(r){
+		$.setForm('#dashboard-widget-share-form',r);
+		init_components('#dashboard-widget-share-form');
+		$('#dashboard-widget-share-form').attr('data-id',r.id);
+	});
+}
+
+//Suppression d'élement dashboardwidgetshare
+function dashboard_widget_share_delete(element){
+	if(!confirm('Êtes vous sûr de vouloir supprimer cet item ?')) return;
+	var line = $(element).closest('tr');
+	
+	$.action({
+		action: 'dashboard_widget_share_delete',
+		id: line.attr('data-id')
+	},function(r){
+		line.remove();
+		$.message('info','Item supprimé');
+	});
+}

+ 0 - 0
plugin/dashboard/js/progressbar.js → dashboard/js/progressbar.js


+ 0 - 0
plugin/dashboard/js/progressbar.min.js → dashboard/js/progressbar.min.js


+ 0 - 0
plugin/dashboard/js/widget.js → dashboard/js/widget.js


+ 0 - 0
plugin/dashboard/page.home.php → dashboard/page.home.php


+ 0 - 0
plugin/dashboard/setting.dashboard.php → dashboard/setting.dashboard.php


+ 0 - 0
plugin/dashboard/widget.clock.php → dashboard/widget.clock.php


+ 0 - 0
plugin/dashboard/widget.html.configure.php → dashboard/widget.html.configure.php


+ 0 - 0
plugin/dashboard/widget.html.php → dashboard/widget.html.php


+ 0 - 0
plugin/dashboard/widget.logs.php → dashboard/widget.logs.php


+ 0 - 0
plugin/dashboard/widget.profile.configure.php → dashboard/widget.profile.configure.php


+ 0 - 0
plugin/dashboard/widget.profile.php → dashboard/widget.profile.php


+ 21 - 12
plugin/dashboard/Dashboard.class.php

@@ -1,21 +1,30 @@
 <?php
 /**
- * Define a dashboard.
- * @author Administrateur
+ * Define a Dasboard
+ * @author Jean CARRUESCO
  * @category Plugin
  * @license copyright
  */
 class Dashboard extends Entity{
-	public $id,$user,$label,$icon,$default,$mandatory;
-	protected $TABLE_NAME = 'dashboard_dashboard';
-	public $fields =
-	array(
-		'id' => 'key',
-		'user' => 'string',
-		'label' => 'string',
-		'icon' => 'string',
-		'mandatory' => 'boolean',
-		'default' => 'boolean'
+
+	public $id;
+	public $label; //Libellé (Texte)
+	public $slug; //Slug (Texte)
+	public $scope; //Scope (Texte)
+	public $uid; //Uid (Texte)
+	
+	protected $TABLE_NAME = 'dashboard';
+	public $entityLabel = 'Dasboard';
+	public $fields = array(
+		'id' => array('type'=>'key', 'label' => 'Identifiant'),
+		'label' => array('type'=>'text','label' => 'Libellé'),
+		'slug' => array('type'=>'text','label' => 'Slug'),
+		'scope' => array('type'=>'text','label' => 'Scope'),
+		'uid' => array('type'=>'text','label' => 'Uid')
 	);
+
+	//Colonnes indexées
+	public $indexes = array();
+	
 }
 ?>

+ 56 - 76
plugin/dashboard/DashboardWidget.class.php

@@ -1,93 +1,73 @@
 <?php
 /**
- * Define a dashboardwidget.
- * @author Administrateur
+ * Define a Bloc de tableau de bord
+ * @author Jean CARRUESCO
  * @category Plugin
  * @license copyright
  */
 class DashboardWidget extends Entity{
-	public $id,$minified,$position,$model,$dashboard,$title,$icon,$background,$width,$load,$configure,$configure_callback,$configure_init,$move,$delete,$options,$js,$css,$content,$data,$description,$defaultWidth;
-	protected $TABLE_NAME = 'dashboard_dashboard_widget';
-	public $fields =
-	array(
-		'id' => 'key',
-		'model' => 'string',
-		'data' => 'longstring',
-		'position' => 'int',
-		'minified' => 'boolean',
-		'width' => 'int',
-		'dashboard' => 'int'
-	);
-
-	function __construct(){
- 		parent::__construct();
- 		$this->options = array();
- 		$this->icon = 'fa-caret-right';
- 		$this->title = 'Sans titre';
- 		$this->defaultWidth = 4;
- 		$this->width = 0;
- 	}
-
- 	public function toArray($decoded= false){
-		$fields = parent::toArray($decoded);
-		$fields['options'] = $this->options;
-		$fields['icon'] =  $this->icon;
-		$fields['background'] =  $this->background;
-		$fields['load'] =  $this->load;
-		$fields['configure'] =  $this->configure;
-		$fields['configure_callback'] =  $this->configure_callback;
-		$fields['configure_init'] =  $this->configure_init;
-		$fields['js'] =  $this->js;
-		$fields['css'] =  $this->css;
-		$fields['description'] =  $this->description;
-		$fields['content'] =  $this->content;
-		$fields['title'] =  $this->title;
-		return $fields;
- 	}
-
- 	function data($key=null,$value=null){
- 		$data = json_decode($this->data,true);
- 		if($key==null) return $data;
- 		if(is_array($key) && $value==null){
- 			foreach ($key as $k => $v) {
- 				$data[$k] = $v;
- 				$this->data = json_encode($data);
- 			}
- 			return true;
- 		}
- 	
- 		if($value===null) return isset($data[$key])?$data[$key]:'';
- 		
- 		$data[$key] = $value;
 
- 		$this->data = json_encode($data);
- 		return true;
- 	}
-
- 	public static function current(){
- 		global $_;
- 		$dbWidget = self::getById($_['id']);
+	public $id;
+	public $model; //Modèle (Texte)
+	public $type; //Type (Liste classique)
+	public $meta; //Meta (Texte Long)
+	public $column = 0 ; //Colonne (Nombre Entier)
+	public $row = 0 ; //Ligne (Nombre Entier)
+	public $width = 1; //Largeur (Nombre Entier)
+	public $height = 1; //Hauteur (Nombre Entier)
+	public $dashboard; //Tableau de bord (Nombre Entier)
+	
+	protected $TABLE_NAME = 'dashboard_widget';
+	public $entityLabel = 'Bloc de tableau de bord';
+	public $fields = array(
+		'id' => array('type'=>'key', 'label' => 'Identifiant'),
+		'model' => array('type'=>'text','label' => 'Modèle'),
+		'type' => array('type'=>'list','label' => 'Type'),
+		'meta' => array('type'=>'textarea','label' => 'Meta'),
+		'column' => array('type'=>'integer','label' => 'Colonne'),
+		'row' => array('type'=>'integer','label' => 'Ligne'),
+		'width' => array('type'=>'integer','label' => 'Largeur'),
+		'height' => array('type'=>'integer','label' => 'Hauteur'),
+		'dashboard' => array('type'=>'integer','label' => 'Tableau de bord','link'=>'plugin/dashboard/Dashboard.class.php')
+	);
 
- 		$widget = DashboardWidget::models($dbWidget->model);
- 		$widget->id = $dbWidget->id;
- 		$widget->position =  $dbWidget->position;
- 		$widget->minified =  $dbWidget->minified;
- 		$widget->width = $dbWidget->width;
- 		$widget->dashboard = $dbWidget->dashboard;
- 		$widget->data = $dbWidget->data;
+	//Colonnes indexées
+	public $indexes = array();
+	
+	//liste des Type possibles
+	public static function types($key=null){
+		$items = array(
+			'widget' => array('label'=>'Widget'),
+			'model' => array('label'=>'Modèle de widget'),
+		);
+		if(!isset($key)) return $items;
+		return isset($items[$key]) ? $items[$key] : array('label'=>'Non définis');
+	}
 
- 		return $widget;
- 	}
 
- 	public static function models($modelUid = null){
- 		Plugin::callHook('widget',array(&$models));
+	public static function model($slug = null){
+		
+		/*$model = array();
+		Plugin::callHook('widget',array(&$models));
 
 		foreach($models as $model)
 			$models[$model->model] = $model;
 
-		if(!isset($modelUid)) return $models;
+		if(!isset($modelUid)) return $models;*/
 		
-		return isset($models[$modelUid]) ? $models[$modelUid] : array();
- 	}
+
+		//todo
+		$model = array(
+			'icon' => 'far fa-user',
+			'headerBackground' => 'rgb(0, 123, 255)',
+			'label' => 'Hello monde',
+			'content' => '<p>Wazup ?</p>',
+			'width' => 3,
+			'height' => 2
+		);
+		/*return isset($models[$modelUid]) ? $models[$modelUid] : $model;*/
+	
+		return $model;
+	}
 }
 ?>

+ 74 - 340
plugin/dashboard/action.php

@@ -1,381 +1,115 @@
 <?php
 
 
-	/** DASHBOARD **/
-	//Récuperation d'une liste de dashboard
-	Action::register('dashboard_dashboard_search',function(&$response){
-		global $myUser,$_;
+    /** DASHBOARDWIDGET / BLOC DE TABLEAU DE BORD **/
+
+	Action::register('dashboard_widget_search',function(&$response){
+		global $_;
 		User::check_access('dashboard','read');
+		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
 		require_once(__DIR__.SLASH.'Dashboard.class.php');
-
+		
 		$filters = array();
-		if(!$myUser->can('dashboard','configure')) $filters['creator'] = $myUser->login;
-
-		foreach(Dashboard::loadAll($filters,array('label','creator')) as $dashboard){
-			$userName = User::byLogin($dashboard->user)->fullName();
-			$dashboard->user = !empty($userName) ? $userName : $dashboard->user;
-			$dashboard->mandatory = $dashboard->mandatory == 1;
-			$dashboard->default = $dashboard->default == 1;
-			$response['rows'][] = $dashboard;
-		}
-	});
-
-	//Ajout ou modification d'élément dashboard
-	Action::register('dashboard_dashboard_save',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','edit');
-		if($_['mandatory']==1 && !$myUser->can('dashboard','configure'))  throw new Exception("Vous n'avez pas les droits pour rendre ce dashboard obigatoire",403);
-		require_once(__DIR__.SLASH.'Dashboard.class.php');
-
-		$item = Dashboard::provide();
-		if(!isset($_['user']) || empty($_['user'])) $_['user'] = $myUser->login;
-
-		if($myUser->login!=$item->creator && !$myUser->can('dashboard','configure') && $item->id!=0)  throw new Exception("Vous n'avez pas les droits pour éditer le dashboard d'un autre utilisateur",403);
-		if($myUser->login!=$_['user'] && !$myUser->can('dashboard','configure')) throw new Exception("Vous n'avez pas les droits pour créer un dashboard à un autre utilisateur");
-
-		$item->user = $_['user'];
-		$item->label = $_['label'];
-		$item->icon = $_['icon'];
-		$item->default = $_['default'];
-		if($item->default) Dashboard::change(array('default'=>0), array('user'=>$item->user));
-		$item->mandatory = $_['mandatory'];
-		if($item->mandatory) Dashboard::change(array('mandatory'=>0));
-		$item->save();
-	});
-
-	//Récuperation ou edition d'élément dashboard
-	Action::register('dashboard_dashboard_edit',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','edit');
-		require_once(__DIR__.SLASH.'Dashboard.class.php');
-		$response = Dashboard::getById($_['id']);
-	});
 
-	//Suppression d'élement dashboard
-	Action::register('dashboard_dashboard_delete',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','delete');
-		require_once(__DIR__.SLASH.'Dashboard.class.php');
-		$item = Dashboard::provide();
-		if($myUser->login!=$item->creator && !$myUser->can('dashboard','configure'))  throw new Exception("Vous n'avez pas les droits pour supprimer le dashboard d'un autre utilisateur",403);
-		Dashboard::deleteById($_['id']);
-	});
+		if(empty($_['uid']))  throw new Exception('Scope non spécifié');
 
-	//Sauvegarde des configurations de dashboard
-	Action::register('dashboard_setting_save',function(&$response){
-		global $myUser,$_,$conf;
-		User::check_access('dashboard','configure');
-		foreach(Configuration::setting('dashboard') as $key=>$value){
-			if(!is_array($value)) continue;
-			$allowed[] = $key;
-		}
-		foreach ($_['fields'] as $key => $value)
-			if(in_array($key, $allowed)) $conf->put($key,$value);
-	});
+		$filters['scope'] = $_['scope'];
+		if(empty($_['uid'])) $filters['uid'] = $_['uid'];
 
-	/** DASHBOARDWIDGET **/
-	//Récuperation d'une liste de dashboardwidget
-	Action::register('dashboard_dashboardwidget_search',function(&$response){
-	
-		global $myUser,$myFirm,$_;
-		User::check_access('dashboard','read');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
+		$dashboard = Dashboard::load($filters);
 
-		$models = DashboardWidget::models();
-		$ranksId = $myUser->getRanksId($myFirm->id);
-		$widgetsQry = "SELECT {{table}}.*,ds.mandatory,ds.sort as sort
-			FROM {{table}}
-			LEFT JOIN ".DashboardWidgetShare::tableName()." ds ON ds.widget={{table}}.id
-			WHERE dashboard=?
-				OR {{table}}.id IN (
-					SELECT widget FROM ".DashboardWidgetShare::tableName()." WHERE (entity=? AND uid=?) OR (entity=? and uid IN (".str_repeat('?,', count($ranksId) - 1) . '?'.")) OR (entity IS NULL)
-				)
-				ORDER BY sort, position DESC";
-		$widgets = DashboardWidget::staticQuery($widgetsQry,array_merge(array($_['dashboard'],'user',$myUser->login,'rank'),$ranksId),true);
+		$widgets = DashboardWidget::loadAll(array('dashboard'=>$dashboard->id)); 
+		$response['widgets'] = array();
 
 		foreach($widgets as $widget){
-			if(!isset($models[$widget->model])) continue;
-			$model = clone $models[$widget->model];
+			$row = $widget->toArray();
+			$model = DashboardWidget::model($widget->model);
+			$row = array_merge($model,$row);
 
-			$row = $model->toArray();
-			$row['id'] = $widget->id;
-			$row['width'] = !empty($widget->width) && $widget->width>0 ? $widget->width : $model->defaultWidth;
-			$row['position'] = $widget->position;
-			$row['minified'] = $widget->minified;
-			$row['dashboard'] = $widget->dashboard;
-
-			if(!empty($widget->foreign('mandatory'))) $row['mandatory'] = $widget->foreign('mandatory');
-			if(!empty($widget->foreign('sort'))) $row['position'] = $widget->foreign('sort');
-
-			$response['rows'][] = $row;
-		}
-
-		if(isset($response['rows'])){
-			usort($response['rows'],function($a,$b){
-				return $a['position'] - $b['position'];
-			});
+			$response['widgets'][] = $row ;
 		}
+		
 	});
 
-	//Ajout ou modification d'élément dashboardwidget
-	Action::register('dashboard_dashboardwidget_save',function(&$response){
-		global $myUser,$_;
+	Action::register('dashboard_widget_add',function(&$response){
+		global $_,$myUser;
 		User::check_access('dashboard','edit');
 		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$item = DashboardWidget::provide();
-		$item->model = $_['model'];
-		$item->data = $_['data'];
-		$item->position = $_['position'];
-		$item->minified = $_['minified'];
-		$item->dashboard = $_['dashboard'];
-		$item->save();
-	});
-
-	//Récuperation ou edition d'élément dashboardwidget
-	Action::register('dashboard_dashboardwidget_edit',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','edit');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$response = DashboardWidget::getById($_['id']);
-	});
-
-	//Suppression d'élement dashboardwidget
-	Action::register('dashboard_dashboardwidget_delete',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','delete');
 		require_once(__DIR__.SLASH.'Dashboard.class.php');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$widget = DashboardWidget::getById($_['widget']);
-		if(!$widget) return;
+		
+		if(empty($_['scope'])) throw new Exception('Scope non sépcifié');
+		$filters = array('scope'=>$_['scope']);
 
-		$dashboard = Dashboard::getById($_['dashboard']);
-		if($widget->dashboard!=$dashboard->id || $dashboard->user!=$myUser->login)
-			throw new Exception("Vous ne pouvez supprimer que vos propres widgets");
+		if(!empty($_['uid'])) $filters['uid'] = $_['uid'];
 
-		$widget->deleteById($widget->id);
-		$response['message'] = 'Widget supprimé';
-	});
 
-	//Resize largeur d'élement dashboardwidget
-	Action::register('dashboard_dashboardwidget_resize',function(&$response){
-		global $myUser,$_;
-		require_once(__DIR__.SLASH.'Dashboard.class.php');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		User::check_access('dashboard','edit');
+		// $dashboard = new Dashboard();
+		// $dashboard->uid = $myUser->login;
+		// $dashboard->scope = "home";
+		// $dashboard->save();
 
-		$widget = DashboardWidget::getById($_['widget']);
-		$dashboard = Dashboard::getById($widget->dashboard);
-		if($widget->dashboard!=$dashboard->id || $dashboard->user!=$myUser->login)
-			throw new Exception("Vous ne pouvez redimenssioner que vos propres widgets");
+		$dashboard = Dashboard::load($filters);
+		if(!$dashboard) throw new Exception("Dashboard introuvable");
 
-		$widget->width = $_['width'];
-		$widget->save();
-	});
-
-	Action::register('dashboard_dashboardwidget_refresh',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','read');
-		$widgets = array();
-		Plugin::callHook('widget_refresh',array(&$widgets));
-		$response['rows'] = $widgets;
-	});
-
-	Action::register('dashboard_dashboardwidget_add',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','edit');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
 		$widget = new DashboardWidget();
-		$widget->model = $_['widget'];
-		$widget->position = 666;
-		$widget->minified = false;
-		$widget->width = $widget->width> 0 ? $widget->width: $widget->defaultWidth ;
-		$widget->dashboard = $_['dashboard'];
+		$widget->dashboard = $dashboard->id;
 		$widget->save();
-		$response['message'] = 'Widget ajouté';
-	});
-
-	Action::register('dashboard_dashboardwidget_save_position',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','edit');
-		require_once(__DIR__.SLASH.'Dashboard.class.php');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$dashboard = Dashboard::getById($_['dashboard']);
 
-		if($dashboard->user!=$myUser->login) throw new Exception("Vous ne pouvez modifier que vos propres widgets");
-		$dashboard_widgets = DashboardWidget::loadAll( array('dashboard' => $dashboard->id ) );
+		$row = $widget->toArray();
+		$model = DashboardWidget::model($widget->model);
+		$row = array_merge($model,$row);
 
-		foreach($_['positions'] as $move){
-			foreach($dashboard_widgets as $dashboard_widget){
-				if($dashboard_widget->id!=$move['id']) continue;
-				$dashboard_widget->position = $move['position'];
-				$dashboard_widget->save();
-			}
-		}
+		$response = $row;
 	});
 
-	/* CLOCK */
-	Action::register('dashboard_widget_clock_load',function(&$response){
-		global $myUser;
-		User::check_access('dashboard','read');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$widget = DashboardWidget::current();
-		ob_start();
-		require_once(__DIR__.SLASH.'widget.clock.php');
-		$widget->content = ob_get_clean();
-		echo json_encode($widget);
-		exit;
-	});
-
-	/* LOGS */
-	Action::register('dashboard_widget_log_load',function(&$response){
-		global $myUser;
-		require_once('DashboardWidget.class.php');
-		User::check_access('log','read');
-
-		$widget = DashboardWidget::current();
-		$widget->title = '30 derniers logs';
-
-		ob_start();
-		require_once(__DIR__.SLASH.'widget.logs.php');
-		$widget->content = ob_get_clean();
-		echo json_encode($widget);
-		exit;
-	});
-
-	/* PROFILE */
-	Action::register('dashboard_widget_profile_load',function(&$response){
-		global $myUser;
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		User::check_access('dashboard','read');
-		$widget = DashboardWidget::current();
-		if(!empty($widget->data('background-color'))) $widget->background = $widget->data('background-color');
-		ob_start();
-		require_once(__DIR__.SLASH.'widget.profile.php');
-		$widget->content = ob_get_clean();
-		echo json_encode($widget);
-		exit;
-	});
-
-	Action::register('dashboard_widget_profile_configure',function(&$response){
-		global $myUser;
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		User::check_access('dashboard','read');
-		$widget = DashboardWidget::current();
-		ob_start();
-		require_once(__DIR__.SLASH.'widget.profile.configure.php');
-		$content = ob_get_clean();
-
-		echo $content ;
-		exit;
-	});
-
-	Action::register('dashboard_widget_profile_configure_save',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','read');
+	Action::register('dashboard_widget_move',function(&$response){
+		global $_;
+		User::check_access('dashboard','edit');
 		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$widget = DashboardWidget::getById($_['id']);
-		$widget->data('background-color',$_['widget-profile-background-color']);
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		
+		if(empty($_['widget']['id'])) throw new Exception('Id non spécifié');
+		if(!isset($_['widget']['column'])) throw new Exception('Colonne non spécifié');
+		if(!isset($_['widget']['row'])) throw new Exception('Ligne non spécifié');
+
+		$widget = DashboardWidget::getById($_['widget']['id'],1);
+		if(!$widget) throw new Exception('Widget introuvable');
+		
+		$widget->column = $_['widget']['column'];
+		$widget->row = $_['widget']['row'];
 		$widget->save();
 	});
-
-
-	/* HTML */
-	Action::register('dashboard_widget_html_load',function(&$response){
-		global $myUser;
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		User::check_access('dashboard','read');
-		$widget = DashboardWidget::current();
-		$widget->title = $widget->data('title') != "" ? $widget->data('title') :  'Bloc HTML';
-		if($widget->data('color') != "") $widget->background = $widget->data('color');
-		ob_start();
-		require_once(__DIR__.SLASH.'widget.html.php');
-		$widget->content = ob_get_clean();
-		echo json_encode($widget);
-		exit;
-	});
-
-	Action::register( 'dashboard_widget_html_configure',function(&$response){
-		global $myUser;
-		User::check_access('dashboard','read');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$widget = DashboardWidget::current();
-		ob_start();
-		require_once(__DIR__.SLASH.'widget.html.configure.php');
-		$content = ob_get_clean();
-		echo $content ;
-		exit;
-	});
-
-	Action::register('dashboard_widget_html_configure_save',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','read');
+	
+	Action::register('dashboard_widget_resize',function(&$response){
+		global $_;
+		User::check_access('dashboard','edit');
 		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$widget = DashboardWidget::getById($_['id']);
-		$widget->data('html',html_entity_decode($_['widget-html-content']));
-		$widget->data('title',$_['widget-html-title']);
-		$widget->data('color',$_['widget-html-color']);
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		
+		if(empty($_['widget']['id'])) throw new Exception('Id non spécifié');
+		if(!isset($_['widget']['width'])) throw new Exception('Largeur non spécifiée');
+		if(!isset($_['widget']['height'])) throw new Exception('Hauteur non spécifiée');
+
+		$widget = DashboardWidget::getById($_['widget']['id'],1);
+		if(!$widget) throw new Exception('Widget introuvable');
+		
+		$widget->width = $_['widget']['width'];
+		$widget->height = $_['widget']['height'];
 		$widget->save();
 	});
 
-	/* DASHBOARD SHARE */
-	/** DASHBOARDWIDGETSHARE **/
-	//Récuperation d'une liste de dashboardwidgetshare
-	Action::register('dashboard_widget_share_search',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','read');
-		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
-		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-
-		$dashboardwidgetshares = DashboardWidgetShare::loadAll(array(), null,  null,array('*'), 1);
-		foreach($dashboardwidgetshares as $dashboardwidgetshare){
-			$row = $dashboardwidgetshare->toArray();
-
-			$row['widget'] = $dashboardwidgetshare->join('widget')->toArray();
-			$row['for'] = 'Tout le monde';
-			if($row['entity'] == 'rank' ) $row['for'] = 'Rang: '. Rank::getById($row['uid'] )->label;
-			if($row['entity'] == 'user' ) $row['for'] ='Utilisateur: '. User::byLogin($row['uid'] )->fullName();
-			$response['rows'][] = $row;
-		}
-	});
-
-	//Ajout ou modification d'élément dashboardwidgetshare
-	Action::register('dashboard_widget_share_save',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','edit');
-		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
-		//DashboardWidgetShare::create();
-		$item = DashboardWidgetShare::provide();
-
-		if(!isset( $_['widget']) || !is_numeric($_['widget'])) throw new Exception("Widget non spécifié ou invalide");
-
-		$item->widget = $_['widget'];
-		$item->mandatory = 1;//$_['mandatory'];
-
-		if(isset($_['entity'])){
-			$item->entity = $_['entity'];
-			$item->uid = $_['uid'];
-		}
-		$item->sort = !isset($_['sort']) ? 0 : $_['sort'];
-		$item->save();
-	});
-
-
-	//Récuperation ou edition d'élément dashboardwidgetshare
-	Action::register('dashboard_widget_share_edit',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','edit');
-		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
+	Action::register('dashboard_widget_delete',function(&$response){
+		global $_;
+		User::check_access('dashboard','delete');
 		require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-		$response = DashboardWidgetShare::getById($_['id'],1)->toArray();
-	});
+		require_once(__DIR__.SLASH.'Dashboard.class.php');
+		
+		if(empty($_['widget']['id'])) throw new Exception('Id non spécifié');
 
-	//Suppression d'élement dashboardwidgetshare
-	Action::register('dashboard_widget_share_delete',function(&$response){
-		global $myUser,$_;
-		User::check_access('dashboard','delete');
-		require_once(__DIR__.SLASH.'DashboardWidgetShare.class.php');
-		DashboardWidgetShare::deleteById($_['id']);
+		$widget = DashboardWidget::getById($_['widget']['id'],1);
+		if(!$widget) throw new Exception('Widget introuvable');
+		
+		DashboardWidget::deleteById($widget->id);
 	});
 
-?>
+?>

+ 5 - 3
plugin/dashboard/app.json

@@ -3,11 +3,13 @@
 	"name": "Dashboard",
 	"author" : {
 		"name" : "Valentin CARRUESCO",
-		"mail" : ""
+		"mail" : "contact@idleman.fr"
 	},
 	"version": "2.0",
 	"url": "http://no-url.com",
-	"licence": {"name": "MIT","url" : ""},
-	"description": "Dashboard avec widgets dynamiques",
+	"licence": {"name": "","url" : ""},
+	"color": "#273c75",
+	"icon": "far fa-object-ungroup",
+	"description": "",
 	"require" : {}
 }

+ 131 - 0
plugin/dashboard/css/component.css

@@ -0,0 +1,131 @@
+
+.dashboard-container {
+  background-color: #efefef;
+}
+
+.dashboard-container .dashboard-grid{
+  display: grid;
+  list-style-type: none;
+  margin: 0;
+  padding: 0;
+  grid-column-gap: 0px;
+  grid-row-gap: 0px;
+  position: relative;
+}
+
+.dashboard-container .dashboard-placeholder{
+  box-shadow: inset 0px 0px 1px 2px transparent;
+  list-style-type: none;
+  margin: 0;
+  min-height: 100px;
+  transition: all 0.2s ease-in-out;
+}
+
+.dashboard-container .dashboard-placeholder.bordered{
+  box-shadow: inset 0px 0px 1px 1px #bbbbbb;
+}
+
+.dashboard-container .dashboard-placeholder.drag-over{
+  box-shadow: inset 0px 0px 1px 2px #bbbbbb;
+}
+
+.dashboard-container .dashboard-widget{
+  position: absolute;
+  background-color: #ffffff;
+  border-radius: 10px;
+  box-shadow: 0 0 20px 0 #cecece!important;
+  transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
+}
+
+.dashboard-widget.ui-draggable-dragging{
+  transform: scale(1.03);
+  box-shadow: 0 0 40px 10px rgba(0,0,0,0.1)!important;
+}
+
+.dashboard-widget .dashboard-widget-header{
+  padding: 5px;
+  background-color: #222222;
+  color: #ffffff;
+  border-radius: 10px 10px 0 0;
+  display: flex;
+}
+
+.dashboard-widget .dashboard-widget-content{
+  padding: 5px;
+  max-height: 100%;
+  overflow: auto;
+}
+
+.dashboard-widget .dashboard-widget-header .widget-header-icon,
+.dashboard-widget .dashboard-widget-header .widget-header-title,
+.dashboard-widget .dashboard-widget-header .widget-header-options{
+  display: inline-block;
+  vertical-align: top;
+}
+
+.dashboard-widget .dashboard-widget-header .widget-header-icon{
+  padding-left: 5px;
+}
+
+.dashboard-widget .dashboard-widget-header .widget-header-title{
+  padding-left: 5px;
+  flex-grow: 1;
+}
+
+.dashboard-widget.widget-draggable .dashboard-widget-header .widget-header-title{
+  cursor: move;
+}
+
+
+.dashboard-widget .dashboard-widget-header .widget-header-options{
+  list-style-type: none;
+  margin: 0;
+  cursor: pointer;
+  float: right;
+  padding: 0;
+}
+
+.dashboard-widget .dashboard-widget-header .widget-header-options:after{
+  content :'';
+  display: block;
+  clear: both;
+}
+
+.dashboard-widget .widget-header-options li{
+  display: inline-block;
+}
+
+
+.dashboard-widget .dashboard-widget-resize{
+  opacity: 0;
+  position: absolute;
+  bottom: -3px;
+  right: 3px;
+  cursor: all-scroll;
+  transform: rotate(45deg);
+  transition: all 0.2s ease-in-out;
+}
+
+.dashboard-widget:hover .dashboard-widget-resize {
+  opacity: 0.3;
+}
+
+.dashboard-widget .widget-header-options li{
+  padding-left: 3px;
+  padding-right: 3px;
+  opacity: 0.5;
+  transition: opacity 0.2s ease-in-out;
+}
+
+.dashboard-widget .widget-header-options .widget-option-delete,
+.dashboard-widget .dashboard-widget-resize{
+  display: none;
+}
+.dashboard-widget.widget-removable .widget-header-options .widget-option-delete,
+.dashboard-widget.widget-resizeable .dashboard-widget-resize{
+  display: inline-block;
+}
+
+.dashboard-widget .widget-header-options li:hover{
+  opacity: 1;
+}

+ 7 - 309
plugin/dashboard/css/main.css

@@ -1,315 +1,13 @@
 /** DASHBOARD **/
-.module-index body{
-    background-color: #f5f5f5;
-}
-/* Conteneur principal du plugin dashboard */
-.dashboard {
-	
-}
-.dashboard-widget-log,.dashboard-widget-log li{
+/* Conteneur principal du plugin Dashboard */
+.plugin-dashboard{
 	list-style-type: none;
-	margin: 0;
-	padding: 5px;
-	border-bottom: 1px solid #f1f1f1;
-}
-.dashboard-widget-log{
-	padding:0 15px 15px 15px;
-}
-.dashboard-widget-log li {
-	font-size: 11px;
-}
-.dashboard-widget-log li >h2{
-	font-size: 14px;
-	margin-top: 15px;
-	padding: 3px 0 3px 0;
-	border-bottom: 2px solid #cecece;
+	margin:0;
+	padding:0;
 }
 
-/** DASHBOARD **/
-.widget[data-mandatory] {
-    border-left: 0px;
-}
+.plugin-dashboard #dashboards {}
 
-.widget .readonly-veil{
-	width:100%;
-	height:100%;
-	position:absolute;
-	top:0;
-	left:0;
-	opacity: 0.5;
-	background-color: #ffffff;
-	border-radius: 10px;
-	z-index: 100;
-}
+/* formulaire d'édition de tableau de bord */
+.plugin-dashboard .dashboard-form {}
 
-.widget[data-mandatory="1"] .widget_resize,.widget[data-mandatory="1"] .widget_header{
-	cursor: default;
-}
-.widget[data-mandatory="1"] .widget_options{
-	display: none;
-}
-.dashboard-container.readonly .dashboard-widget-menu, .dashboard-container.readonly .widget_options,.dashboard-container.readonly .widget_resize{
-	display: none;
-}
-.dashboard-container.readonly .widget_header{
-	cursor:default;
-}
-#dashboardForm #icon{
-	font-family:"Font Awesome 5 Free";
-}
-#widget-list{
-	width: 100%;
-    display: block;
-    overflow-y: auto;
-    padding: 5px;
-}
-#dashboard{
-	list-style-type:none;
-	margin:0;
-	padding:0;
-}
-#dashboard > li{
-	float:left;
-}
-#dashboard-view{
-	width: 50px;
-    background-color: #343a40;
-    list-style-type: none;
-    color: #505050;
-    margin: 0;
-    padding: 0;
-    min-height: 100%;
-    position: fixed;
-    left: 0;
-    top: 50px;
-    border-top: 4px solid #454a4e;
-}
-#dashboard-view li{
-	cursor:pointer;
-	text-align: center;
-    font-size: 1.3em;
-	transition:background 0.2s ease-in-out;
-}
-#dashboard-view li.dashboard-view-title{
-	color: #cecece;
-    font-size: 11px;
-    cursor: default;
-    background-color: #475058;
-    font-weight: bold;
-}
-#dashboard-view li.dashboard-item{
-	width: 50px;
-	position: relative;
-	padding: 10px;
-	transition: padding-bottom 0.2s ease-in-out;
-}
-#dashboard-view li.dashboard-item > div{
-	padding-top: 0.15rem;
-	display: flex;
-	width: 30px;
-	height: 30px;
-	z-index: 10;
-	background-color: #fafafa;
-	border-radius: 100%;
-	text-align: center;
-	border: 2px solid #fafafa;
-	transition: all 0.2s ease-in-out;
-}
-#dashboard-view li.dashboard-item i {
-	margin: auto;
-    font-size: 0.7em;
-    transition: all 0.2s ease-in-out;
-}
-#dashboard-view li.dashboard-item:hover{
-	padding-bottom: 20px;
-}
-#dashboard-view li.dashboard-item span {
-	transform: translateY(-25px);
-	font-size: 9px;
-	position: absolute;
-	left: 0;
-	bottom: 5px;
-	color: #808a94;
-	text-transform: uppercase;
-	display: block;
-	width: 100%;
-	text-align: center;
-	font-weight: bold;
-	overflow: hidden;
-	text-overflow: ellipsis;
-	opacity: 0;
-	z-index: 1;
-	transition: all 0.2s ease-in-out;
-}
-#dashboard-view li.dashboard-item:hover span {
-	transform: translateX(0px);
-	opacity: 1;
-}
-#dashboard-view li:hover{
-	background-color: #475058;
-	transition:
-		background-color 0.2s ease-in-out,
-		padding-bottom 0.2s ease-in-out;
-}
-#dashboard-view li[data-selected] > div{
-	background-color: #1d1d1d;
-    border-color: #1d1d1d;
-    border-top: 2px solid #dc3545;
-    color: #fafafa;
-}
-.widget_placeholder{
-	display: block;
-	width: auto;
-	background: rgba(0,0,0,0.1);
-	height: 335px;
-	margin-top: 20px;
-	border-radius: 10px;
-    border: 2px dashed #cecece;
-    transition: all 0.2s linear 0s;
-}
-.widget{
-	transition: width 0.2s linear,height 0.2s linear,opacity 0.2s linear;
-	/*display: table;*/
-	padding:20px;
-	transition: transform 0.2s ease-in-out,max-width 0.2s ease-in-out;
-}
-.widget.ui-sortable-helper{
-	transform: scale(1.05);
-}
-.widget_window{
-	border-radius: 10px;
-    box-shadow: 0 0 20px 0 #cecece!important;
-	padding: 0;
-	text-align: left;
-	transition: opacity 0.2s linear 0s;
-	z-index: 100;
-	position: relative;
-}
-.widget_resize{
-	position: absolute;
-	height: 100%;
-	cursor: ew-resize;
-	width:5px;
-	top:0px;
-	right:0px;
-	z-index: 100;
-}
-.widget_header{
-	background-color: #50597b;
-	padding: 5px;
-	border-radius: 10px 10px 0 0;
-	box-sizing: border-box;
-	color: #ffffff;
-	cursor: move;
-	transition:background 0.2s linear;
-}
-.widget_header > i {
-	display: inline-block;
-}
-.widget_options{
-	float: right;
-	margin: -5px 0 0 10px;
-	padding: 0;
-}
-.widget_options li{
-	cursor: pointer;
-	display: inline-block;
-	opacity: 0.5;
-	padding: 5px;
-	transition: opacity 0.2s linear 0s;
-	vertical-align: top;
-}
-.widget_options li:hover{
-	opacity: 1;
-}
-.widget_content{
-	background-color: #ffffff;
-	border-top:0;
-	overflow:auto;
-	border-radius: 0 0 10px 10px;
-	width:100%;
-	height:300px;
-	padding: 0px;
-}
-.widget_content::-webkit-scrollbar {
-	width: 6px;
-	height: 6px;
-}
-.widget_content::-webkit-scrollbar-button {
-	width: 0px;
-	height: 0px;
-}
-.widget_content::-webkit-scrollbar-thumb {
-	background: #cecece;
-	border: 0px none #ffffff;
-	border-radius: 50px;
-}
-.widget_content::-webkit-scrollbar-thumb:hover {
-	background: #707070;
-}
-.widget_content::-webkit-scrollbar-thumb:active {
-	background: #949494;
-}
-.widget_content::-webkit-scrollbar-track {
-	background: transparent;
-	border: 0px none #ffffff;
-	border-radius: 50px;
-}
-.widget_content::-webkit-scrollbar-track:hover {
-	background: #d4d4d4;
-}
-.widget_content::-webkit-scrollbar-track:active {
-	background: #d3d3d3;
-}
-.widget_content::-webkit-scrollbar-corner {
-	background: transparent;
-}
-.dashboard-container{
-	padding: 10px 0 0 0;
-}
-.dashboard-widget-menu{
-	margin: 0 0 0 15px;
-	padding: 0;
-}
-.dashboard-widget-menu li{
-	list-style-type: none;
-	color:#cecece;
-	display: inline-block;
-	cursor: pointer;
-	padding:5px;
-}
-.dashboard-widget-menu li {
-	opacity:0.5;
-	display: inline-block;
-	transition : all 0.2s ease-in-out;
-}
-.dashboard-widget-menu li:hover {
-	opacity:1;
-}
-.widgetDescription{
-	
-}
-.widgetColor{
-	display: inline-block;
-	vertical-align: top;
-	padding-right: 5px;
-	color:#333333;
-}
-.widgetColor small{
-	width:15px;
-	height:15px;
-	border-radius: 100%;
-	display: inline-block;
-	margin-right: 5px;
-	margin-top: 4px;
-	vertical-align: top;
-}
-@media only screen and (max-width: 500px) {
-	.widget_dropper{
-		display: none;
-	}
-	.widget,.widget_window{
-		display: block;
-		width:100%;
-	}
-}

+ 65 - 82
plugin/dashboard/dashboard.plugin.php

@@ -1,20 +1,36 @@
 <?php
-//Cette fonction va generer une page quand on clique sur dashboard dans menu
+
+
+
+//Déclaration d'un item de menu dans le menu principal
+function dashboard_menu(&$menuItems){
+	global $myUser;
+	if(!$myUser->can('dashboard','read')) return;
+	$menuItems[] = array(
+		'sort'=>3,
+		'url'=>'index.php?module=dashboard',
+		'label'=>'Dashboard',
+		'icon'=> 'far fa-object-ungroup',
+		'color'=> '#273c75'
+	);
+}
+
+//Cette fonction va generer une page quand on clique sur Dashboard dans menu
 function dashboard_page(){
-	global $_,$myUser,$conf;
-	if(isset($_['module']) || !$myUser->connected()) return;
-	$page = !isset($_['page']) ? 'home' : $_['page'];
+	global $_;
+	if(!isset($_['module']) || $_['module'] !='dashboard') return;
+	$page = !isset($_['page']) ? 'list.dashboard' : $_['page'];
+	$page = str_replace('..','',$page);
 	$file = __DIR__.SLASH.'page.'.$page.'.php';
 	if(!file_exists($file)) throw new Exception("Page ".$page." inexistante");
+	
 	require_once($file);
 }
 
 //Fonction executée lors de l'activation du plugin
 function dashboard_install($id){
 	if($id != 'fr.core.dashboard') return;
-	global $conf;
-	Entity::install(__DIR__);
-	$conf->put('dashboard_enable_sidebar',1);
+	Entity::install(__DIR__);	
 }
 
 //Fonction executée lors de la désactivation du plugin
@@ -24,22 +40,21 @@ function dashboard_uninstall($id){
 }
 
 //Déclaration des sections de droits du plugin
-//Déclaration des sections de droits du plugin
-Right::register('dashboard',array('label'=>'Gestion des droits sur le plugin dashboard'));
-
-
-//Comprends toutes les actions du plugin qui ne nécessitent pas de vue html
-require_once(__DIR__.SLASH.'action.php');
+Right::register('dashboard',array('label'=>'Gestion des droits sur le plugin Dashboard'));
 
 
+//cette fonction comprends toutes les actions du plugin qui ne nécessitent pas de vue html
+function dashboard_action(){
+	require_once(__DIR__.SLASH.'action.php');
+}
 //Déclaration du menu de réglages
 function dashboard_menu_setting(&$settingMenu){
-	global  $myUser;
+	global $myUser;
 	
 	if(!$myUser->can('dashboard','configure')) return;
-	$settingMenu[] = array(
+	$settingMenu[]= array(
 		'sort' =>1,
-		'url' => 'setting.php?section=dashboard',
+		'url' => 'setting.php?section=global.dashboard',
 		'icon' => 'fas fa-angle-right',
 		'label' => 'Dashboard'
 	);
@@ -48,87 +63,55 @@ function dashboard_menu_setting(&$settingMenu){
 //Déclaration des pages de réglages
 function dashboard_content_setting(){
 	global $_;
-	$_['section'] = str_replace('..', '', $_['section']);
-
 	if(file_exists(__DIR__.SLASH.'setting.'.$_['section'].'.php'))
 		require_once(__DIR__.SLASH.'setting.'.$_['section'].'.php');
 }
 
 
+require_once(__DIR__.SLASH.'action.php');
 
-function dashboard_default_widget(&$widgets){
-	global $myUser;
-	require_once(__DIR__.SLASH.'DashboardWidget.class.php');
-
-	$modelWidget = new DashboardWidget();
-	$modelWidget->model = 'clock';
-	$modelWidget->title = 'Horloge';
-	$modelWidget->icon = 'far fa-clock';
-	$modelWidget->background = '#212529';
-	$modelWidget->load = 'action.php?action=dashboard_widget_clock_load';
-	$modelWidget->js = [Plugin::url().'/js/progressbar.min.js?v=2',Plugin::url().'/js/widget.js?v=2'];
-	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
-	$modelWidget->description = "Affiche l'heure en temps réel";
-	$widgets[] = $modelWidget;
-
-	$modelWidget = new DashboardWidget();
-	$modelWidget->model = 'profile';
-	$modelWidget->title = 'Profil';
-	$modelWidget->minWidth = 2;
-	$modelWidget->maxWidth = 4;
-	$modelWidget->icon = 'far fa-user';
-	$modelWidget->background = '#007bff';
-	$modelWidget->load = 'action.php?action=dashboard_widget_profile_load';
-	$modelWidget->js = [Plugin::url().'/js/widget.js?v='.time()];
-	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
-	$modelWidget->configure = 'action.php?action=dashboard_widget_profile_configure';
-	$modelWidget->configure_callback = 'dashboard_widget_profile_configure_save';
-	$modelWidget->description = "Affiche les informations de profil";
-	$widgets[] = $modelWidget;
-
-	$modelWidget = new DashboardWidget();
-	$modelWidget->model = 'html';
-	$modelWidget->title = 'Texte / Code HTML';
-	$modelWidget->icon = 'fas fa-code';
-	$modelWidget->background = '#686de0';
-	$modelWidget->load = 'action.php?action=dashboard_widget_html_load';
-	$modelWidget->js = [Plugin::url().'/js/widget.js?v='.time()];
-	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
-	$modelWidget->configure = 'action.php?action=dashboard_widget_html_configure';
-	$modelWidget->configure_callback = 'dashboard_widget_html_configure_save';
-	$modelWidget->description = "Affiche un texte ou un morceau de code html intégré";
-	$widgets[] = $modelWidget;
-
-	$modelWidget = new DashboardWidget();
-	$modelWidget->model = 'log';
-	$modelWidget->title = 'Logs';
-	$modelWidget->defaultWidth = 8;
-	$modelWidget->options[] = array('function'=>'window.location = \'setting.php?section=log\';','icon'=>'fa-eye','title'=>'Voir tous les logs');
-	$modelWidget->icon = 'far fa-comment-dots';
-	$modelWidget->background = '#28a745';
-	$modelWidget->load = 'action.php?action=dashboard_widget_log_load';
-	$modelWidget->js = [Plugin::url().'/js/widget.js?v='.time()];
-	$modelWidget->css = [Plugin::url().'/css/widget.css?v='.time()];
-	$modelWidget->description = "Affiche les informations des 30 derniers logs";
-	if($myUser->can('log','read')) 
-		$widgets[] = $modelWidget;
+
+//Déclaration des settings de base
+//Types possibles : voir FieldType.class.php. Un simple string définit une catégorie.
+Configuration::setting('dashboard',array(
+    "Général",
+    //'dashboard_enable' => array("label"=>"Activer","type"=>"boolean"),
+));
+  
+function dashboard_application_bottom(){
+?>
+ <template id="dashboard-widget-template">
+        <div class="dashboard-widget" data-id="{{id}}">
+        	<div class="dashboard-widget-header">
+        		<div class="widget-header-icon"><i class="fas fa-question"></i></div>
+        		<div class="widget-header-title"></div>
+        		<ul class="widget-header-options"></ul>
+        	</div>
+        	<div class="dashboard-widget-content"></div>
+        	<div class="dashboard-widget-resize"><i class="fas fa-chevron-right"></i></div>
+    	</div>
+ </template>
+   <?php
 }
 
 
 
 //Déclation des assets
+
+
+
 Plugin::addCss("/css/main.css"); 
 Plugin::addJs("/js/main.js"); 
+Plugin::addCss("/css/component.css"); 
+Plugin::addJs("/js/dashboard.js"); 
+Plugin::addJs("/js/component.js"); 
 
 //Mapping hook / fonctions
 Plugin::addHook("install", "dashboard_install");
 Plugin::addHook("uninstall", "dashboard_uninstall"); 
-
-
-Plugin::addHook("page", "dashboard_page");  
-
-Plugin::addHook("menu_setting", "dashboard_menu_setting");    
-Plugin::addHook("content_setting", "dashboard_content_setting");   
-Plugin::addHook("widget", "dashboard_default_widget");
-
+Plugin::addHook("menu_main", "dashboard_menu"); 
+Plugin::addHook("page", "dashboard_page"); 
+Plugin::addHook("menu_setting", "dashboard_menu_setting");
+Plugin::addHook("content_setting", "dashboard_content_setting");
+Plugin::addHook("application_bottom", "dashboard_application_bottom");
 ?>

+ 100 - 0
plugin/dashboard/js/component.js

@@ -0,0 +1,100 @@
+
+function init_components_dashboard(input){
+
+	var data = $.extend({
+		column : 8,
+		line : 8,
+		onMove : 'dashboard_widget_move',
+		onRemove : 'dashboard_widget_delete',
+		onResize : 'dashboard_widget_resize',
+		onAdd : 'dashboard_widget_add'
+	},{
+		column : input.attr('data-column'),
+		line : input.attr('data-line'),
+		scope : input.attr('data-scope'),
+		uid : input.attr('data-uid'),
+		id : input.attr('data-id'),
+	});
+
+
+	component = input.data('component');
+	if(!component){
+		component = new Dashboard({
+			element : input,
+			columnNumber : data.column,
+			lineNumber : data.line
+		});
+		input.data('component',component);
+	}
+
+	
+	if(data.scope){
+		$.action({
+			action : 'dashboard_widget_search',
+			scope : data.scope,
+			uid : data.uid
+		},function(response){
+			component.addWidgets(response.widgets);
+		});
+	}
+
+	/*component.addWidgets([{
+		width : 1,
+		height: 1,
+		row: 3,
+		column: 5,
+		id: 1,
+		icon : 'far fa-user',
+		headerBackground : 'rgb(0, 123, 255)',
+		label : 'Hello monde',
+		content : '<p>Wazup ?</p>'
+	},
+	{
+		width : 3,
+		height: 3,
+		row: 0,
+		column: 1,
+		id: 2,
+		removable : false,
+		resizeable : false,
+		draggable : false,
+		label : 'ByBy',
+		content : '<p>Wazup 2 ?</p>'
+	}]);*/
+
+	$('.btn-widget-add').click(function(){
+
+		$.action({
+			action : data.onAdd,
+			scope : data.scope,
+			uid : data.uid
+		},function(widget){
+			component.addWidgets([widget]);
+		});
+		
+	});
+	
+	component.on('move',function(widget){
+		console.log('Widget has moved',widget);
+		$.action({
+			action : data.onMove,
+			widget : widget
+		});
+		
+	}).on('resize',function(widget){
+		console.log('Widget has resized',widget);
+		$.action({
+			action : data.onResize,
+			widget : widget
+		});
+	}).on('delete',function(widget){
+		console.log('Widget has removed',widget);
+		$.action({
+			action : data.onRemove,
+			widget : widget
+		});
+	});
+
+
+}
+

+ 222 - 0
plugin/dashboard/js/dashboard.js

@@ -0,0 +1,222 @@
+class Dashboard {
+  constructor(options) {
+    this.options = $.extend({
+			columnNumber : 1,
+			lineNumber : 1,
+			placeHolderPadding : 10
+	},options);
+    this.widgets = {};
+
+    var grid  = '<div class="dashboard-grid" >';
+	for(var u =0 ; u<options.lineNumber;u++){
+		for(var i =0 ; i<options.columnNumber;i++){
+			grid += '<div data-row="'+u+'" data-column="'+i+'" style="grid-row: '+(u+1)+" / "+(u+1)+";grid-column: "+(i+1)+" / "+(i+1)+';padding:'+this.options.placeHolderPadding+'px;" class="dashboard-placeholder"></div>';
+		}
+	}
+	grid  += '</div>';
+	this.grid = $(grid);
+	this.options.element.addClass('dashboard-container');
+	this.options.element.css({width:'100%',height:'1000px'});
+	this.options.element.append(this.grid);
+	this.widgetTemplate = $('#dashboard-widget-template').html();
+	this.placeHolders = $('.dashboard-placeholder');
+	this.triggers = [];
+	
+
+	var object = this;
+
+	this.placeHolders.droppable({
+	  tolerance : 'pointer',
+      classes: {
+        "ui-droppable-hover": 'drag-over'
+      },
+      drop: function( event, ui ) {
+      	var data = $(this).data();
+        var widgetId = ui.draggable.attr('data-id');
+        object.widgets[widgetId].column = data.column;
+        object.widgets[widgetId].row = data.row;
+        object.renderPositions();
+        object.trigger('move',object.widgetToArray(object.widgets[widgetId]));
+      }
+    });
+
+	$(window).resize(function() {
+	  	object.renderPositions();
+	});
+
+	$(window).mousemove(function(e){
+		if(!object.resizing) return;
+		$('.dashboard-widget').css('z-index','');
+		object.resizing.element.css('z-index',1000);
+		var objectPosition = object.resizing.element.position();
+		object.resizing.element.css('width',(e.clientX - objectPosition.left)+'px');
+		object.resizing.element.css('height',(e.clientY - objectPosition.top - 50)+'px');
+
+	});
+	$(window).mouseup(function(){
+		if(!object.resizing) return;
+		$('.dashboard-placeholder').removeClass('bordered');
+		var firstPlaceHolder = object.placeHolders.eq(0);
+		var placeholderWidth = firstPlaceHolder.outerWidth();
+		var placeholderHeight = firstPlaceHolder.outerHeight();
+		
+		object.widgets[object.resizing.id].width = Math.round(object.resizing.element.outerWidth() / placeholderWidth);
+		object.widgets[object.resizing.id].height = Math.round(object.resizing.element.outerHeight() / placeholderHeight);
+		object.renderPositions();
+		object.trigger('resize',object.widgetToArray(object.widgets[object.resizing.id]) );
+		object.resizing = null;
+	});
+
+  }
+
+  on(event,callback){
+  	if(!this.triggers[event]) this.triggers[event] = [];
+  	this.triggers[event].push(callback);
+  	return this;
+  }
+  trigger(event,data){
+  	if(!this.triggers[event]) return;
+  	if(!data) data = {};
+  	var abort = false;
+  	for(var k in this.triggers[event]){
+  		if(this.triggers[event][k](data) === true) abort = true;
+  	}
+  	return abort;
+  }
+
+  //Ajoute un ou plusieurs widgets
+  addWidgets(widgets){
+  	var object = this;
+  	for(var k in widgets){
+  		var widgetOptions = $.extend({
+  			id : Math.floor(Math.random() * 100)+Date.now(),
+  			draggable : true,
+  			resizeable : true,
+  			removable : true,
+  			width: 1,
+  			height : 1,
+  			row : 0,
+  			column :0,
+  			options : [],
+  			label : 'Sans titre',
+  			content : "Aucun contenu"
+  		},widgets[k]);
+	  
+
+  		widgetOptions.options.push({icon : 'fa fa-times', class:'widget-option-delete' , label : 'Supprimer' , click : function(element){ object.deleteWidget($(element).closest('.dashboard-widget').attr('data-id')); }});
+
+	  	var widgetElement = $(object.widgetTemplate);
+	  	widgetOptions.element = widgetElement;
+	  	object.widgets[widgetOptions.id] = widgetOptions;
+	  	object.grid.append(widgetElement);
+	  	
+		 	widgetElement.draggable(
+		 		{ 
+		 			handle: '.widget-header-title', 
+		 			containment: object.grid,
+		 			"ui-draggable-dragging" : "widget-dragging",
+		 			start : function(event){
+		 				
+		 				var element = $(event.currentTarget);
+		 				if(!element.hasClass('widget-draggable'))return false;
+
+		 				$('.dashboard-widget').css('z-index','');
+		 				element.css('z-index',1000);
+		 			}
+		 		}
+		 	);
+		 	widgetElement.find('.dashboard-widget-resize').mousedown(function(){
+		 		var element = $(this).closest('.dashboard-widget');
+		 		if(!element.hasClass('widget-resizeable'))return;
+		 		object.resizing = object.widgets[element.attr('data-id')];
+		 		$('.dashboard-placeholder').addClass('bordered');
+		 	});
+		 	widgetElement.find('.widget-option-delete').click(function(){
+		 		object.deleteWidget($(this).closest('.dashboard-widget').attr('data-id'));
+		 	});
+
+		 	object.trigger('add',object.widgetToArray(widgetOptions));
+	 	}
+
+
+  	this.renderContents();
+  	this.renderPositions();
+
+  	
+  }
+
+  //Supprime un widget en fonctiond e son id
+  deleteWidget(id){
+  	var widget = this.widgets[id];
+  	if(this.trigger('delete',this.widgetToArray(widget))) return;
+  	this.widgets[id].element.remove();
+  }
+
+  widgetToArray(widget){
+  	if(!widget) return {};
+  	return {
+		    id: widget.id,
+		    draggable: widget.draggable,
+		    resizeable: widget.resizeable,
+		    removable: widget.removable,
+		    width: widget.width,
+		    height: widget.height,
+		    row: widget.row,
+		    column: widget.column,
+		    label: widget.label,
+		    content: widget.content,
+		    icon: widget.icon,
+		    headerBackground: widget.headerBackground
+		};
+  }
+
+  //rafraichis le contenu des widgets en fonction du tableaud e widgets interne
+  renderContents() {
+	for(var k in this.widgets){
+		var widget = this.widgets[k];
+		if(widget.id) widget.element.attr('data-id',widget.id);
+		if(widget.label !== null) widget.element.find('.widget-header-title').html(widget.label);
+		if(widget.icon !== null) widget.element.find('.widget-header-icon i').attr('class',widget.icon);
+		if(widget.options !== null){
+			var tpl = '<li class="{{class}}" title="{{label}}"><i class="{{icon}}"></i></li>';
+			widget.element.find('.widget-header-options').html('');
+			for(var u in widget.options){
+				var option = $(Mustache.render(tpl,widget.options[u]));
+				
+				if(widget.options[u].click){
+					option.data('click',widget.options[u].click);
+					option.click(function(){
+						$(this).data('click')(this);
+					});
+				}
+				widget.element.find('.widget-header-options').append(option);
+			}
+		}
+		if(widget.headerBackground !== null) widget.element.find('.dashboard-widget-header').css('backgroundColor',widget.headerBackground);
+		widget.element
+		.toggleClass('widget-draggable',widget.draggable)
+		.toggleClass('widget-resizeable',widget.resizeable)
+		.toggleClass('widget-removable',widget.removable);
+		if(widget.content !== null) widget.element.find('.dashboard-widget-content').html(widget.content);
+	}
+  }
+
+  //resize / replace les wicgets en fonction de la taille de la fenetre et des cellules de grilles
+  renderPositions() {
+	var firstPlaceHolder = this.placeHolders.eq(0);
+	var placeholderWidth = firstPlaceHolder.outerWidth();
+	var placeholderHeight = firstPlaceHolder.outerHeight();
+
+	for(var k in this.widgets){
+		var widget = this.widgets[k];
+		var cell = $('[data-row="'+widget.row+'"][data-column="'+widget.column+'"]').position();
+		if(!cell) continue;
+		widget.element.css({
+			width  : ((widget.width * placeholderWidth)-(this.options.placeHolderPadding*2))+'px',
+			height : ((widget.height * placeholderHeight)-(this.options.placeHolderPadding*2))+'px',
+			top : (cell.top+this.options.placeHolderPadding)+'px',
+			left : (cell.left+this.options.placeHolderPadding)+'px'
+		});
+	}
+  }
+}

+ 26 - 490
plugin/dashboard/js/main.js

@@ -1,522 +1,58 @@
-//handle target
-var target = false;
-var refreshInterval = null;
-var resizeTarget = false;
-
 //CHARGEMENT DE LA PAGE
-$(function(){
-
-	if($.urlParam('section') == 'list.widget.share') dashboard_widget_share_search();
-	if(($.page()!='' && $.page()!='index') || $.urlParam('module')!=null) return;
-	dashboard_dashboardwidget_search();
-		
-	$('#dashboard-view li').click(function(){
-		$('#dashboard-view li').removeAttr('data-selected');
-		$(this).attr('data-selected',1);
-		dashboard_dashboardwidget_search();
-	});
-	$('#widget-list').change(function(){
-		var option = $('#widget-list option:selected').data();
-		option.text = $('#widget-list option:selected').text();
-
-		var description = $('.widgetDescription');
-		description.removeClass('hidden');
-		$('h3 span', description).text($(this).val() == '' ? 'Sélectionnez un widget' : option.text);
-		$('h3 i', description).attr('class',option.icon);
-		$('p', description).text(option.description);
-		$('.widgetWidth', description).html(option.width=='' ? '' : '<span class="text-muted font-weight-bold mr-2">LARGEUR</span> '+Math.round(option.width*100 /12)+'%');
-		$('.widgetColor', description).html(option.background=='' ? '' : '<span class="text-muted font-weight-bold  mr-2">COULEUR</span> <small style="background-color:'+option.background+'"></small><span>'+option.background+'</span>   ');
-	});
-
-	if(!$('.dashboard-container').hasClass('readonly')){
-		$('#dashboard').sortable({
-			distance: 15,
-			cancel :'[data-mandatory="1"]',
-			tolerance: "pointer",
-			handle: ".widget_header",
-			opacity: 0.8,
-			placeholder: {
-				element: function(clone, ui) {
-					var classes = clone.attr('class').split(' ');
-					for(var key in classes){
-						var number = classes[key].match(/col-xl-([0-9])/i);
-						if(number != null)
-							colSize = number[1] > 9 ? 9 : number[1];
-					}
-	                return $('<div class="ui_sortable_placeholder widget_placeholder col-xl-'+colSize+' col-md-'+colSize*2+' col-lg-'+colSize*2+'"></div>');
-	            },
-	            update: function() {
-	                return;
-	            }
-			},
-			update:function(event,ui){
-				var data = dashboard_dashboardwidget_save_position();
-			}
-		});
-		$( ".widget" ).disableSelection();
+function init_plugin_dashboard(){
+	switch($.urlParam('page')){
+		default:
+		break;
 	}
-});
+	
+}
 
 
-function init_setting_dashboard(){
-	dashboard_dashboard_search();
-	dashboard_widget_share_search();
-}
 
 //Enregistrement des configurations
 function dashboard_setting_save(){
 	$.action({ 
-		action : 'dashboard_setting_save', 
-		fields :  $('#dashboard-setting-form').toJson() 
+		action: 'dashboard_setting_save', 
+		fields:  $('#dashboard-setting-form').toJson() 
 	},function(){
 		$.message('success','Enregistré');
 	});
 }
 
+//** BLOC DE TABLEAU DE BORD **/
+//Récuperation d'une liste  bloc de tableau de bord dans le tableau #dashboardwidgets
+function dashboard_dashboardwidget_search(callback,exportMode){
+	var box = new FilterBox('#dashboard_dashboardwidget-filters');
+	if(exportMode) $('.btn-export').addClass('btn-preloader');
 
-/** DASHBOARD **/
-//Récuperation d'une liste de dashboard dans le tableau #dashboards
-function dashboard_dashboard_search(callback){
-	$('#dashboards').fill({
-		action:'dashboard_dashboard_search'
-	},function(){
+	$('#dashboardwidgets').fill({
+		action:'dashboard_dashboardwidget_search',
+		filters: box.filters(),
+		sort: $('#dashboardwidgets').sortable_table('get'),
+		export:  !exportMode ? false : exportMode
+	},function(response){
+		if(!exportMode) $('.results-count > span').text(response.pagination.total);
 		if(callback!=null) callback();
 	});
 }
 
-//Ajout ou modification d'élément dashboard
-function dashboard_dashboard_save(){
-	var form = $('#dashboard-form');
-	var data = form.toJson();
-
-	$.action(data,function(r){
-		form.attr('data-id','');
-		reset_inputs(form);
-		$('#icon').val('far fa-bookmark');
-		init_components(form);
-		
-		$.message('success','Enregistré');
-		
-		dashboard_dashboard_search();
-	});
-}
-
-//Récuperation ou edition d'élément dashboard
-function dashboard_dashboard_edit(element){
-	var line = $(element).closest('tr');
-
-	$.action({
-		action:'dashboard_dashboard_edit',
-		id:line.attr('data-id')
-	},function(r){
-		$.setForm('#dashboard-form',r);
-		$('#dashboard-form').attr('data-id',r.id);
-		init_components('#dashboard-form');
-	});
-}
-
-//Suppression d'élement dashboard
-function dashboard_dashboard_delete(element){
-	if(!confirm('Êtes-vous sûr de vouloir supprimer ce dashboard ?')) return;
-	var line = $(element).closest('tr');
-
-	$.action({
-		action : 'dashboard_dashboard_delete',
-		id : line.attr('data-id')
-	},function(r){
-		line.remove();
-		$.message('info','Dashboard supprimé');
-	});
-}
-
-/** DASHBOARDWIDGET **/
-function configure_widget(element){
-	var widget = $(element).closest('.widget');
-	var configureUrl = widget.attr('data-configure');
-	
-	if(configureUrl.substring(0,11) == 'setting.php'){
-		window.open(configureUrl); 
-	}else{
-		var modal = $('#configureWidgetModal');
-		modal.attr('data-widget',widget.attr('data-id')).modal('show');
-		$('.pluginContent', modal).load(widget.attr('data-configure'),{
-			id : widget.attr('data-id')
-		},function(){
-			init_components();
-			var callback = widget.attr('data-configure-init');
-			if(window[callback]!=null) window[callback](widget,modal);
-		});
-	}
-}
-
-function dashboard_dashboardwidget_save_configuration(){
-	var modal = $('#configureWidgetModal');
-	var widget = $('.widget[data-id="'+modal.attr('data-widget')+'"]');
-
-	modal.modal('hide');
-	
-	var callback = widget.attr('data-configure-callback');
-	if(window[callback]!=null) window[callback](widget,modal);
-}
-
-
-//Récuperation d'une liste de dashboardwidget dans le tableau #dashboardwidgets
-function dashboard_dashboardwidget_search(callback){
-	var dashboard = $('#dashboard-view li[data-selected]').attr('data-id');
-	if(!dashboard || dashboard=='') return;
-	$.action({
-		action : 'dashboard_dashboardwidget_search',
-		dashboard : dashboard
-	},function(r){
-		$('#dashboard .widget:visible').remove();
-
-		for(var i in r.rows){
-			var widget = r.rows[i];
-			dashboard_dashboardwidget_append(widget,function(widget){
-				dashboard_dashboardwidget_load(widget);
-			});
-		}
-		clearInterval(refreshInterval);
-		refreshInterval = setInterval(function(){
-			dashboard_dashboardwidget_refresh();
-		},3000);
-
-		if(!$('.dashboard-container').hasClass('readonly')){
-			//Gestion du resize de largeur
-			$('.widget_resize').draggable({
-				axis: "x",
-				cancel :'[data-mandatory="1"]',
-				start : function(event,ui){
-					var widget = ui.helper.closest('.widget');
-					$('.widget_content',widget).append('<div class="readonly-veil"></div>');
-				},
-				drag: function(event,ui) {
-					var widget = ui.helper.closest('.widget');
-				    var width = widget.attr('data-width');
-				    var percent = ui.position.left * 100 / ui.originalPosition.left;
-				    var newWidth = Math.round(width * percent/100);
-				    newWidth = newWidth<1 ? 1 : newWidth;
-				    newWidth = newWidth>12 ? 12 : newWidth;
-
-				    //Gestion taille widget max
-				    if(widget.attr('data-maxWidth') && widget.attr('data-maxWidth').length){
-				    	maxWidth = widget.attr('data-maxWidth');
-					    newWidth = newWidth>maxWidth ? maxWidth : newWidth;
-				    }
-				    //Gestion taille widget min
-			        if(widget.attr('data-minWidth') && widget.attr('data-minWidth').length){
-			        	minWidth = widget.attr('data-minWidth');
-					    newWidth = newWidth<minWidth ? minWidth : newWidth;
-			        }
-	
-				    widget.removeClass('col-xl-1 col-xl-2 col-xl-3 col-xl-4 col-xl-5 col-xl-6 col-xl-7 col-xl-8 col-xl-9 col-xl-10 col-xl-11 col-xl-12');
-				    widget.addClass('col-xl-'+newWidth);
-
-				    widget.removeClass('col-lg-1 col-lg-2 col-lg-3 col-lg-4 col-lg-5 col-lg-6 col-lg-7 col-lg-8 col-lg-9 col-lg-10 col-lg-11 col-lg-12');
-				    widget.addClass('col-lg-'+newWidth*2);
-
-				    widget.removeClass('col-md-1 col-md-2 col-md-3 col-md-4 col-md-5 col-md-6 col-md-7 col-md-8 col-md-9 col-md-10 col-md-11 col-md-12');
-				    widget.addClass('col-md-'+newWidth*2);
-	
-				},
-				stop: function(event,ui){
-					var resizebar = ui.helper;
-					var widget = resizebar.closest('.widget');
-					$('.widget .readonly-veil').remove();
-					var width = widget.attr('data-width');
-					var newWidth = widget.attr('class').match(/col-xl-([0-9]*)/)
-					newWidth = newWidth[1];
-					
-					if(newWidth!=width){
-					    $.action({
-							action : 'dashboard_dashboardwidget_resize',
-							widget : widget.attr('data-id'),
-							width: newWidth
-						},function(r){
-							widget.attr('data-width',newWidth);
-							
-						});
-					}
-					resizebar.removeAttr('style');
-				}
-			});
-		}
-	});
-}
-
-function dashboard_dashboardwidget_refresh(){
-	$.action({
-		action : 'dashboard_dashboardwidget_refresh',
-		dashboard : $('#dashboard-view li[data-selected]').attr('data-id')
-	},function(r){
-
-		for(var id in r.rows){
-			var widget = r.rows[id];
-
-			if(widget.widget){
-				var header = $('.widget[data-id="'+id+'"]').find('.widget_header'); 
-				if(widget.widget.title) header.find('span').text(widget.widget.title);
-				if(widget.widget.icon) header.find('i').attr('class','fa '+widget.widget.icon);
-				if(widget.widget.background) header.css('backgroundColor',widget.widget.background);
-			}
-
-			if(widget.callback){
-				if(window[widget.callback]!=null) window[widget.callback]($('.widget[data-id="'+id+'"]'),widget.data);
-			}
-			
-		}
-	});
-}
-
-//Mise à jour des infos d'un élement widget à partir d'un object data
-function dashboard_dashboardwidget_render(widget,data){
-	widget.attr('data-id',data.id);
-	widget.removeClass (function (index, css) {
-		return (css.match (/(^|\s)col-xl-\S+/g) || []).join(' ');
-	});
-	var width = data.width!=0 ? data.width : data.defaultWidth; 
-
-	widget.attr('data-id',data.id)
-		.attr('data-load',data.load)
-		.attr('data-configure',data.configure)
-		.attr('data-configure-callback',data.configure_callback)
-		.attr('data-configure-init',data.configure_init)
-		.attr('data-delete',data.delete)
-		.attr('data-mandatory',data.mandatory && data.mandatory==1 ?1:0)
-		.attr('data-model',data.model)
-		.attr('data-width',width)
-		.addClass('col-xl-'+width)
-		.addClass('col-lg-'+width*2)
-		.addClass('col-md-'+width*2)
-		.find('.widget_header')
-		.css('background',data.background)
-		.find('i:eq(0)').attr('class',data.icon);
-
-	if(data.minWidth && data.minWidth!=0) widget.attr('data-minWidth', data.minWidth);
-	if(data.maxWidth && data.maxWidth!=0) widget.attr('data-maxWidth', data.maxWidth);
-
-	widget.find('.widget_header span:eq(0)').html(data.title);
-	widget.find('.widget_content').html(data.content);
-		
-	var options = '';
-	for(var k in data.options){
-		var option = data.options[k];
-		options+='<li onclick="'+option.function+'" '+(option.title ? 'title="'+option.title+'"':'')+'><i class="fa '+option.icon+'"></i> '+(option.label ? option.label:'')+'</li>';
-	}
-	if(data.configure) options+="<li title='Configurer' onclick='configure_widget(this);'><i class='fa fa-wrench'></i></li>";
-	options+="<li title='Supprimer' onclick='dashboard_dashboardwidget_delete(this);'><i class='fa fa-times'></i></li>";
-
-	widget.find('.widget_options').html(options);
-	widget.data('data',data);
-	widget.removeClass('hidden');
-
-	return widget;
-}
-
-//Modification d'un widget existant
-function dashboard_dashboardwidget_update(data){
-	var widget = $('.widget[data-id="'+data.id+'"]');
-	var data = $.extend(widget.data('data'), data);
-	dashboard_dashboardwidget_render(widget,data);
-}
-
-//Ajout ou modification d'élément dashboardwidget
+//Ajout ou modification bloc de tableau de bord
 function dashboard_dashboardwidget_save(){
 	var data = $('#dashboardwidget-form').toJson();
 	$.action(data,function(r){
-		$('#dashboardwidget-form').attr('data-id','');
-		dashboard_dashboardwidget_search();
-		$.message('success','Enregistré');
-	});
-}
-
-//Chargement du contenu php du widget
-function dashboard_dashboardwidget_load(widget){
-	
-	$.getJSON(widget.load,{
-		id : widget.id,
-		model : widget.model,
-		content:'',
-	},function(r){
-		if(r.error && r.error.code == 403){
-			$('.widget[data-id="'+widget.id+'"]').remove();
-			return;
-		}
-		dashboard_dashboardwidget_update(r);
-		var data = $.extend($('.widget[data-id="'+widget.id+'"]').data('data'), r.widget);
-
-		var init = 'widget_'+data.model.replace(/[^a-z0-9]/i,'_')+'_init';
-		if(window[init]!=null) window[init]();
-	});
-}
-
-//Ajout (manuel par l'user) d'un widget
-function dashboard_dashboardwidget_add(){
-	$.action({
-		action : 'dashboard_dashboardwidget_add',
-		dashboard : $('#dashboard-view li[data-selected]').attr('data-id'),
-		widget : $('#widget-list').val()
-	},function(r){
-		if(r.message) $.message('info',r.message);
-		$('#dashboard_dashboardwidget_appendModal').modal('hide');
-		dashboard_dashboardwidget_search();
-	});
-}
-
-//Ajout (depuis le code) d'un widget
-function dashboard_dashboardwidget_append(data,callback){
-	var tpl = $('#dashboard .widget:hidden').get(0).outerHTML;
-	var widget = $(tpl);
-	$('#dashboard').append(widget);
-	if(data.css!=null){
-		for(k in data.css){
-			var css = data.css[k];
-			if($('link[href="'+css+'"]').length!=0) continue;
-
-			//on supprime tout autre css ayant la même base mais des versions plus vielles
-			var baseCss = css.replace(/\?v=.*/gm,'');
-			$('link[href^="'+baseCss+'"]').remove();
-
-			var cssFile = document.createElement('link');
-			cssFile.setAttribute("rel","stylesheet");
-			cssFile.setAttribute("type","text/css");
-			cssFile.setAttribute("href", css);
-			document.getElementsByTagName("body")[0].appendChild(cssFile);
-		}
-	}
-	dashboard_dashboardwidget_render(widget,data);
-	if(data.js!=null){
-		dashboard_load_js(data.js,0,function(){
-			if(callback) callback(data);
-		});
-	}else{
-		if(callback) callback(data);
-	}
-	
-	
-}
-
-function dashboard_load_js(files,iteration,callback){
-	var js = files[iteration];
-
-	if($('script[src="'+js+'"]').length!=0 || js==null) {
-		if(files.length > iteration) dashboard_load_js(files,iteration+1,callback);
-		if((files.length-1) == iteration) if(callback) callback();
-		return;
-	}
-	//on supprime tout autre js ayant la même base mais des versions plus vielles
-	var baseJs = js.replace(/\?v=.*/gm,'');
-	$('script[src^="'+baseJs+'?"]').remove();
-
-	var jsFile = document.createElement('script');
-	jsFile.setAttribute("type","text/javascript");
-	document.getElementsByTagName("body")[0].appendChild(jsFile);
-	jsFile.onload = function() {
-		if(files.length > iteration) dashboard_load_js(files,iteration+1,callback);
-		if((files.length-1) == iteration) if(callback) callback();
-	};
-	jsFile.src =  js;
-}
-
-//Récuperation ou edition d'élément dashboardwidget
-function dashboard_dashboardwidget_edit(widget){
-	var line = $(element).closest('tr');
-	$.action({action:'dashboard_dashboardwidget_edit',id:line.attr('data-id')},function(r){
-		$.setForm('#dashboardwidget-form',r);
 		$('#dashboardwidget-form').attr('data-id',r.id);
-	});
-}
-
-//Suppression d'élement dashboardwidget
-function dashboard_dashboardwidget_delete(element){
-	var element = $(element).closest('.widget');
-	var data = element.data('data');
-	
-	$.action({
-		action : 'dashboard_dashboardwidget_delete',
-		dashboard : $('#dashboard-view li[data-selected]').attr('data-id'),
-		widget : data.id,
-	},function(r){
-		element.remove();
-		if(r.message) $.message('info',r.message);
-		else $.message('info', 'Widget supprimé');
-		if(data.delete != null){
-			$.getJSON(data.delete,$.extend(data,{content:''}));
-		}
-	});
-}
-
-
-
-//Enregistrement de toutes les positions de widget
-function dashboard_dashboardwidget_save_position(){
-	var positions = [];
-	$('.widget:visible').each(function(i,element){
-		positions.push({id:$(element).attr('data-id'),position:$(element).index()});
-	});
-	
-	$.action({
-		action : 'dashboard_dashboardwidget_save_position',
-		dashboard : $('#dashboard-view li[data-selected]').attr('data-id'),
-		positions : positions,
-	},function(r){
-		
-	});
-
-}
-
-
-/** DASHBOARDWIDGETSHARE **/
-//Récuperation d'une liste de dashboardwidgetshare dans le tableau #dashboardwidgetshares
-function dashboard_widget_share_search(callback){
-	$('#dashboard-widget-shares').fill({
-		action: 'dashboard_widget_share_search'
-	},function(response){
-		if(callback!=null) callback();
-	});
-}
-
-//Ajout ou modification d'élément dashboardwidgetshare
-function dashboard_widget_share_save(){
-	var data = $('#dashboard-widget-share-form').toJson();
-	var target = $('#uid').data('values');
-
-	if(target){
-		data.entity = target[0].entity;
-		data.uid = target[0].uid;
-	}
-
-	$.action(data,function(r){
-		$('#dashboard-widget-share-form').attr('data-id','');
-		$('#dashboard-widget-share-form').clear();
-		dashboard_widget_share_search();
-		
+		$.urlParam('id',r.id);
 		$.message('success','Enregistré');
 	});
 }
 
 
-//Récuperation ou edition d'élément dashboardwidgetshare
-function dashboard_widget_share_edit(element){
-	var line = $(element).closest('tr');
-
-	$.action({
-		action: 'dashboard_widget_share_edit',
-		id: line.attr('data-id')
-	},function(r){
-		$.setForm('#dashboard-widget-share-form',r);
-		init_components('#dashboard-widget-share-form');
-		$('#dashboard-widget-share-form').attr('data-id',r.id);
-	});
-}
-
-//Suppression d'élement dashboardwidgetshare
-function dashboard_widget_share_delete(element){
+//Suppression bloc de tableau de bord
+function dashboard_dashboardwidget_delete(element){
 	if(!confirm('Êtes vous sûr de vouloir supprimer cet item ?')) return;
-	var line = $(element).closest('tr');
-	
+	var line = $(element).closest('.item-line');
 	$.action({
-		action: 'dashboard_widget_share_delete',
+		action: 'dashboard_dashboardwidget_delete',
 		id: line.attr('data-id')
 	},function(r){
 		line.remove();

+ 13 - 0
plugin/dashboard/page.list.test.php

@@ -0,0 +1,13 @@
+ <?php
+global $myUser;
+User::check_access('dashboard','read');
+require_once(__DIR__.SLASH.'Dashboard.class.php');
+?>
+<div class="plugin-dashboard">
+    <div class="row">
+        <div class="col-md-12">
+            <div class="btn btn-light btn-widget-add mb-1"><i class="fas fa-plus"></i> Widget</div>
+            <div data-type="dashboard" data-scope="home" data-uid="<?php echo $myUser->login ?>"></div>
+        </div>
+    </div>
+</div>