Plugin.class.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <?php
  2. /*
  3. @nom: Plugin
  4. @auteur: Valentin CARRUESCO (http://blog.idleman.fr)
  5. @description: Classe de gestion des plugins au travers de l'application
  6. */
  7. class Plugin{
  8. const FOLDER = '/plugins';
  9. protected $name,$author,$address,$link,$licence,$path,$description,$version,$state,$type;
  10. function __construct(){
  11. }
  12. public static function includeAll(){
  13. global $i18n, $i18n_js, $theme;
  14. $pluginFiles = Plugin::getFiles(true);
  15. if(is_array($pluginFiles)) {
  16. foreach($pluginFiles as $pluginFile) {
  17. // Chargement du fichier de Langue du plugin
  18. $i18n->append(new Translation(dirname($pluginFile),$i18n->languages));
  19. // Inclusion du coeur de plugin
  20. include $pluginFile;
  21. // Gestion des css du plugin en fonction du thème actif
  22. $cssTheme = glob(dirname($pluginFile).'/*/'.$theme.'.css');
  23. $cssDefault = glob(dirname($pluginFile).'/*/default.css');
  24. if(isset($cssTheme[0])){
  25. $GLOBALS['hooks']['css_files'][] = Functions::relativePath(str_replace('\\','/',dirname(__FILE__)),str_replace('\\','/',$cssTheme[0]));
  26. }else if(isset($cssDefault[0])){
  27. $GLOBALS['hooks']['css_files'][] = Functions::relativePath(str_replace('\\','/',dirname(__FILE__)),str_replace('\\','/',$cssDefault[0]));
  28. }
  29. }
  30. }
  31. $i18n_js = $i18n->getJson();
  32. }
  33. private static function getStates(){
  34. $stateFile = dirname(__FILE__).Plugin::FOLDER.'/plugins.states.json';
  35. if(!file_exists($stateFile)) touch($stateFile);
  36. return json_decode(file_get_contents($stateFile),true);
  37. }
  38. private static function setStates($states){
  39. $stateFile = dirname(__FILE__).Plugin::FOLDER.'/plugins.states.json';
  40. file_put_contents($stateFile,json_encode($states));
  41. }
  42. public static function pruneStates() {
  43. $statesBefore = self::getStates();
  44. if(empty($statesBefore))
  45. $statesBefore = array();
  46. $statesAfter = array();
  47. $error = false;
  48. if (is_array($statesBefore))
  49. {
  50. foreach($statesBefore as $file=>$state) {
  51. if (file_exists($file))
  52. $statesAfter[$file] = $state;
  53. else
  54. $error = true;
  55. }
  56. }
  57. if ($error) self::setStates($statesAfter);
  58. }
  59. private static function getObject($pluginFile){
  60. $plugin = new Plugin();
  61. $fileLines = file_get_contents($pluginFile);
  62. if(preg_match_all("#@author\s(.+)\s\<(.*)\>#", $fileLines, $matches)) {
  63. foreach($matches[1] as $match) {
  64. $authors[] = trim($match);
  65. }
  66. $plugin->setAuthor($authors);
  67. foreach($matches[2] as $match) {
  68. $address[] = strtolower($match);
  69. }
  70. $plugin->setAddress($address);
  71. }
  72. if(preg_match("#@name\s(.+)[\r\n]#", $fileLines, $match))
  73. $plugin->setName($match[1]);
  74. if(preg_match("#@licence\s(.+)[\r\n]#", $fileLines, $match))
  75. $plugin->setLicence($match[1]);
  76. if(preg_match("#@version\s(.+)[\r\n]#", $fileLines, $match))
  77. $plugin->setVersion($match[1]);
  78. if(preg_match("#@link\s(.+)[\r\n]#", $fileLines, $match))
  79. $plugin->setLink(trim($match[1]));
  80. if(preg_match("#@type\s(.+)[\r\n]#", $fileLines, $match))
  81. $plugin->setType(trim($match[1]));
  82. if(preg_match("#@description\s(.+)[\r\n]#", $fileLines, $match))
  83. $plugin->setDescription(trim($match[1]));
  84. if(Plugin::loadState($pluginFile) || $plugin->getType()=='component'){
  85. $plugin->setState(1);
  86. }else{
  87. $plugin->setState(0);
  88. }
  89. $plugin->setPath($pluginFile);
  90. return $plugin;
  91. }
  92. public static function getAll(){
  93. $pluginFiles = Plugin::getFiles();
  94. $plugins = array();
  95. if(is_array($pluginFiles)) {
  96. foreach($pluginFiles as $pluginFile) {
  97. $plugin = Plugin::getObject($pluginFile);
  98. $plugins[]=$plugin;
  99. }
  100. }
  101. usort($plugins, "Plugin::sortPlugin");
  102. return $plugins;
  103. }
  104. public static function install($url) {
  105. $logger = new Logger('settings');
  106. if(empty($url)){
  107. $logger->appendLogs("Pas d'url renseignée.");
  108. }elseif(filter_var($url, FILTER_VALIDATE_URL) === false){
  109. $logger->appendLogs("L'url renseignée n'est pas valide.");
  110. }else{
  111. $logger->appendLogs('Téléchargement du plugin...');
  112. $pluginBaseFolder = str_replace('/', '', self::FOLDER).'/';
  113. $tempZipName = $pluginBaseFolder.md5(microtime());
  114. file_put_contents($tempZipName,self::getUrlContent(urldecode($url)));
  115. if(file_exists($tempZipName)){
  116. $logger->appendLogs('Plugin téléchargé <span class="button notice">OK</span>');
  117. $logger->appendLogs('Extraction du plugin...');
  118. $zip = new ZipArchive;
  119. $res = $zip->open($tempZipName);
  120. if ($res === TRUE) {
  121. $tempZipFolder = $tempZipName.'_';
  122. $pluginFolder = $tempZipFolder;
  123. $zip->extractTo($tempZipFolder);
  124. $zip->close();
  125. $logger->appendLogs('Plugin extrait <span class="button notice">OK</span>');
  126. $fi = new FilesystemIterator($tempZipFolder, FilesystemIterator::SKIP_DOTS);
  127. if(iterator_count($fi) === 1) {
  128. foreach($fi as $file){
  129. $filename = $file->getFilename();
  130. $pluginFolder = $pluginBaseFolder.$filename;
  131. rename($tempZipFolder.'/'.$filename, $pluginFolder);
  132. rmdir($tempZipFolder);
  133. }
  134. }
  135. $pluginName = glob($pluginFolder.'/*.plugin*.php');
  136. if(count($pluginName)>0){
  137. $pluginName = str_replace(array($pluginFolder.'/','.enabled','.disabled','.plugin','.php'),'',$pluginName[0]);
  138. if(!file_exists($pluginBaseFolder.$pluginName)){
  139. $logger->appendLogs('Renommage...');
  140. if(rename($pluginFolder,$pluginBaseFolder.$pluginName)){
  141. $logger->appendLogs('Plugin installé, rechargez la page pour voir le plugin <span class="button notice">pensez à l\'activer</span>');
  142. }else{
  143. Functions::rmFullDir($pluginFolder);
  144. $logger->appendLogs('Impossible de renommer le plugin <span class="button error">Erreur</span>');
  145. }
  146. }else{
  147. $logger->appendLogs('Plugin déjà installé <span class="button warning">OK</span>');
  148. }
  149. }else{
  150. $logger->appendLogs('Plugin invalide, fichier principal manquant <span class="button error">Erreur</span>');
  151. }
  152. } else {
  153. $logger->appendLogs('Echec de l\'extraction <span class="button error">Erreur</span>');
  154. }
  155. unlink($tempZipName);
  156. }else{
  157. $logger->appendLogs('Echec du téléchargement <span class="button error">Erreur</span>');
  158. }
  159. }
  160. if(Functions::isAjaxCall()){
  161. echo json_encode($logger->getLogs(), JSON_HEX_QUOT | JSON_HEX_TAG);
  162. } else {
  163. $logger->save();
  164. header('location: ./settings.php#pluginBloc');
  165. }
  166. }
  167. public function getGithubMarketRepos() {
  168. header('Content-Type: application/json');
  169. echo json_encode($this->getGithubMarketReposInfos($this->getGithubMarketReposList()));
  170. }
  171. protected function getGithubMarketReposList() {
  172. return json_decode(self::getUrlContent("https://api.github.com/orgs/Leed-market/repos"));
  173. }
  174. protected function getGithubMarketReposInfos($repos) {
  175. $infos = array();
  176. $installedPluginsNames = $this->getInstalledPluginsNames();
  177. foreach($repos as $repo) {
  178. $repoName = $repo->name;
  179. if(!in_array(strtolower($repoName), $installedPluginsNames)) {
  180. $infos[] = array(
  181. 'name' => $repoName,
  182. 'description' => isset($repo->description) ? $repo->description : false,
  183. 'zipUrl' => 'https://github.com/'.$repo->full_name.'/archive/'.$repo->default_branch.'.zip'
  184. );
  185. }
  186. }
  187. return $infos;
  188. }
  189. protected function getInstalledPluginsNames() {
  190. $names = array();
  191. $installedPlugins = self::getAll();
  192. if(!$installedPlugins || empty($installedPlugins)) {
  193. return $names;
  194. }
  195. foreach($installedPlugins as $installedPlugin) {
  196. $names[] = strtolower($installedPlugin->getName());
  197. }
  198. return $names;
  199. }
  200. protected static function getUrlContent($url) {
  201. $timeout = 20;
  202. $ch = curl_init();
  203. curl_setopt($ch, CURLOPT_URL, $url);
  204. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  205. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  206. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  207. curl_setopt($ch, CURLOPT_REFERER, $url);
  208. curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
  209. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true );
  210. $datas = curl_exec($ch);
  211. curl_close($ch);
  212. return $datas;
  213. }
  214. public static function addHook($hookName, $functionName) {
  215. $GLOBALS['hooks'][$hookName][] = $functionName;
  216. }
  217. public static function addCss($css) {
  218. $bt = debug_backtrace();
  219. $pathInfo = explode('/',dirname($bt[0]['file']));
  220. $count = count($pathInfo);
  221. $name = $pathInfo[$count-1];
  222. $path = '.'.Plugin::FOLDER.'/'.$name.$css;
  223. $GLOBALS['hooks']['css_files'][] = $path;
  224. }
  225. public static function callCss(){
  226. $return='';
  227. if(isset($GLOBALS['hooks']['css_files'])) {
  228. foreach($GLOBALS['hooks']['css_files'] as $css_file) {
  229. $return .='<link rel="stylesheet" href="'.$css_file.'">'."\n";
  230. }
  231. }
  232. return $return;
  233. }
  234. public static function addLink($rel, $link, $type='', $title='') {
  235. $GLOBALS['hooks']['head_link'][] = array("rel"=>$rel, "link"=>$link, "type"=>$type, "title"=>$title);
  236. }
  237. public static function callLink(){
  238. $return='';
  239. if(isset($GLOBALS['hooks']['head_link'])) {
  240. foreach($GLOBALS['hooks']['head_link'] as $head_link) {
  241. $return .='<link rel="'.$head_link['rel'].'" href="'.$head_link['link'].'" type="'.$head_link['type'].'" title="'.$head_link['title'].'" />'."\n";
  242. }
  243. }
  244. return $return;
  245. }
  246. public static function path(){
  247. $bt = debug_backtrace();
  248. $pathInfo = explode('/',dirname($bt[0]['file']));
  249. $count = count($pathInfo);
  250. $name = $pathInfo[$count-1];
  251. return '.'.Plugin::FOLDER.'/'.$name.'/';
  252. }
  253. public static function addJs($js) {
  254. $bt = debug_backtrace();
  255. $pathInfo = explode('/',dirname($bt[0]['file']));
  256. $count = count($pathInfo);
  257. $name = $pathInfo[$count-1];
  258. $path = '.'.Plugin::FOLDER.'/'.$name.$js;
  259. $GLOBALS['hooks']['js_files'][] = $path;
  260. }
  261. public static function callJs(){
  262. $return='';
  263. if(isset($GLOBALS['hooks']['js_files'])) {
  264. foreach($GLOBALS['hooks']['js_files'] as $js_file) {
  265. $return .='<script type="text/javascript" src="'.$js_file.'"></script>'."\n";
  266. }
  267. }
  268. return $return;
  269. }
  270. public static function callHook($hookName, $hookArguments) {
  271. //echo '<div style="display:inline;background-color:#CC47CB;padding:3px;border:5px solid #9F1A9E;border-radius:5px;color:#ffffff;font-size:15px;">'.$hookName.'</div>';
  272. if(isset($GLOBALS['hooks'][$hookName])) {
  273. foreach($GLOBALS['hooks'][$hookName] as $functionName) {
  274. call_user_func_array($functionName, $hookArguments);
  275. }
  276. }
  277. }
  278. public static function getFiles($onlyActivated=false){
  279. $enabled = $disabled = array();
  280. $files = glob(dirname(__FILE__). Plugin::FOLDER .'/*/*.plugin*.php');
  281. if(empty($files))
  282. $files = array();
  283. foreach($files as $file){
  284. $plugin = Plugin::getObject($file);
  285. if($plugin->getState()){
  286. $enabled [] = $file;
  287. }else{
  288. $disabled [] = $file;
  289. }
  290. }
  291. if(!$onlyActivated)$enabled = array_merge($enabled,$disabled);
  292. return $enabled;
  293. }
  294. public static function loadState($plugin){
  295. $states = Plugin::getStates();
  296. return (isset($states[$plugin])?$states[$plugin]:false);
  297. }
  298. public static function changeState($plugin,$state){
  299. $states = Plugin::getStates();
  300. $states[$plugin] = $state;
  301. Plugin::setStates($states);
  302. }
  303. public static function enabled($pluginUid){
  304. $plugins = Plugin::getAll();
  305. foreach($plugins as $plugin){
  306. if($plugin->getUid()==$pluginUid){
  307. Plugin::changeState($plugin->getPath(),true);
  308. $install = dirname($plugin->getPath()).'/install.php';
  309. if(file_exists($install))require_once($install);
  310. }
  311. }
  312. }
  313. public static function disabled($pluginUid){
  314. $plugins = Plugin::getAll();
  315. foreach($plugins as $plugin){
  316. if($plugin->getUid()==$pluginUid){
  317. Plugin::changeState($plugin->getPath(),false);
  318. $uninstall = dirname($plugin->getPath()).'/uninstall.php';
  319. if(file_exists($uninstall))require_once($uninstall);
  320. }
  321. }
  322. }
  323. function getUid(){
  324. $pathInfo = explode('/',$this->getPath());
  325. $count = count($pathInfo);
  326. $name = $pathInfo[$count-1];
  327. return $pathInfo[$count -2].'-'.substr($name,0,strpos($name,'.'));
  328. }
  329. static function sortPlugin($a, $b){
  330. if ($a->getState() == $b->getState())
  331. if ($a->getName() == $b->getName())
  332. return 0;
  333. else
  334. return $a->getName() < $b->getName() ? -1 : 1;
  335. else
  336. return $a->getState() < $b->getState() ? -1 : 1;
  337. }
  338. function getName(){
  339. return $this->name;
  340. }
  341. function setName($name){
  342. $this->name = $name;
  343. }
  344. function setAuthor($author){
  345. $this->author = $author;
  346. }
  347. function getAuthor(){
  348. return $this->author;
  349. }
  350. function getAddress(){
  351. return $this->address;
  352. }
  353. function setAddress($address){
  354. $this->address = $address;
  355. }
  356. function getLicence(){
  357. return $this->licence;
  358. }
  359. function setLicence($licence){
  360. $this->licence = $licence;
  361. }
  362. function getPath(){
  363. return $this->path;
  364. }
  365. function setPath($path){
  366. $this->path = $path;
  367. }
  368. function getDescription(){
  369. return $this->description;
  370. }
  371. function setDescription($description){
  372. $this->description = $description;
  373. }
  374. function getLink(){
  375. return $this->link;
  376. }
  377. function setLink($link){
  378. $this->link = $link;
  379. }
  380. function getVersion(){
  381. return $this->version;
  382. }
  383. function setVersion($version){
  384. $this->version = $version;
  385. }
  386. function getState(){
  387. return $this->state;
  388. }
  389. function setState($state){
  390. $this->state = $state;
  391. }
  392. function getType(){
  393. return $this->type;
  394. }
  395. function setType($type){
  396. $this->type = $type;
  397. }
  398. }
  399. ?>