Browse Source

pluginisation

idleman 6 years ago
parent
commit
b23e6ee617
18 changed files with 588 additions and 147 deletions
  1. 3 1
      .gitignore
  2. 36 1
      action.php
  3. 164 0
      class/Plugin.class.php
  4. 4 1
      common.php
  5. 6 4
      constant.php
  6. 12 0
      css/main.css
  7. 91 0
      dashboard.php
  8. 48 6
      header.php
  9. 4 131
      index.php
  10. 2 1
      install.php
  11. 29 1
      js/main.js
  12. 5 1
      js/plugins.js
  13. 38 0
      plugin.php
  14. 28 0
      plugin/modele/Voiture.class.php
  15. 9 0
      plugin/modele/app.json
  16. 4 0
      plugin/modele/main.css
  17. 5 0
      plugin/modele/main.js
  18. 100 0
      plugin/modele/modele.plugin.php

+ 3 - 1
.gitignore

@@ -1,2 +1,4 @@
 /upload/*
-!/upload/.htaccess
+!/upload/.htaccess
+/plugin/enabled.json
+/database/.db

+ 36 - 1
action.php

@@ -314,6 +314,41 @@ switch ($_['action']){
 		Type::fromFileImport($_FILES['file'],$sketch,$type);
 		
 	break;
+
+	// PLUGINS
+	case 'search_plugin':
+	Action::write(function($_,&$response){
+		global $myUser;
+		if( $myUser->rank != 'ADMIN' ) throw new Exception("Permissions insuffisantes");
+		foreach(Plugin::getAll() as $plugin){
+			$plugin->folder = array('name'=>$plugin->folder,'path'=>$plugin->path());
+			$response['rows'][] = $plugin;
+		}
+		
+	});
+	break;
+	
+	case 'change_plugin_state':
+	Action::write(function($_,&$response){
+		global $myUser;
+		if( $myUser->rank != 'ADMIN' ) throw new Exception("Permissions insuffisantes");
+		
+		$plugin = Plugin::getById($_['plugin']);
+		
+		if($_['state']){
+			$states = Plugin::states();
+			$missingRequire = array();
+			foreach($plugin->require as $require=>$version):
+				$req = Plugin::getById($require);
+			if($req == null || $req==false || !$req->state || $req->version!=$version)
+				$missingRequire[]= $require.' - '.$version;
+			endforeach;
+			if(count($missingRequire)!=0) throw new Exception("Plugins pré-requis non installés : ".implode(',',$missingRequire));
+		}
+		
+		Plugin::state($_['plugin'],$_['state']);
+	});
+	break;
 	
 	//COMPONENT
 
@@ -632,7 +667,7 @@ switch ($_['action']){
 	break;
 	
 	default:
-		
+		Plugin::callHook("action");  
 	break;
 }
 

+ 164 - 0
class/Plugin.class.php

@@ -0,0 +1,164 @@
+<?php
+
+/**
+* Manage plugin système trhought php hooks
+* @author valentin carruesco
+* @category Core
+* @license copyright
+*/
+
+class Plugin{
+	public $id,$name,$author,$link,$licence,$folder,$description,$version,$state,$type,$require;
+
+	
+	function __construct(){
+		$this->state = false;
+	}
+	public static function exist($id){
+		foreach(self::getAll() as $plugin)
+			if($plugin->id==$id) return true;
+
+		return false;
+	}
+	public static function addHook($hookName, $functionName){
+		$GLOBALS['hooks'][$hookName][] = $functionName;  
+	}
+	public static function callHook($hookName, $hookArguments = array()) {
+		if(!isset($GLOBALS['hooks'][$hookName])) return;
+		foreach($GLOBALS['hooks'][$hookName] as $functionName)
+			call_user_func_array($functionName, $hookArguments);  
+	}
+	
+	public static function includeAll(){
+		foreach(self::getAll() as $plugin):
+			if(!$plugin->state) continue;
+		$main = $plugin->path().SLASH.$plugin->folder.'.plugin.php';
+		if(file_exists($main))
+			require_once($main);
+		endforeach;
+	}
+	
+	public static function getAll(){
+		$enabled = self::states();
+		$plugins = array();
+		foreach(glob(__ROOT__.PLUGIN_PATH.'*'.SLASH.'app.json') as $file)
+			$plugins[] = self::parseManifest($file);
+
+		usort($plugins, function($a, $b){
+			if ($a->name == $b->name) 
+				$result = 0;
+
+			if($a->name < $b->name){
+				$result = -1;
+			} else{
+				$result = 1;
+			}
+			return  $result;
+		});
+		return $plugins;
+	}
+	
+	public static function getById($id){
+		$plugin = false;
+		foreach(self::getAll() as $onePlugin)
+			if($onePlugin->id==$id) $plugin = $onePlugin;
+		return $plugin;
+	}
+	
+	public static function parseManifest($file){
+		$enabled = self::states();
+		$plugin = new self();
+		$manifest = json_decode(file_get_contents($file),true);
+		if(!$manifest) return $plugin;
+		if(!isset($manifest['id']) || !isset($manifest['name']) ) continue;
+		$plugin->name = $manifest['name'];
+		$plugin->id = $manifest['id'];
+		$plugin->folder = basename(dirname($file));
+		if(in_array($plugin->id,$enabled)) $plugin->state = true;
+		if(isset($manifest['author'])) $plugin->author = $manifest['author'];
+		if(isset($manifest['url'])) $plugin->url = $manifest['url'];
+		if(isset($manifest['licence'])) $plugin->licence = $manifest['licence'];
+		if(isset($manifest['description'])) $plugin->description = $manifest['description'];
+		if(isset($manifest['version'])) $plugin->version = $manifest['version'];
+		if(isset($manifest['core'])) $plugin->core = $manifest['core'];
+		if(isset($manifest['require'])) $plugin->require = $manifest['require'];
+		return $plugin;
+	}
+	
+	public static function state($id,$state){
+		$enabled = self::states();
+		
+		$plugin = self::getById($id);
+		
+		$main = $plugin->path().SLASH.$plugin->folder.'.plugin.php';
+		if(file_exists($main))
+			require_once($main);
+		
+		
+		if($state==0){
+			$key = array_search($plugin->id, $enabled);
+			
+			if($key  !== false)
+				unset($enabled[$key]);
+			
+			plugin::callHook('uninstall',array($plugin->id));
+		}else{
+			if(!in_array($plugin->id,$enabled))
+				$enabled[] = $plugin->id;
+			plugin::callHook('install',array($plugin->id));
+		}
+		
+		self::states($enabled);
+	}
+	
+	public static  function states($states = false){
+		$enabledFile = __ROOT__.PLUGIN_PATH.'enabled.json';
+		if(!file_exists($enabledFile)) touch($enabledFile);
+		if(!is_array($states)){
+			$enabled = json_decode(file_get_contents($enabledFile),true);
+			return !is_array($enabled)?array():$enabled;
+		}
+		file_put_contents($enabledFile,json_encode($states));
+	}
+	
+	public function path(){
+		return __ROOT__.PLUGIN_PATH.$this->folder;
+	}
+	
+
+	public static function url(){
+		$bt =  debug_backtrace();
+		return ROOT_URL.SLASH.PLUGIN_PATH.basename(dirname($bt[0]['file']));
+	}
+
+
+	public static function addCss($css) {  
+		$bt =  debug_backtrace();
+		$GLOBALS['hooks']['css_files'][] = str_replace(__ROOT__,'',dirname($bt[0]['file'])).$css;  
+	}
+
+	public static function callCss(){
+		if(!isset($GLOBALS['hooks']['css_files'])) return '';
+		$stream = '';
+		foreach($GLOBALS['hooks']['css_files'] as $css_file) 
+			$stream .='<link rel="stylesheet" href="'.$css_file.'">'."\n";
+		return $stream;
+	}
+	
+	public static function addJs($js){  
+		global $_;
+		$bt =  debug_backtrace();
+		$GLOBALS['hooks']['js_files'][] = str_replace(__ROOT__,'',dirname($bt[0]['file'])).$js;  
+	}
+
+	public static function callJs(){
+		if(!isset($GLOBALS['hooks']['js_files'])) return '';
+		$stream = '';
+		foreach($GLOBALS['hooks']['js_files'] as $js_file)
+			$stream .='<script type="text/javascript" src="'.$js_file.'"></script>'."\n";
+		return $stream;
+	}
+	
+}
+
+?>

+ 4 - 1
common.php

@@ -19,8 +19,11 @@
     $myUser = new User();
     $conf = new Configuration();
     $conf->getAll();
-    $page = basename($_SERVER['PHP_SELF']);
+    $page = basename($_SERVER['PHP_SELF']).(isset($_SERVER['QUERY_STRING'])?'?'.$_SERVER['QUERY_STRING']:'');
+
+  
 
     if (isset($_SESSION['currentUser'])) {
         $myUser =unserialize($_SESSION['currentUser']);
     }
+    Plugin::includeAll();

+ 6 - 4
constant.php

@@ -1,15 +1,17 @@
 <?php
+define('SLASH',DIRECTORY_SEPARATOR);
+define('__ROOT__',dirname(__FILE__).SLASH);
 
-define('__ROOT__',dirname(__FILE__).DIRECTORY_SEPARATOR);
 define('TIME_ZONE','Europe/Paris');
 define('ENTITY_PREFIX', '');
-define('DATABASE_PATH','database/.db');
+define('DATABASE_PATH','database'.SLASH.'.db');
 define('BASE_CONNECTION_STRING','sqlite:'.DATABASE_PATH);
 define('BASE_LOGIN',null);
 define('BASE_PASSWORD',null);
 define('UPLOAD_PATH','upload');
-define('SKETCH_PATH',UPLOAD_PATH.'/sketch/');
-define('PART_PATH',UPLOAD_PATH.'/part/');
+define('SKETCH_PATH',UPLOAD_PATH.SLASH.'sketch'.SLASH);
+define('PART_PATH',UPLOAD_PATH.SLASH.'part'.SLASH);
+define('PLUGIN_PATH','plugin'.SLASH);
 define('ALLOWED_RESOURCE_IMAGE','jpg,png,jpeg,gif,bmp');
 //Laisser vide si pas de vérification sur les resources de fichiers, sinon placez les extension
 //autorisées sous la forme : jpg,png,jpeg,gif,bmp,txt,doc,docx,cmd,bat,exe,cpp,c,h,hpp,ino,php,js,css,ttf,woff,svg,pdf,xls,xlsx,sql,ico,doc,docx

+ 12 - 0
css/main.css

@@ -297,6 +297,18 @@ a.navbar-brand {
     background: url('../img/icon.png') no-repeat 5px center !important;
 }
 
+.pointer{
+	cursor:pointer;
+}
+.right{
+	float:right;
+}
+.left{
+	float:left;
+}
+.clear{
+	clear:both;
+}
 /* ANIMATIONS */
 
 @-webkit-keyframes pop-in {

+ 91 - 0
dashboard.php

@@ -0,0 +1,91 @@
+	<?php global $myUser; ?>
+	<div class="jumbotron" id="sketchBlock">
+			<div class="jumbotron-contents">
+				<h2><i class="fa fa-inbox"></i> Sketch(s)</h2>
+				
+
+				<?php if ($myUser->connected()): ?>
+				<div class="btn-group">
+				  <button class="btn btn-success" type="button" data-toggle="modal" data-target="#editSketch">Ajouter</button>
+				  <button data-toggle="dropdown" class="btn btn-success dropdown-toggle" type="button">
+					<span class="caret"></span>
+					<span class="sr-only">Toggle Dropdown</span>
+				  </button>
+				  <ul role="menu" class="dropdown-menu">
+					<li><a data-toggle="modal" data-target="#editSketch">Créer</a></li>
+					<li><a data-toggle="modal" data-target="#importSketch" href="#">Importer depuis un hackpoint</a></li>
+					<li><a id="importJsonSketch">Importer depuis un fichier json</a></li>
+				  </ul>
+				</div>
+				<?php endif; ?>
+				
+				<table class="table table-striped table-hover" id="sketchs">
+					<thead>
+						<tr>
+							<th>#</th>
+							<th>Libellé</th>
+							<th>Propriétaire</th>
+							<th>Visibilité</th>
+							<th style="width:100px;">Options</th>
+						</tr>
+					</thead>
+					<tbody>
+						<tr data-id="{{id}}" style="display:none;">
+							<td><a href="sketch.php?id={{id}}">{{id}}</a></td>
+							<td><a href="sketch.php?id={{id}}">{{label}}</a></td>
+							<td>{{owner}}</td>
+							<td>{{#public}}<i class="fa fa-eye"></i> Publique{{/public}}{{^public}}<i class="fa fa-eye-slash"></i> Privé{{/public}}</td>
+							<td>
+								<div onclick="delete_sketch(this);" class="btn btn-danger btn-mini btn-rounded pulse"><i class="fa fa-times"></i></div>
+							</td>
+						</tr>
+					</tbody>
+				</table>
+			</div>
+		</div>
+		
+	<!-- Modal -->
+	<div id="editSketch" class="modal fade" role="dialog" data-action="create_sketch">
+	  <div class="modal-dialog">
+
+	    <!-- Modal content-->
+	    <div class="modal-content">
+	      <div class="modal-header">
+	        <button type="button" class="close" data-dismiss="modal">&times;</button>
+	        <h4 class="modal-title">Edition Sketch</h4>
+	      </div>
+	      <div class="modal-body">
+	      	<label for="label">Comment vas t-on appeller ça ? :D</label>
+	       <input class="form-control" type="text" id="label"/>
+	      </div>
+	      <div class="modal-footer">
+	      	<button type="button" class="btn btn-primary" onclick="create_sketch();" data-dismiss="modal">Enregistrer</button>
+	        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+	      </div>
+	    </div>
+		
+	  </div>
+	</div>
+	
+	<!-- Modal -->
+	<div id="importSketch" class="modal fade" role="dialog" data-action="import_sketch">
+	  <div class="modal-dialog">
+
+	    <!-- Modal content-->
+	    <div class="modal-content">
+	      <div class="modal-header">
+	        <button type="button" class="close" data-dismiss="modal">&times;</button>
+	        <h4 class="modal-title">Edition Sketch</h4>
+	      </div>
+	      <div class="modal-body">
+	      	<label for="url">Quelle est l'adresse du sketch ? :D</label>
+	        <input class="form-control" type="text" id="url"/>
+	      </div>
+	      <div class="modal-footer">
+	      	<button type="button" class="btn btn-primary" onclick="import_sketch();" data-dismiss="modal">Enregistrer</button>
+	        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+	      </div>
+	    </div>
+		
+	  </div>
+	</div>

+ 48 - 6
header.php

@@ -1,5 +1,32 @@
 <?php 
 require_once __DIR__.DIRECTORY_SEPARATOR.'common.php';
+
+
+Plugin::addHook("page", function(){
+	global $_;
+	if(isset($_['module'])) return;
+	require_once('dashboard.php');
+}); 
+
+Plugin::addHook("menu_main", function(&$menuItems){
+	global $_,$myUser;
+	$menuItems[] = array(
+	'sort'=>0,
+	'url'=>'index.php',
+	'label'=>'Sketch',
+	'icon'=>'codepen'
+	);
+
+	if (! $myUser->connected()) return;
+	$menuItems[] = array(
+	'sort'=>1,
+	'url'=>'component.php',
+	'label'=>'Composants',
+	'icon'=>'codepen'
+	);
+}); 
+
+
 ?>
 <!doctype html>
 <html class="no-js" lang="">
@@ -40,10 +67,23 @@ require_once __DIR__.DIRECTORY_SEPARATOR.'common.php';
 					
 					<div id="bs-example-navbar-collapse-5" class="collapse navbar-collapse">
                       <ul class="nav navbar-nav">
-                        <li <?php echo $page=='index.php'?'class="active"':''; ?>><a href="index.php">Sketch</a></li>
-                        <?php if ($myUser->connected()): ?>
-						<li <?php echo $page=='component.php'?'class="active"':''; ?>><a href="component.php">Composants</a></li>
-						<?php endif; ?>
+                       
+
+						<?php 
+						$menuItems = array();
+						Plugin::callHook("menu_main",array(&$menuItems)); 
+						
+						usort($menuItems, function($a, $b){
+							if ($a['sort'] == $b['sort']) {
+						        return 0;
+						    }
+						    return ($a['sort'] < $b['sort']) ? -1 : 1;
+						});
+
+						foreach($menuItems as $item):
+						?>
+						<li <?php echo $page==$item['url']?'class="active"':''; ?>><a href="<?php echo $item['url'];?>"><?php echo $item['label'];?></a></li>
+						<?php endforeach; ?>
                       </ul>
                       <ul class="nav navbar-nav navbar-right">
 					  
@@ -61,8 +101,10 @@ require_once __DIR__.DIRECTORY_SEPARATOR.'common.php';
                         <li class="dropdown <?php echo $page=='account.php'?'active':''; ?>" >
                           <a data-toggle="dropdown" class="dropdown-toggle" href="#"> Connecté en tant que <?php echo $myUser->login; ?> <b class="caret"></b></a>
                           <ul role="menu" class="dropdown-menu">
-                            <li class="dropdown-header">Profil</li>
-                            <li ><a href="account.php">Modifier</a></li>
+                            <li class="dropdown-header">Préférences</li>
+                            <li ><a href="plugin.php">Plugins</a></li>
+                            <li ><a href="account.php">Profil</a></li>
+                            
                             <li class="divider"></li>
                             <li><a href="action.php?action=logout">Déconnexion</a></li>
                           </ul>

+ 4 - 131
index.php

@@ -1,132 +1,5 @@
-<?php require_once __DIR__.DIRECTORY_SEPARATOR.'header.php'; ?>
-
-	
-
-			<div class="jumbotron" id="sketchBlock">
-			<div class="jumbotron-contents">
-				<h2><i class="fa fa-inbox"></i> Sketch(s)</h2>
-				
-				<textarea>
-					
-					void setup() {
-  Serial.begin(9600); 
-  pinMode(PRESENCE_PIN, INPUT);    
-  pinMode(LIGHT_PIN, INPUT);
-  pinMode(SOUND_PIN, INPUT);
-  pinMode(LED_PIN, OUTPUT);
-  digitalWrite(LED_PIN,LOW);
-  Serial.println("Demarrage de P.R.O.P.I.S.E");
-  delay(3000);
-  digitalWrite(LED_PIN,HIGH);
-  Serial.println("Rruuullleez !");
-}
-
-				</textarea>
- <script>
-      /*var cEditor = CodeMirror.fromTextArea(document.getElementById("c-code"), {
-        lineNumbers: true,
-        matchBrackets: true,
-        mode: "text/x-csrc"
-      });
-     
-      var ceylonEditor = CodeMirror.fromTextArea(document.getElementById("ceylon-code"), {
-          lineNumbers: true,
-          matchBrackets: true,
-          mode: "text/x-ceylon"
-      });
-      var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
-      CodeMirror.keyMap.default[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";*/
-    </script>
-				<?php if ($myUser->connected()): ?>
-				<div class="btn-group">
-				  <button class="btn btn-success" type="button" data-toggle="modal" data-target="#editSketch">Ajouter</button>
-				  <button data-toggle="dropdown" class="btn btn-success dropdown-toggle" type="button">
-					<span class="caret"></span>
-					<span class="sr-only">Toggle Dropdown</span>
-				  </button>
-				  <ul role="menu" class="dropdown-menu">
-					<li><a data-toggle="modal" data-target="#editSketch">Créer</a></li>
-					<li><a data-toggle="modal" data-target="#importSketch" href="#">Importer depuis un hackpoint</a></li>
-					<li><a id="importJsonSketch">Importer depuis un fichier json</a></li>
-				  </ul>
-				</div>
-				<?php endif; ?>
-				
-				<table class="table table-striped table-hover" id="sketchs">
-					<thead>
-						<tr>
-							<th>#</th>
-							<th>Libellé</th>
-							<th>Propriétaire</th>
-							<th>Visibilité</th>
-							<th style="width:100px;">Options</th>
-						</tr>
-					</thead>
-					<tbody>
-						<tr data-id="{{id}}" style="display:none;">
-							<td><a href="sketch.php?id={{id}}">{{id}}</a></td>
-							<td><a href="sketch.php?id={{id}}">{{label}}</a></td>
-							<td>{{owner}}</td>
-							<td>{{#public}}<i class="fa fa-eye"></i> Publique{{/public}}{{^public}}<i class="fa fa-eye-slash"></i> Privé{{/public}}</td>
-							<td>
-								<div onclick="delete_sketch(this);" class="btn btn-danger btn-mini btn-rounded pulse"><i class="fa fa-times"></i></div>
-							</td>
-						</tr>
-					</tbody>
-				</table>
-			</div>
-		</div>
-		
-	<!-- Modal -->
-	<div id="editSketch" class="modal fade" role="dialog" data-action="create_sketch">
-	  <div class="modal-dialog">
-
-	    <!-- Modal content-->
-	    <div class="modal-content">
-	      <div class="modal-header">
-	        <button type="button" class="close" data-dismiss="modal">&times;</button>
-	        <h4 class="modal-title">Edition Sketch</h4>
-	      </div>
-	      <div class="modal-body">
-	      	<label for="label">Comment vas t-on appeller ça ? :D</label>
-	       <input class="form-control" type="text" id="label"/>
-	      </div>
-	      <div class="modal-footer">
-	      	<button type="button" class="btn btn-primary" onclick="create_sketch();" data-dismiss="modal">Enregistrer</button>
-	        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-	      </div>
-	    </div>
-		
-	  </div>
-	</div>
-	
-	<!-- Modal -->
-	<div id="importSketch" class="modal fade" role="dialog" data-action="import_sketch">
-	  <div class="modal-dialog">
-
-	    <!-- Modal content-->
-	    <div class="modal-content">
-	      <div class="modal-header">
-	        <button type="button" class="close" data-dismiss="modal">&times;</button>
-	        <h4 class="modal-title">Edition Sketch</h4>
-	      </div>
-	      <div class="modal-body">
-	      	<label for="url">Quelle est l'adresse du sketch ? :D</label>
-	        <input class="form-control" type="text" id="url"/>
-	      </div>
-	      <div class="modal-footer">
-	      	<button type="button" class="btn btn-primary" onclick="import_sketch();" data-dismiss="modal">Enregistrer</button>
-	        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-	      </div>
-	    </div>
-		
-	  </div>
-	</div>
-			
-
-
 <?php 
-
-
-
-require_once __ROOT__.'footer.php' ?>
+require_once __DIR__.DIRECTORY_SEPARATOR.'header.php'; 
+Plugin::callHook("page"); 
+require_once __ROOT__.'footer.php' 
+?>

+ 2 - 1
install.php

@@ -63,7 +63,8 @@ try {
 	if(!extension_loaded('gd') || !function_exists('gd_info'))  throw new Exception('L\'extension php GD2  est requise, veuillez installer GD2 (sous linux : <code>sudo apt-get install php5-gd && service apache2 restart</code>)');
 	if(!in_array('sqlite',PDO::getAvailableDrivers())) throw new Exception('Le driver SQLITE est requis, veuillez installer sqlite3 (sous linux : <code>sudo apt-get install php5-sqlite && service apache2 restart</code>)');
 	
-	if (!file_exists(__ROOT__.PART_PATH)) mkdir(__ROOT__.PART_PATH,0755,true);
+    if (!file_exists(__ROOT__.PART_PATH)) mkdir(__ROOT__.PART_PATH,0755,true);
+	if (!file_exists(__ROOT__.PLUGIN_PATH)) mkdir(__ROOT__.PLUGIN_PATH,0755,true);
 	
 	//Class entities
 	Entity::install(__ROOT__.'class');

+ 29 - 1
js/main.js

@@ -24,7 +24,24 @@ $(document).ready(function(){
 
 });
 
-
+function init_plugin(){
+	search_plugin(function(){
+			$('.toggle').change(function(){
+				var input = $('input',this);
+				var value = input.prop('checked');
+				
+				$.action({
+					action : 'change_plugin_state',
+					plugin : $(this).closest('li').attr('data-id'),
+					state:value?1:0
+				},function(r){},
+				function(r){
+						input.prop('checked',!value);
+				});
+				
+			});
+		});
+}
 
 function init_index(){
 	search_sketch();
@@ -103,6 +120,17 @@ function init_component(){
 	
 };
 
+/*PLUGINS*/
+
+// SEARCH
+function search_plugin(callback){
+	$('#plugins').fill({action:'search_plugin'},function(){
+		if(callback!=null) callback();
+	});
+}
+
+
+/*COMPONENTS*/
 function save_component(){
 	var data = $.getForm('#editComponent');
 	

+ 5 - 1
js/plugins.js

@@ -272,7 +272,11 @@ $.fn.extend({
 					container.find(childName+':visible:not(.nofill)').remove();
 				}
 				var tpl = model.get(0).outerHTML;
-				
+
+				//fix jquery backslahes break
+				tpl = tpl.replace(/{{##/g,'{{/').replace(/{{\/(.*)}}=""/g,'{{/$1}}');
+				//fix images url not found on template
+				tpl = tpl.replace(/(<img\s[^>]*\s)(data-src)/g,'$1src');
 				$.action(option,function(r){
 					for(var key in r.rows){
 						var line = $(Mustache.render(tpl,r.rows[key]));

+ 38 - 0
plugin.php

@@ -0,0 +1,38 @@
+<?php require_once __DIR__.DIRECTORY_SEPARATOR.'header.php'; ?>
+
+	<?php if (!$myUser->connected()) {
+    header('location: index.php');
+} ?>
+
+	<div class="row">
+	<div class="col-md-12">
+		<h3>Plugins</h3>
+		<ul id="plugins" class="list-group">
+			<li class="list-group-item" style="display:none;" data-id="{{id}}">
+				<h4 class="list-group-item-heading">{{name}} <span class="label label-info">v{{version}}</span></h4>
+				<p class="list-group-item-text">{{description}}</p>
+				
+				<a class="pointer" onclick="$(this).next('ul').slideToggle(200);"><i class="fa fa-search"></i> + d'Infos</a>
+				<ul style="display:none;">
+					<li>ID : {{id}}</li>
+					<li><span class="label label label-default">{{author.name}}</span></li>
+					<li>Licence: {{#licence.url}}<a href="{{licence.url}}">{{/licence.url}}{{licence.name}}{{#licence.url}}</a>{{/licence.url}}</li>
+					<li>Version: <code>{{version}}</code></li>
+					<li>Site web: <a href="{{url}}">{{url}}</a></li>
+					<li>Dossier: {{folder.path}}</li>
+					<li>Pré-requis : <ul>{{#require}}<li>{{key}} - <span class="label label-info">{{value}}</span></li>{{/require}}</ul></li>
+				</ul>
+				<label class="activator">
+					<small>On/Off</small>
+					<label class="toggle">
+						<input {{#state}}checked=""{{##state}} type="checkbox">
+						<span class="handle"></span>
+					</label>
+				</label>
+				
+			</li>
+		</ul>
+	</div>
+</div>
+
+<?php require_once __ROOT__.'footer.php' ?>

+ 28 - 0
plugin/modele/Voiture.class.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ @nom: Voiture
+ @auteur: Idleman (idleman@idleman.fr)
+ @description:  Modele de classe pour les plugins
+ */
+
+//Ce fichier permet de gerer vos donnees en provenance de la base de donnees
+
+//Il faut changer le nom de la classe ici (je sens que vous allez oublier)
+class Voiture extends Entity{
+
+	
+	public $id,$marque,$vitesse; //Pour rajouter des champs il faut ajouter les variables ici...
+	protected $TABLE_NAME = 'plugin_voiture'; 	//Penser a mettre le nom du plugin et de la classe ici
+	protected $fields = 
+	array( //...Puis dans l'array ici mettre nom du champ => type
+		'id'=>'key',
+		'marque'=>'string',
+		'vitesse'=>'int'
+	);
+
+
+
+}
+
+?>

+ 9 - 0
plugin/modele/app.json

@@ -0,0 +1,9 @@
+{
+	"id": "fr.idleman.modele",
+	"name": "Modèle",
+	"version": "1.0",
+	"url": "http://blog.idleman.fr",
+	"licence": {"name": "MIT","url" : "http://choosealicense.com/licenses/mit/"},
+	"description": "Modèle de plugin",
+	"require" : {}
+}

+ 4 - 0
plugin/modele/main.css

@@ -0,0 +1,4 @@
+/* Fichier de style CSS rŽservŽ a votre plugin*/
+#main aside.squeletteMenu{
+	background-color: #3C3C3C;
+}

+ 5 - 0
plugin/modele/main.js

@@ -0,0 +1,5 @@
+//fonction appelée depuis la fonction squelette_plugin_menu de la page squelette.plugin.[enabled/disabled].php 
+function squelette_javascript(){
+	alert('Cette fonction ne sert à rien :p');
+}
+

+ 100 - 0
plugin/modele/modele.plugin.php

@@ -0,0 +1,100 @@
+<?php
+
+
+//Cette fonction va generer un nouveau element dans le menu
+function test_plugin_menu(&$menuItems){
+	global $_;
+	$menuItems[] = array(
+	'sort'=>10,
+	'url'=>'index.php?module=modele',
+	'label'=>'Modele',
+	'icon'=>'codepen'
+	);
+}
+
+
+
+
+
+//Cette fonction va generer une page quand on clique sur Modele dans menu
+function test_plugin_page(){
+	global $_;
+	if(!isset($_['module']) || $_['module']!='modele') return;
+	require_once('Voiture.class.php');
+	?>
+	<h3>Mon plugin</h3>
+	<h5>Plugins d'exemple</h5>
+	
+<?php
+}
+
+function test_plugin_install($id){
+	if($id != 'fr.idleman.modele') return;
+	require_once('Voiture.class.php');
+	//Création de la table voiture
+	Voiture::create();
+	//Création d'une voiture d'exemple
+	$pixo = new Voiture();
+	$pixo->marque = "Nissan Pixo";
+	$pixo->vitesse = 110;
+	$pixo->save();
+	// en cas d'erreur : throw new Exception('Mon erreur');
+}
+function test_plugin_uninstall($id){
+	if($id != 'fr.idleman.modele') return;
+	require_once('Voiture.class.php');
+	Voiture::drop();
+	// en cas d'erreur : throw new Exception('Mon erreur');
+}
+
+function test_plugin_section(&$sections){
+	$sections['modele'] = 'Gestion du plugin Modèle';
+}
+
+
+//cette fonction comprends toutes les actions du plugin qui ne nécessitent pas de vue html
+function test_plugin_action(){
+	global $_,$conf;
+	switch($_['action']){
+		case 'test_widget_load':
+			$widget = Widget::getById($_['id']);
+			$widget->title = 'Au commencement, il y avait yana';
+			echo json_encode($widget);
+		break;
+	}
+}
+
+
+
+function test_plugin_widget_refresh(&$widgets){
+	$widget = Widget::getById(1);
+	$widget->title = 'Hello widget !';
+	$widget->icon = 'fa-user';
+	$widget->content = 'Dernier rafraichissement : '.date('d/m/Y H:i:s');
+	$widgets[] = $widget ;
+}
+
+function test_plugin_widget(&$widgets){
+	$modelWidget = new Widget();
+	$modelWidget->model = 'test';
+	$modelWidget->title = 'Horloge';
+	$modelWidget->icon = 'fa-caret-right';
+	$modelWidget->background = '#50597b';
+	$modelWidget->load = 'action.php?action=test_widget_load';
+	$modelWidget->delete = 'action.php?action=test_widget_delete';
+	$modelWidget->js = [Plugin::url().'/main.js'];
+	$modelWidget->css = [Plugin::url().'/main.css'];
+	$widgets[] = $modelWidget;
+}
+
+Plugin::addCss("/main.css"); 
+Plugin::addJs("/main.js"); 
+
+Plugin::addHook("install", "test_plugin_install");
+Plugin::addHook("uninstall", "test_plugin_uninstall"); 
+Plugin::addHook("section", "test_plugin_section");
+Plugin::addHook("menu_main", "test_plugin_menu"); 
+Plugin::addHook("page", "test_plugin_page");  
+Plugin::addHook("action", "test_plugin_action");    
+
+?>