mostly filebased Content Presentation System
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

595 lines
22KB

  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. if($this->extras[$key]) {
  96. if ($param['type'] == 'txt') {
  97. $this->read_textfile($this->extras[$key]);
  98. }
  99. }
  100. }
  101. }
  102. ///////////////////////////////////////
  103. // prepare content as per the struct //
  104. ///////////////////////////////////////
  105. function fill_content() {
  106. $md = new \Parsedown();
  107. foreach($this->domains as $domain_key=>$domain) {
  108. $this->state['current_domain'] = $domain_key;
  109. foreach($this->structs[$domain_key]['txt'] as $key=>$file) {
  110. $str = $this->read_textfile($file);
  111. $str = self::content_element_dispatcher($str);
  112. $str = $md->text($str);
  113. $str = sprintf("<div class='post'>%s</div>", $str);
  114. $this->content[$domain][$key] = sprintf(
  115. "<div class=\"item %s %s\">%s</div>",
  116. $page,
  117. $key,
  118. $str
  119. );
  120. }
  121. foreach($this->structs[$domain_key]['tpl'] as $key=>$file) {
  122. $str = file_get_contents($file);
  123. $str = \Template::instance()->render($file);
  124. $str = self::linkify($str);
  125. $this->content[$domain][$key] = sprintf(
  126. "<div class=\"item %s %s\">%s</div>",
  127. $page,
  128. $key,
  129. $str
  130. );
  131. }
  132. if (false) { // TODO: make this configurable in main.cfg
  133. foreach($this->structs[$domain_key]['pic'] as $key=>$file) {
  134. $this->content[$domain][$key] = sprintf(
  135. "<img class=\"direct\" src=\"/$file\" />"
  136. );
  137. }
  138. }
  139. foreach($this->structs[$domain_key]['csv'] as $key=>$file) {
  140. $csv = new \Modules\Ography($file,TRUE);
  141. $str="<table>";
  142. foreach($csv->entries as $entry) {
  143. $tmp="";
  144. foreach($entry as $key=>$value) {
  145. $tmp .= sprintf("<td class=\"%s\">%s</td>", $key, $value);
  146. }
  147. $str .= sprintf("<tr>%s</tr>",$tmp);
  148. }
  149. $str.="</table>";
  150. $this->content[$domain][$key] = $str;
  151. }
  152. }
  153. }
  154. //////////////////////
  155. // read config data //
  156. //////////////////////
  157. function read_config($domain=false) {
  158. foreach ($this->domains as $source=>$destination) {
  159. if (is_string($domain)) {
  160. if ($source != $domain) { continue; }
  161. } elseif (is_array($domain)) {
  162. if (!in_array($source,$domain)) { continue; }
  163. }
  164. foreach ($this->structs[$source]['txt'] as $key=>$file) {
  165. $this->read_textfile($file);
  166. }
  167. }
  168. return $this->config;
  169. }
  170. ////////////////
  171. // recursions //
  172. ////////////////
  173. function search_up($key,$paths,$ext) {
  174. $return = "";
  175. if(count($paths) == 2) {
  176. $current = $paths[0];
  177. $last_try = $paths[1];
  178. $ls=scandir($current);
  179. foreach($ls as $f) {
  180. if(!strncmp($f,'.',1)) continue; // ignore hidden files
  181. $ex=explode(".", $f);
  182. if(in_array(strtolower(end($ex)),$ext)) {
  183. if($ex[0]==$key) {
  184. $return = $current.$f;
  185. break;
  186. }
  187. }
  188. }
  189. }
  190. if ($return) {
  191. return $return;
  192. } elseif($current == $last_try) {
  193. return false;
  194. } else {
  195. $p = explode('/',$current);
  196. array_pop($p);
  197. array_pop($p);
  198. return self::search_up($key,array(implode("/",$p)."/",$last_try),$ext);
  199. }
  200. }
  201. ///////////////////////
  202. // Utility functions //
  203. ///////////////////////
  204. function read_textfile($file) {
  205. $str = file_get_contents($file);
  206. $str = self::linkify($str);
  207. $str = self::strip_comments($str);
  208. $str = self::get_config_from_content($str);
  209. return $str;
  210. }
  211. function strip_comments($str) {
  212. $single_line_comments = "/(^;.*\R)/m";
  213. $str = preg_replace($single_line_comments,"",$str);
  214. $multi_line_comments = "/\/\*.*?\*\//s";
  215. $str = preg_replace($multi_line_comments,"",$str);
  216. return $str;
  217. }
  218. function linkify($string) {
  219. $pattern = "/\s@(\w+)[=]([\w,]+)\s/";
  220. $count = 0;
  221. $new = preg_replace_callback
  222. ($pattern,
  223. function($m){
  224. $f3 = \Base::instance();
  225. return $f3->get('SITE_URL')
  226. .$f3->alias($m[1],self::$keyword."=".$m[2])
  227. ;},
  228. $string);
  229. return $new;
  230. }
  231. function get_config_from_content($string) {
  232. $f3 = \Base::instance();
  233. $f = 0;
  234. $pattern = "/#\+(\w+):\s?(.*)/";
  235. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  236. for ($i=0;$i<$f;$i++) {
  237. $string = str_replace($matches[0][$i],"",$string);
  238. $key = $matches[1][$i];
  239. $value = $matches[2][$i];
  240. if(strtolower($value) == "false") {
  241. $value = FALSE;
  242. }
  243. if(!strncmp(trim($value),'@',1)) {
  244. //var_dump($f3->get('DATA'));
  245. if (array_key_exists($key,$f3->get('DATA'))) {
  246. $DATA = $f3->get('DATA.'.$key);
  247. $conf = array($DATA['type'],$DATA['dir']);
  248. $relation = new \Modules\TOC($conf,$f3->get('CONTENT'),substr($value,1));
  249. $relation->dispatch();
  250. $this->config[$key]=array_shift($relation->entries);
  251. } else {
  252. $this->config[$key]=$value;
  253. }
  254. } else {
  255. $this->config[$key]=$value;
  256. }
  257. }
  258. $pattern = "/§>\s*(\w+):(.*?)\R[\011\040]*\R/s";
  259. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  260. for ($i=0;$i<$f;$i++) {
  261. $string = str_replace($matches[0][$i],"",$string);
  262. $key = $matches[1][$i];
  263. $value = trim($matches[2][$i]);
  264. if(strtolower($value) == "false") {
  265. $value = FALSE;
  266. }
  267. if (!strncmp($value,'@',1)) {
  268. # var_dump();
  269. if (array_key_exists($key,$f3->get('DATA'))) {
  270. $entries = explode("@",$value);
  271. array_shift($entries); // first entry is always empty
  272. $DATA = $f3->get('DATA.'.$key);
  273. $conf = array($DATA['type'],$DATA['dir']);
  274. $relation = new \Modules\TOC($conf,$f3->get('CONTENT'),$entries);
  275. $relation->dispatch();
  276. if(/*count($entries) >*/ 1) {
  277. $this->config[$key]= new CMultiple($relation->entries);
  278. } else {
  279. $this->config[$key]=array_shift($relation->entries);
  280. }
  281. } else {
  282. $this->config[$key]=$value;
  283. }
  284. } else {
  285. $this->config[$key]=$value;
  286. }
  287. }
  288. return $string;
  289. }
  290. function content_element_dispatcher($string) {
  291. $f3 = \Base::instance();
  292. $md = new \Parsedown();
  293. $f0 = 0;
  294. // find occorances of {| keyword |}
  295. $pattern = "/\{\|(.+?)\|\}/s";
  296. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  297. for ($i=0;$i<$f;$i++) {
  298. $body = preg_split("/\R/",trim($matches[1][$i]));
  299. $request = explode(":", trim(array_shift($body)));
  300. $new="";
  301. switch($request[0]) {
  302. case 'test':
  303. $new="seems to work";
  304. break;
  305. case ';':
  306. $new="";
  307. break;
  308. case 'path':
  309. $new="/".$this->folder;
  310. break;
  311. case 'space':
  312. $new=sprintf("<div style=\"height:%s;\"></div>",
  313. $request[1]
  314. );
  315. break;
  316. case 'small-text':
  317. if(count($body)) {
  318. $new=sprintf("<div class=\"f1\">%s</div>",
  319. implode("\n",$body)
  320. );
  321. } else {
  322. $new=sprintf("<span class=\"f1\">%s</span>",
  323. $request[1]
  324. );
  325. }
  326. break;
  327. case 'TOC':
  328. // throw away TOC part of request, we don't need it
  329. array_shift($request);
  330. $toc = new \Modules\TOC($request,$this->folder,$body);
  331. $toc->dispatch();
  332. $new=sprintf("<div class=\"TOC %s\">%s</div>",
  333. array_shift($request),
  334. $toc);
  335. break;
  336. case 'box':
  337. array_shift($request); //get rid of identifier
  338. $type = array_shift($request);
  339. $pics = explode(":",array_shift($body));
  340. $pic = $pics[0];
  341. $pic_hover = count($pics) > 1 ? $pics[1] : $pic;
  342. $cap1 = array_shift($body);
  343. $cap2 = array_shift($body);
  344. $cap1_hover = array_shift($body);
  345. $cap2_hover = array_shift($body);
  346. $pic = new CachedImage($this->folder.$pic);
  347. $pic_hover = new CachedImage($this->folder.$pic_hover);
  348. switch($type) {
  349. case 'download':
  350. $file = "/".$this->folder.implode(":",$request);
  351. $link='href="'.$file.'" download ';
  352. break;
  353. case 'lightbox':
  354. $body = implode("\n",$body);
  355. if (count($request) >= 2) {
  356. $body=str_replace([$request[0],$request[1]],["{|","|}"],$body);
  357. $body=$this->content_element_dispatcher($body);
  358. }
  359. $hash=md5($body);
  360. $add=sprintf('<div id="%s" class="content_elment_box_body">%s</div>',
  361. $hash,
  362. $md->text($body));
  363. $link='href="#" data-featherlight="#'.$hash.'" ';
  364. break;
  365. case 'internal':
  366. $dest=implode(":",$request);
  367. $link=sprintf('href="%s" ',$f3->get('SITE_URL')."/".$dest);
  368. break;
  369. case 'external':
  370. $dest=implode(":",$request);
  371. $link=sprintf('href="%s" target="_blank"',$dest);
  372. break;
  373. default:
  374. $link='href="#"';
  375. break;
  376. }
  377. $new=sprintf("<div><a class=\"content_element_box\" %s><div class=\"content_element_box\">
  378. <div class=\"image\">
  379. <img class=\"standard\" src=\"%s\" /><img class=\"hover\" src=\"%s\" />
  380. </div>
  381. <div class=\"caption standard\">
  382. <span class=\"first\">%s</span><br>
  383. <span class=\"second\">%s</span>
  384. </div>
  385. <div class=\"caption hover\">
  386. <span class=\"first\">%s</span><br>
  387. <span class=\"second\">%s</span>
  388. </div></div></a>%s</div>",
  389. $link,
  390. $pic->get_src(400),
  391. $pic_hover->get_src(400),
  392. $cap1,$cap2,
  393. $cap1_hover,$cap2_hover,
  394. $add
  395. );
  396. break;
  397. case 'calendar':
  398. array_shift($request);
  399. $conf = array('path', $this->folder);
  400. $v = $this->read_config();
  401. $cal = new \Modules\CCalendar($v,$conf);
  402. $new=sprintf("<div class=\"calendar\">%s</div>",
  403. $cal);
  404. break;
  405. case 'header':
  406. array_shift($request);
  407. $conf = array('path', $this->folder);
  408. $v = $this->read_config();
  409. switch (array_shift($request)) {
  410. case 'event':
  411. $el = new CEvent($v,$conf);
  412. $el->set_layout('archive');
  413. $new = $el;
  414. break;
  415. case 'concert':
  416. $el = new CConcert($v,$conf);
  417. $el->set_layout('header');
  418. $new = $el;
  419. break;
  420. }
  421. break;
  422. case 'image':
  423. $key=$request[1];
  424. $image="";
  425. if(!$key) {
  426. $new=self::warn("Content Module \"Image\" needs name of a file (without extension)");
  427. break;
  428. }
  429. foreach($this->structs as $domain=>$destination) {
  430. if(array_key_exists($key, $this->structs[$domain]['pic'])) {
  431. $image = $this->structs[$domain]['pic'][$key];
  432. unset($this->structs[$domain]['pic'][$key]);
  433. unset($this->content[$this->domains[$domain]][$key]);
  434. break;
  435. }
  436. }
  437. if ($image) {
  438. if( in_array($request[2],array('left','right','full'))) {
  439. $class = $request[2];
  440. } else {
  441. $class = 'left';
  442. }
  443. $img = new \Image($image);
  444. $ratio = ($img->width() >= $img->height())
  445. ? "landscape"
  446. : "portrait"
  447. ;
  448. $cached = new CachedImage($image);
  449. foreach ($body as $k=>$line) {
  450. if (strpos($line,"©") !== FALSE
  451. || strpos($line,"&copy;") !== FALSE) {
  452. $body[$k] = sprintf("<span class=\"copyright\">%s</span>",$line);
  453. }
  454. }
  455. $new=sprintf("<div class='image-container %s'>"
  456. ."<div class=\"media %s\"><a href=\"%s\" data-featherlight=\"image\"><img src=\"%s\" alt=\"user supplied image\" /></a></div>"
  457. ."<img src=\"%s\" style=\"display:none;\" alt=\"user supplied image\" />"
  458. ."<div class=\"caption\">%s</div>"
  459. ."</div>",
  460. $class,
  461. $ratio,
  462. $cached->get_src(1400),
  463. $cached->get_src(500),
  464. $cached->get_src(1400),
  465. $md->text(implode("\n",$body))
  466. );
  467. } else {
  468. $new=self::warn("image \"$key\" not found");
  469. }
  470. break;
  471. case 'devide':
  472. $new = "<div>";
  473. $contents = explode($request[1],implode("\n",$body));
  474. $counter=0;
  475. foreach($contents as $part) {
  476. $counter++;
  477. if (count($request) >= 4) {
  478. $part=str_replace([$request[2],$request[3]],["{|","|}"],$part);
  479. $part=$this->content_element_dispatcher($part);
  480. }
  481. $new .= sprintf("<div>%s</div>",
  482. $md->text($part)
  483. );
  484. }
  485. $new.="</div>";
  486. break;
  487. case 'page':
  488. array_shift($request);
  489. $target = array_shift($request);
  490. $class = array_shift($request);
  491. $folder = $this->folder.$target."/";
  492. $f = new \Modules\FilesInFolders($folder);
  493. $f->prepare_files();
  494. $f->fill_content();
  495. $new = sprintf('<div class="content_element_page %s">%s</div>',
  496. $class,
  497. implode("\n",$f->content['default']));
  498. break;
  499. case 'youtube':
  500. $vid=array_shift($request);
  501. $pos= array_shift($request);
  502. if( in_array($pos,array('left','right','full'))) {
  503. $class = $pos;
  504. } else {
  505. $class = 'left';
  506. }
  507. $video=sprintf("<iframe width=\"700\" height=\"394\" class=\"ytvideo\" "
  508. ."src=\"https://www.youtube.com/embed/%s\"></iframe>",
  509. $vid);
  510. $thumbnail = sprintf("<div class=\"video-thumbnail\"><a href=\"%s\" data-featherlight=\"#%s\">"
  511. ."<img class=\"thumbnail\" src=\"https://img.youtube.com/vi/%s/mqdefault.jpg\" alt=\"video-preview\"/>"
  512. ."<img class=\"play-button\" src=\"%s\" alt=\"play-button\" />"
  513. ."</a></div><div class=\"lightbox\" id=\"%s\" >%s</div>",
  514. "https://www.youtube.com/watch?v=".$vid,
  515. $vid,
  516. $vid,
  517. "/rsc/img/play-button.png",
  518. $vid,
  519. $video
  520. );
  521. foreach ($body as $k=>$line) {
  522. if (strpos($line,"©") !== FALSE
  523. || strpos($line,"&copy;") !== FALSE) {
  524. $body[$k] = sprintf("<span class=\"copyright\">%s</span>",$line);
  525. }
  526. }
  527. $new=sprintf("<div class='video-container %s'>"
  528. ."<div class=\"media\">%s</div>"
  529. ."<div class=\"caption\">%s</div>"
  530. ."</div>",
  531. $class,
  532. $thumbnail,
  533. $md->text(implode("\n",$body)));
  534. break;
  535. default:
  536. $new=self::warn("Content Module \"".$request[0]."\" unknown");
  537. break;
  538. }
  539. $string = str_replace($matches[0][$i],$new,$string);
  540. }
  541. return $string;
  542. }
  543. function warn($message) {
  544. return sprintf("<div class=\"warning\">%s</div>",$message);
  545. }
  546. }