mostly filebased Content Presentation System
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

500 lines
18KB

  1. <?php
  2. namespace Modules;
  3. class FilesInFolders {
  4. private $folder;
  5. public $content = array();
  6. public $extras = array();
  7. private $domains = array('default'=>'default');
  8. private $keyfiles = array();
  9. public $structs = array();
  10. private $EXT=array(
  11. 'txt'=>array( 'txt', 'text', 'md' ),
  12. 'pic'=>array( 'jpg', 'jpeg', 'png', 'svg' ),
  13. 'tpl'=>array( 'html', 'htm', 'tpl' ),
  14. 'csv'=>array( 'csv' )
  15. );
  16. public $config = array();
  17. private $state=array();
  18. function __construct($folder,$conf=array()) {
  19. $f3 = \Base::instance();
  20. if(is_dir($folder)) { // are we given a valid path?
  21. $this->folder = $folder;
  22. } else { // and if not?
  23. $this->folder = $f3->get('CONTENT')."./";
  24. }
  25. if(is_array($conf)) {
  26. foreach($conf as $key=>$value) {
  27. switch($key) {
  28. case 'content':
  29. if(is_array($value)) {
  30. foreach($value as $k=>$v) {
  31. $this->domains[$k] = $v;
  32. }
  33. }
  34. break;
  35. case 'keyfiles':
  36. if(is_array($value)) {
  37. foreach($value as $k=>$v) {
  38. $this->keyfiles[$k] = $v;
  39. }
  40. }
  41. break;
  42. }
  43. }
  44. }
  45. foreach($this->domains as $domain) {
  46. $this->content[$domain] = array();
  47. }
  48. foreach($this->keyfiles as $keys=>$d) {
  49. $this->extras[$keys] = "";
  50. }
  51. }
  52. /////////////////////////////
  53. // read folder into struct //
  54. /////////////////////////////
  55. function prepare_files() {
  56. foreach ($this->domains as $k=>$v) {
  57. foreach($this->EXT as $cat=>$endings) {
  58. $this->structs[$k][$cat]=array();
  59. }
  60. }
  61. $ls = scandir($this->folder);
  62. foreach ($ls as $k=>$f) {
  63. if (!strncmp($f,'.',1)) continue; // ignore hidden files
  64. $ex=explode(".", $f);
  65. $ext=strtolower(end($ex));
  66. if (array_key_exists($ex[0],$this->domains)) {
  67. $domain_key=$ex[0];
  68. $sort_key=1;
  69. } elseif (array_key_exists($ex[0], $this->keyfiles)) {
  70. if(in_array($ext,$this->EXT[$this->keyfiles[$ex[0]]['type']])) {
  71. $this->extras[$ex[0]]=$this->folder.$f;
  72. continue;
  73. }
  74. $domain_key='default';
  75. $sort_key=0;
  76. } else {
  77. $domain_key='default';
  78. $sort_key=0;
  79. }
  80. foreach ($this->EXT as $cat=>$endings) {
  81. if (in_array($ext, $endings)) {
  82. $this->structs[$domain_key][$cat][$ex[$sort_key]] = $this->folder.$f;
  83. break;
  84. }
  85. }
  86. }
  87. foreach($this->keyfiles as $key=>$param) {
  88. if(!$this->extras[$key]) {
  89. $this->extras[$key] = self::search_up(
  90. $key,
  91. array($this->folder,$param['until']),
  92. $this->EXT[$param['type']]
  93. );
  94. }
  95. }
  96. }
  97. ///////////////////////////////////////
  98. // prepare content as per the struct //
  99. ///////////////////////////////////////
  100. function fill_content() {
  101. $md = new \Parsedown();
  102. foreach($this->domains as $domain_key=>$domain) {
  103. $this->state['current_domain'] = $domain_key;
  104. foreach($this->structs[$domain_key]['txt'] as $key=>$file) {
  105. $str = $this->read_textfile($file);
  106. $str = self::content_element_dispatcher($str);
  107. $str = $md->text($str);
  108. $str = sprintf("<div class='post'>%s</div>", $str);
  109. $this->content[$domain][$key] = sprintf(
  110. "<div class=\"item %s %s\">%s</div>",
  111. $page,
  112. $key,
  113. $str
  114. );
  115. }
  116. foreach($this->structs[$domain_key]['tpl'] as $key=>$file) {
  117. $str = file_get_contents($file);
  118. $str = \Template::instance()->render($file);
  119. $str = self::linkify($str);
  120. $this->content[$domain][$key] = sprintf(
  121. "<div class=\"item %s %s\">%s</div>",
  122. $page,
  123. $key,
  124. $str
  125. );
  126. }
  127. if (false) { // TODO: make this configurable in main.cfg
  128. foreach($this->structs[$domain_key]['pic'] as $key=>$file) {
  129. $this->content[$domain][$key] = sprintf(
  130. "<img class=\"direct\" src=\"/$file\" />"
  131. );
  132. }
  133. }
  134. foreach($this->structs[$domain_key]['csv'] as $key=>$file) {
  135. $csv = new \Modules\Ography($file,TRUE);
  136. $str="<table>";
  137. foreach($csv->entries as $entry) {
  138. $tmp="";
  139. foreach($entry as $key=>$value) {
  140. $tmp .= sprintf("<td class=\"%s\">%s</td>", $key, $value);
  141. }
  142. $str .= sprintf("<tr>%s</tr>",$tmp);
  143. }
  144. $str.="</table>";
  145. $this->content[$domain][$key] = $str;
  146. }
  147. }
  148. }
  149. //////////////////////
  150. // read config data //
  151. //////////////////////
  152. function read_config($domain=false) {
  153. foreach ($this->domains as $source=>$destination) {
  154. if (is_string($domain)) {
  155. if ($source != $domain) { continue; }
  156. } elseif (is_array($domain)) {
  157. if (!in_array($source,$domain)) { continue; }
  158. }
  159. foreach ($this->structs[$source]['txt'] as $key=>$file) {
  160. $this->read_textfile($file);
  161. }
  162. }
  163. return $this->config;
  164. }
  165. ////////////////
  166. // recursions //
  167. ////////////////
  168. function search_up($key,$paths,$ext) {
  169. $return = "";
  170. if(count($paths) == 2) {
  171. $current = $paths[0];
  172. $last_try = $paths[1];
  173. $ls=scandir($current);
  174. foreach($ls as $f) {
  175. if(!strncmp($f,'.',1)) continue; // ignore hidden files
  176. $ex=explode(".", $f);
  177. if(in_array(strtolower(end($ex)),$ext)) {
  178. if($ex[0]==$key) {
  179. $return = $current.$f;
  180. break;
  181. }
  182. }
  183. }
  184. }
  185. if ($return) {
  186. return $return;
  187. } elseif($current == $last_try) {
  188. return false;
  189. } else {
  190. $p = explode('/',$current);
  191. array_pop($p);
  192. array_pop($p);
  193. return self::search_up($key,array(implode("/",$p)."/",$last_try),$ext);
  194. }
  195. }
  196. ///////////////////////
  197. // Utility functions //
  198. ///////////////////////
  199. function read_textfile($file) {
  200. $str = file_get_contents($file);
  201. $str = self::linkify($str);
  202. $str = self::strip_comments($str);
  203. $str = self::get_config_from_content($str);
  204. return $str;
  205. }
  206. function strip_comments($str) {
  207. $single_line_comments = "/(^;.*\R)/m";
  208. $str = preg_replace($single_line_comments,"",$str);
  209. $multi_line_comments = "/\/\*.*?\*\//s";
  210. $str = preg_replace($multi_line_comments,"",$str);
  211. return $str;
  212. }
  213. function linkify($string) {
  214. $pattern = "/\s@(\w+)[=]([\w,]+)\s/";
  215. $count = 0;
  216. $new = preg_replace_callback
  217. ($pattern,
  218. function($m){
  219. $f3 = \Base::instance();
  220. return $f3->get('SITE_URL')
  221. .$f3->alias($m[1],self::$keyword."=".$m[2])
  222. ;},
  223. $string);
  224. return $new;
  225. }
  226. function get_config_from_content($string) {
  227. $f3 = \Base::instance();
  228. $f = 0;
  229. $pattern = "/#\+(\w+):\s?(.*)/";
  230. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  231. for ($i=0;$i<$f;$i++) {
  232. $string = str_replace($matches[0][$i],"",$string);
  233. $key = $matches[1][$i];
  234. $value = $matches[2][$i];
  235. if(strtolower($value) == "false") {
  236. $value = FALSE;
  237. }
  238. if(!strncmp(trim($value),'@',1)) {
  239. //var_dump($f3->get('DATA'));
  240. if (array_key_exists($key,$f3->get('DATA'))) {
  241. $DATA = $f3->get('DATA.'.$key);
  242. $conf = array($DATA['type'],$DATA['dir']);
  243. $relation = new \Modules\TOC($conf,$f3->get('CONTENT'),substr($value,1));
  244. $relation->dispatch();
  245. $this->config[$key]=array_shift($relation->entries);
  246. } else {
  247. $this->config[$key]=$value;
  248. }
  249. } else {
  250. $this->config[$key]=$value;
  251. }
  252. }
  253. $pattern = "/§>\s*(\w+):(.*?)\R[\011\040]*\R/s";
  254. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  255. for ($i=0;$i<$f;$i++) {
  256. $string = str_replace($matches[0][$i],"",$string);
  257. $key = $matches[1][$i];
  258. $value = trim($matches[2][$i]);
  259. if(strtolower($value) == "false") {
  260. $value = FALSE;
  261. }
  262. if (!strncmp($value,'@',1)) {
  263. # var_dump();
  264. if (array_key_exists($key,$f3->get('DATA'))) {
  265. $entries = explode("@",$value);
  266. array_shift($entries); // first entry is always empty
  267. $DATA = $f3->get('DATA.'.$key);
  268. $conf = array($DATA['type'],$DATA['dir']);
  269. $relation = new \Modules\TOC($conf,$f3->get('CONTENT'),$entries);
  270. $relation->dispatch();
  271. if(/*count($entries) >*/ 1) {
  272. $this->config[$key]= new CMultiple($relation->entries);
  273. } else {
  274. $this->config[$key]=array_shift($relation->entries);
  275. }
  276. } else {
  277. $this->config[$key]=$value;
  278. }
  279. } else {
  280. $this->config[$key]=$value;
  281. }
  282. }
  283. return $string;
  284. }
  285. function content_element_dispatcher($string) {
  286. $md = new \Parsedown();
  287. $f0 = 0;
  288. // find occorances of {| keyword |}
  289. $pattern = "/\{\|(.+?)\|\}/s";
  290. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  291. for ($i=0;$i<$f;$i++) {
  292. $body = preg_split("/\R/",trim($matches[1][$i]));
  293. $request = explode(":", trim(array_shift($body)));
  294. $new="";
  295. switch($request[0]) {
  296. case 'test':
  297. $new="seems to work";
  298. break;
  299. case ';':
  300. $new="";
  301. break;
  302. case 'path':
  303. $new="/".$this->folder;
  304. break;
  305. case 'space':
  306. $new=sprintf("<div style=\"height:%s;\"></div>",
  307. $request[1]
  308. );
  309. break;
  310. case 'small-text':
  311. if(count($body)) {
  312. $new=sprintf("<div class=\"f1\">%s</div>",
  313. implode("\n",$body)
  314. );
  315. } else {
  316. $new=sprintf("<span class=\"f1\">%s</span>",
  317. $request[1]
  318. );
  319. }
  320. break;
  321. case 'TOC':
  322. // throw away TOC part of request, we don't need it
  323. array_shift($request);
  324. $toc = new \Modules\TOC($request,$this->folder,$body);
  325. $toc->dispatch();
  326. $new=sprintf("<div class=\"TOC %s\">%s</div>",
  327. array_shift($request),
  328. $toc);
  329. break;
  330. case 'calendar':
  331. array_shift($request);
  332. $conf = array('path', $this->folder);
  333. $v = $this->read_config();
  334. $cal = new \Modules\CCalendar($v,$conf);
  335. //$cal->dispatch();
  336. //$conf = array('path', $this->folder);
  337. //$v = $this->read_config();
  338. $new=sprintf("<div class=\"calendar\">%s</div>",
  339. $cal);
  340. break;
  341. case 'header':
  342. array_shift($request);
  343. $conf = array('path', $this->folder);
  344. $v = $this->read_config();
  345. switch (array_shift($request)) {
  346. case 'event':
  347. $el = new CEvent($v,$conf);
  348. $el->set_layout('archive');
  349. $new = $el;
  350. break;
  351. case 'concert':
  352. $el = new CConcert($v,$conf);
  353. $el->set_layout('header');
  354. $new = $el;
  355. break;
  356. }
  357. break;
  358. case 'image':
  359. $key=$request[1];
  360. $image="";
  361. if(!$key) {
  362. $new=self::warn("Content Module \"Image\" needs name of a file (without extension)");
  363. break;
  364. }
  365. foreach($this->structs as $domain=>$destination) {
  366. if(array_key_exists($key, $this->structs[$domain]['pic'])) {
  367. $image = $this->structs[$domain]['pic'][$key];
  368. unset($this->structs[$domain]['pic'][$key]);
  369. unset($this->content[$this->domains[$domain]][$key]);
  370. break;
  371. }
  372. }
  373. if ($image) {
  374. if( in_array($request[2],array('left','right','full'))) {
  375. $class = $request[2];
  376. } else {
  377. $class = 'left';
  378. }
  379. $img = new \Image($image);
  380. $ratio = ($img->width() >= $img->height())
  381. ? "landscape"
  382. : "portrait"
  383. ;
  384. $cached = new CachedImage($image);
  385. foreach ($body as $k=>$line) {
  386. if (strpos($line,"©") !== FALSE
  387. || strpos($line,"&copy;") !== FALSE) {
  388. $body[$k] = sprintf("<span class=\"copyright\">%s</span>",$line);
  389. }
  390. }
  391. $new=sprintf("<div class='image-container %s'>"
  392. ."<div class=\"media %s\"><a href=\"/%s\" data-featherlight=\"image\"><img src=\"/%s\" alt=\"user supplied image\" /></a></div>"
  393. ."<img src=\"/%s\" style=\"display:none;\" alt=\"user supplied image\" />"
  394. ."<div class=\"caption\">%s</div>"
  395. ."</div>",
  396. $class,
  397. $ratio,
  398. $cached->get_src(1400),
  399. $cached->get_src(500),
  400. $cached->get_src(1400),
  401. $md->text(implode("\n",$body))
  402. );
  403. } else {
  404. $new=self::warn("image \"$key\" not found");
  405. }
  406. break;
  407. case 'youtube':
  408. if( in_array($request[2],array('left','right','full'))) {
  409. $class = $request[2];
  410. } else {
  411. $class = 'left';
  412. }
  413. $video=sprintf("<iframe width=\"700\" height=\"394\" class=\"ytvideo\" "
  414. ."src=\"https://www.youtube.com/embed/%s\"></iframe>",
  415. $request[1]);
  416. $thumbnail = sprintf("<div class=\"video-thumbnail\"><a href=\"%s\" data-featherlight=\"#%s\">"
  417. ."<img class=\"thumbnail\" src=\"https://img.youtube.com/vi/%s/mqdefault.jpg\" alt=\"video-preview\"/>"
  418. ."<img class=\"play-button\" src=\"%s\" alt=\"play-button\" />"
  419. ."</a></div><div class=\"lightbox\" id=\"%s\" >%s</div>",
  420. "https://www.youtube.com/watch?v=".$request[1],
  421. $request[1],
  422. $request[1],
  423. "/rsc/img/play-button.png",
  424. $request[1],
  425. $video
  426. );
  427. foreach ($body as $k=>$line) {
  428. if (strpos($line,"©") !== FALSE
  429. || strpos($line,"&copy;") !== FALSE) {
  430. $body[$k] = sprintf("<span class=\"copyright\">%s</span>",$line);
  431. }
  432. }
  433. $new=sprintf("<div class='video-container %s'>"
  434. ."<div class=\"media\">%s</div>"
  435. ."<div class=\"caption\">%s</div>"
  436. ."</div>",
  437. $class,
  438. $thumbnail,
  439. $md->text(implode("\n",$body)));
  440. break;
  441. default:
  442. $new=self::warn("Content Module \"".$request[0]."\" unknown");
  443. break;
  444. }
  445. $string = str_replace($matches[0][$i],$new,$string);
  446. }
  447. return $string;
  448. }
  449. function warn($message) {
  450. return sprintf("<div class=\"warning\">%s</div>",$message);
  451. }
  452. }