core-commandline.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <?php
  2. // Copyright (c) 2013-2014 Datenstrom, http://datenstrom.se
  3. // This file may be used and distributed under the terms of the public license.
  4. // Command line core plugin
  5. class YellowCommandline
  6. {
  7. const Version = "0.4.5";
  8. var $yellow; //access to API
  9. var $content; //number of content pages
  10. var $media; //number of media files
  11. var $system; //number of system files
  12. var $error; //number of build errors
  13. var $locationsArguments; //locations with arguments detected
  14. var $locationsPagination; //locations with pagination detected
  15. // Handle plugin initialisation
  16. function onLoad($yellow)
  17. {
  18. $this->yellow = $yellow;
  19. $this->yellow->config->setDefault("commandlineIgnoreLocation", "");
  20. $this->yellow->config->setDefault("commandlineSystemFile", ".htaccess");
  21. }
  22. // Handle command help
  23. function onCommandHelp()
  24. {
  25. $help .= "version\n";
  26. $help .= "build [DIRECTORY LOCATION]\n";
  27. $help .= "clean [DIRECTORY LOCATION]\n";
  28. return $help;
  29. }
  30. // Handle command
  31. function onCommand($args)
  32. {
  33. list($name, $command) = $args;
  34. switch($command)
  35. {
  36. case "": $statusCode = $this->helpCommand(); break;
  37. case "version": $statusCode = $this->versionCommand(); break;
  38. case "build": $statusCode = $this->buildCommand($args); break;
  39. case "clean": $statusCode = $this->cleanCommand($args); break;
  40. default: $statusCode = $this->pluginCommand($args);
  41. }
  42. if($statusCode == 0)
  43. {
  44. $statusCode = 400;
  45. echo "Yellow $command: Command not found\n";
  46. }
  47. return $statusCode;
  48. }
  49. // Show available commands
  50. function helpCommand()
  51. {
  52. echo "Yellow ".Yellow::Version."\n";
  53. foreach($this->getCommandHelp() as $line) echo (++$lineCounter>1 ? " " : "Syntax: ")."yellow.php $line\n";
  54. return 200;
  55. }
  56. // Show software version
  57. function versionCommand()
  58. {
  59. echo "Yellow ".Yellow::Version."\n";
  60. foreach($this->getPluginVersion() as $line) echo "$line\n";
  61. return 200;
  62. }
  63. // Build static pages
  64. function buildCommand($args)
  65. {
  66. $statusCode = 0;
  67. list($dummy, $command, $path, $location) = $args;
  68. if(empty($location) || $location[0]=='/')
  69. {
  70. if($this->checkStaticConfig())
  71. {
  72. $statusCode = $this->buildStatic($path, $location);
  73. } else {
  74. $statusCode = 500;
  75. list($this->content, $this->media, $this->system, $this->error) = array(0, 0, 0, 1);
  76. $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile");
  77. echo "ERROR bulding pages: Please configure serverScheme, serverName and serverBase in file '$fileName'!\n";
  78. }
  79. echo "Yellow $command: $this->content content, $this->media media, $this->system system";
  80. echo ", $this->error error".($this->error!=1 ? 's' : '');
  81. echo ", status $statusCode\n";
  82. } else {
  83. $statusCode = 400;
  84. echo "Yellow $command: Invalid arguments\n";
  85. }
  86. return $statusCode;
  87. }
  88. // Build static directories and files
  89. function buildStatic($path, $location)
  90. {
  91. $this->yellow->toolbox->timerStart($time);
  92. $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/');
  93. $this->content = $this->media = $this->system = $this->error = $statusCode = 0;
  94. $this->locationsArguments = $this->locationsPagination = array();
  95. if(empty($location))
  96. {
  97. $statusCode = $this->cleanStatic($path, $location);
  98. foreach($this->getStaticLocations() as $location)
  99. {
  100. $statusCode = max($statusCode, $this->buildStaticRequest($path, $location, true));
  101. }
  102. foreach($this->locationsArguments as $location)
  103. {
  104. $statusCode = max($statusCode, $this->buildStaticRequest($path, $location, true));
  105. }
  106. foreach($this->locationsPagination as $location)
  107. {
  108. for($pageNumber=2; $pageNumber<=999; ++$pageNumber)
  109. {
  110. $statusCodeLocation = $this->buildStaticRequest($path, $location.$pageNumber, false, true);
  111. $statusCode = max($statusCode, $statusCodeLocation);
  112. if($statusCodeLocation == 0) break;
  113. }
  114. }
  115. $statusCode = max($statusCode, $this->buildStaticError($path, 404));
  116. foreach($this->getStaticFilesMedia($path) as $fileNameSource=>$fileNameDest)
  117. {
  118. $statusCode = max($statusCode, $this->buildStaticFile($fileNameSource, $fileNameDest, true));
  119. }
  120. foreach($this->getStaticFilesSystem($path) as $fileNameSource=>$fileNameDest)
  121. {
  122. $statusCode = max($statusCode, $this->buildStaticFile($fileNameSource, $fileNameDest, false));
  123. }
  124. } else {
  125. $statusCode = $this->buildStaticRequest($path, $location);
  126. }
  127. $this->yellow->toolbox->timerStop($time);
  128. if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStatic time:$time ms\n";
  129. return $statusCode;
  130. }
  131. // Build static request
  132. function buildStaticRequest($path, $location, $analyse = false, $probe = false)
  133. {
  134. ob_start();
  135. $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1";
  136. $_SERVER["SERVER_NAME"] = $this->yellow->config->get("serverName");
  137. $_SERVER["REQUEST_URI"] = $this->yellow->config->get("serverBase").$location;
  138. $_SERVER["SCRIPT_NAME"] = $this->yellow->config->get("serverBase")."yellow.php";
  139. $_REQUEST = array();
  140. $statusCode = $this->yellow->request();
  141. if($statusCode < 400)
  142. {
  143. $fileName = $this->yellow->toolbox->findStaticFileFromLocation($location, $path,
  144. $this->yellow->config->get("staticDefaultFile"));
  145. $fileData = ob_get_contents();
  146. $modified = strtotime($this->yellow->page->getHeader("Last-Modified"));
  147. if(!$this->yellow->toolbox->createFile($fileName, $fileData, true) ||
  148. !$this->yellow->toolbox->modifyFile($fileName, $modified))
  149. {
  150. $statusCode = 500;
  151. $this->yellow->page->error($statusCode, "Can't write file '$fileName'!");
  152. }
  153. }
  154. ob_end_clean();
  155. if($statusCode==200 && $analyse) $this->analyseStaticContent($fileData);
  156. if($statusCode==404 && $probe) $statusCode = 0;
  157. if($statusCode != 0) ++$this->content;
  158. if($statusCode >= 400)
  159. {
  160. ++$this->error;
  161. echo "ERROR building content location '$location', ".$this->yellow->page->getStatusCode(true)."\n";
  162. }
  163. if(defined("DEBUG") && DEBUG>=3) echo $fileData;
  164. if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticRequest status:$statusCode location:$location\n";
  165. return $statusCode;
  166. }
  167. // Build static error
  168. function buildStaticError($path, $statusCodeRequest)
  169. {
  170. ob_start();
  171. $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1";
  172. $_SERVER["SERVER_NAME"] = $this->yellow->config->get("serverName");
  173. $_SERVER["REQUEST_URI"] = $this->yellow->config->get("serverBase")."/";
  174. $_SERVER["SCRIPT_NAME"] = $this->yellow->config->get("serverBase")."yellow.php";
  175. $_REQUEST = array();
  176. $fileName = "$path/".$this->yellow->config->get("staticErrorFile");
  177. $statusCode = $this->yellow->request($statusCodeRequest);
  178. if($statusCode == $statusCodeRequest)
  179. {
  180. $statusCode = 200;
  181. $fileData = ob_get_contents();
  182. $modified = strtotime($this->yellow->page->getHeader("Last-Modified"));
  183. if(!$this->yellow->toolbox->createFile($fileName, $fileData, true) ||
  184. !$this->yellow->toolbox->modifyFile($fileName, $modified))
  185. {
  186. $statusCode = 500;
  187. $this->yellow->page->statusCode = $statusCode;
  188. $this->yellow->page->set("pageError", "Can't write file '$fileName'!");
  189. }
  190. }
  191. ob_end_clean();
  192. ++$this->system;
  193. if($statusCode >= 400)
  194. {
  195. ++$this->error;
  196. echo "ERROR building error file, ".$this->yellow->page->getStatusCode(true)."\n";
  197. }
  198. if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticError status:$statusCode file:$fileName\n";
  199. return $statusCode;
  200. }
  201. // Build static file
  202. function buildStaticFile($fileNameSource, $fileNameDest, $fileTypeMedia)
  203. {
  204. $statusCode = $this->yellow->toolbox->copyFile($fileNameSource, $fileNameDest, true) &&
  205. $this->yellow->toolbox->modifyFile($fileNameDest, filemtime($fileNameSource)) ? 200 : 500;
  206. if($fileTypeMedia) { ++$this->media; } else { ++$this->system; }
  207. if($statusCode >= 400)
  208. {
  209. ++$this->error;
  210. $fileType = $fileTypeMedia ? "media file" : "system file";
  211. $fileError = $this->yellow->toolbox->getHttpStatusFormatted($statusCode);
  212. echo "ERROR building $fileType, $fileError: Can't write file '$fileNameDest'!\n";
  213. }
  214. if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticFile status:$statusCode file:$fileNameDest\n";
  215. return $statusCode;
  216. }
  217. // Analyse static content, detect locations with arguments and pagination
  218. function analyseStaticContent($text)
  219. {
  220. $serverName = $this->yellow->config->get("serverName");
  221. $serverBase = $this->yellow->config->get("serverBase");
  222. $pagination = $this->yellow->config->get("contentPagination");
  223. preg_match_all("/<a(.*?)href=\"([^\"]+)\"(.*?)>/i", $text, $matches);
  224. foreach($matches[2] as $match)
  225. {
  226. if(preg_match("/^\w+:\/+(.*?)(\/.*)$/", $match, $tokens))
  227. {
  228. if($tokens[1] != $serverName) continue;
  229. $match = $tokens[2];
  230. }
  231. if(!$this->yellow->toolbox->isLocationArgs($match)) continue;
  232. if(substru($match, 0, strlenu($serverBase)) != $serverBase) continue;
  233. $location = rawurldecode(substru($match, strlenu($serverBase)));
  234. if(!$this->yellow->toolbox->isPaginationLocation($location, $pagination))
  235. {
  236. $location = rtrim($location, '/').'/';
  237. if(is_null($this->locationsArguments[$location]))
  238. {
  239. $this->locationsArguments[$location] = $location;
  240. if(defined("DEBUG") && DEBUG>=2) echo "YellowCommandline::analyseStaticContent type:arguments location:$location\n";
  241. }
  242. } else {
  243. $location = rtrim($location, "0..9");
  244. if(is_null($this->locationsPagination[$location]))
  245. {
  246. $this->locationsPagination[$location] = $location;
  247. if(defined("DEBUG") && DEBUG>=2) echo "YellowCommandline::analyseStaticContent type:pagination location:$location\n";
  248. }
  249. }
  250. }
  251. }
  252. // Clean static pages
  253. function cleanCommand($args)
  254. {
  255. $statusCode = 0;
  256. list($dummy, $command, $path, $location) = $args;
  257. if(empty($location) || $location[0]=='/')
  258. {
  259. $statusCode = $this->cleanStatic($path, $location);
  260. echo "Yellow $command: Static page".(empty($location) ? "s" : "")." ".($statusCode!=200 ? "not " : "")."cleaned\n";
  261. } else {
  262. $statusCode = 400;
  263. echo "Yellow $command: Invalid arguments\n";
  264. }
  265. return $statusCode;
  266. }
  267. // Clean static directories and files
  268. function cleanStatic($path, $location)
  269. {
  270. $statusCode = 200;
  271. $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/');
  272. if(empty($location))
  273. {
  274. $statusCode = max($statusCode, $this->pluginCommand(array("all", "clean")));
  275. $statusCode = max($statusCode, $this->cleanStaticDirectory($path));
  276. } else {
  277. $statusCode = $this->cleanStaticFile($path, $location);
  278. }
  279. return $statusCode;
  280. }
  281. // Clean static directory
  282. function cleanStaticDirectory($path)
  283. {
  284. $statusCode = 200;
  285. if($this->isYellowDirectory($path))
  286. {
  287. if(is_file("$path/yellow.php") || $path=="/" || !$this->yellow->toolbox->deleteDirectory($path, true))
  288. {
  289. $statusCode = 500;
  290. echo "ERROR cleaning pages: Can't delete directory '$path'!\n";
  291. }
  292. }
  293. return $statusCode;
  294. }
  295. // Clean static file
  296. function cleanStaticFile($path, $location)
  297. {
  298. $statusCode = 200;
  299. $fileName = $this->yellow->toolbox->findStaticFileFromLocation($location, $path,
  300. $this->yellow->config->get("staticDefaultFile"));
  301. if($this->isYellowDirectory($path) && is_file($fileName))
  302. {
  303. $entry = basename($fileName);
  304. if($entry!="yellow.php" && substru($entry, 0, 1)!=".")
  305. {
  306. if(!$this->yellow->toolbox->deleteFile($fileName))
  307. {
  308. $statusCode = 500;
  309. echo "ERROR cleaning pages: Can't delete file '$fileName'!\n";
  310. }
  311. }
  312. }
  313. return $statusCode;
  314. }
  315. // Forward plugin command
  316. function pluginCommand($args)
  317. {
  318. $statusCode = 0;
  319. foreach($this->yellow->plugins->plugins as $key=>$value)
  320. {
  321. if($key == "commandline") continue;
  322. if(method_exists($value["obj"], "onCommand"))
  323. {
  324. $statusCode = $value["obj"]->onCommand($args);
  325. if($statusCode != 0) break;
  326. }
  327. }
  328. return $statusCode;
  329. }
  330. // Check static configuration
  331. function checkStaticConfig()
  332. {
  333. $serverScheme = $this->yellow->config->get("serverScheme");
  334. $serverName = $this->yellow->config->get("serverName");
  335. $serverBase = $this->yellow->config->get("serverBase");
  336. return !empty($serverScheme) && !empty($serverName) &&
  337. $this->yellow->toolbox->isValidLocation($serverBase) && $serverBase!="/";
  338. }
  339. // Return static locations from file system
  340. function getStaticLocations()
  341. {
  342. $locations = array();
  343. $serverScheme = $this->yellow->config->get("serverScheme");
  344. $serverName = $this->yellow->config->get("serverName");
  345. $serverBase = $this->yellow->config->get("serverBase");
  346. $this->yellow->page->setRequestInformation($serverScheme, $serverName, $serverBase, "", "");
  347. if($this->yellow->config->isExisting("commandlineIgnoreLocation"))
  348. {
  349. $regex = "#^".$this->yellow->config->get("commandlineIgnoreLocation")."#";
  350. }
  351. foreach($this->yellow->pages->index(true, true) as $page)
  352. {
  353. if(!empty($regex) && preg_match($regex, $page->location)) continue;
  354. array_push($locations, $page->location);
  355. }
  356. if(!$this->yellow->pages->find("/") && $this->yellow->config->get("multiLanguageMode")) array_unshift($locations, "/");
  357. return $locations;
  358. }
  359. // Return static media files
  360. function getStaticFilesMedia($path)
  361. {
  362. $files = array();
  363. $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive(
  364. $this->yellow->config->get("mediaDir"), "/.*/", false, false);
  365. foreach($fileNames as $fileName) $files[$fileName] = "$path/$fileName";
  366. return $files;
  367. }
  368. // Return static system files
  369. function getStaticFilesSystem($path)
  370. {
  371. $files = array();
  372. $fileNames = $this->yellow->toolbox->getDirectoryEntries(
  373. $this->yellow->config->get("pluginDir"), "/\.(css|js|jpg|png|txt|woff)/", false, false);
  374. foreach($fileNames as $fileName)
  375. {
  376. $files[$fileName] = $path.$this->yellow->config->get("pluginLocation").basename($fileName);
  377. }
  378. $fileNames = $this->yellow->toolbox->getDirectoryEntries(
  379. $this->yellow->config->get("themeDir"), "/\.(css|js|jpg|png|txt|woff)/", false, false);
  380. foreach($fileNames as $fileName)
  381. {
  382. $files[$fileName] = $path.$this->yellow->config->get("themeLocation").basename($fileName);
  383. }
  384. $fileNames = array();
  385. array_push($fileNames, $this->yellow->config->get("commandlineSystemFile"));
  386. array_push($fileNames, $this->yellow->config->get("configDir").$this->yellow->config->get("robotsTextFile"));
  387. foreach($fileNames as $fileName) $files[$fileName] = "$path/".basename($fileName);
  388. return $files;
  389. }
  390. // Return command help
  391. function getCommandHelp()
  392. {
  393. $data = array();
  394. foreach($this->yellow->plugins->plugins as $key=>$value)
  395. {
  396. if(method_exists($value["obj"], "onCommandHelp"))
  397. {
  398. foreach(preg_split("/[\r\n]+/", $value["obj"]->onCommandHelp()) as $line)
  399. {
  400. list($command, $text) = explode(' ', $line, 2);
  401. if(!empty($command) && is_null($data[$command])) $data[$command] = $line;
  402. }
  403. }
  404. }
  405. uksort($data, strnatcasecmp);
  406. return $data;
  407. }
  408. // Return plugin version
  409. function getPluginVersion()
  410. {
  411. $data = array();
  412. foreach($this->yellow->plugins->plugins as $key=>$value) $data[$key] = "$value[class] $value[version]";
  413. usort($data, strnatcasecmp);
  414. return $data;
  415. }
  416. // Check if directory contains Yellow files
  417. function isYellowDirectory($path)
  418. {
  419. return is_file("$path/yellow.php") || is_file("$path/".$this->yellow->config->get("commandlineSystemFile"));
  420. }
  421. }
  422. $yellow->plugins->register("commandline", "YellowCommandline", YellowCommandline::Version);
  423. ?>