mostly filebased Content Presentation System
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

726 rindas
28KB

  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. debug("about to construct FIF: $folder");
  20. $f3 = \Base::instance();
  21. if(is_dir($folder)) { // are we given a valid path?
  22. $this->folder = $folder;
  23. } else { // and if not?
  24. $this->folder = $f3->get('CONTENT')."./";
  25. }
  26. if(is_array($conf)) {
  27. foreach($conf as $key=>$value) {
  28. switch($key) {
  29. case 'content':
  30. if(is_array($value)) {
  31. foreach($value as $k=>$v) {
  32. $this->domains[$k] = $v;
  33. }
  34. }
  35. break;
  36. case 'keyfiles':
  37. if(is_array($value)) {
  38. foreach($value as $k=>$v) {
  39. $this->keyfiles[$k] = $v;
  40. }
  41. }
  42. break;
  43. }
  44. }
  45. }
  46. foreach($this->domains as $domain) {
  47. $this->content[$domain] = array();
  48. }
  49. foreach($this->keyfiles as $keys=>$d) {
  50. $this->extras[$keys] = "";
  51. }
  52. debug("constructed files in folder: $folder");
  53. }
  54. /////////////////////////////
  55. // read folder into struct //
  56. /////////////////////////////
  57. function prepare_files() {
  58. foreach ($this->domains as $k=>$v) {
  59. foreach($this->EXT as $cat=>$endings) {
  60. $this->structs[$k][$cat]=array();
  61. }
  62. }
  63. $ls = scandir($this->folder);
  64. foreach ($ls as $k=>$f) {
  65. if (!strncmp($f,'.',1)) continue; // ignore hidden files
  66. $ex=explode(".", $f);
  67. $ext=strtolower(end($ex));
  68. if (array_key_exists($ex[0],$this->domains)) {
  69. $domain_key=$ex[0];
  70. $sort_key=1;
  71. } elseif (array_key_exists($ex[0], $this->keyfiles)) {
  72. if(in_array($ext,$this->EXT[$this->keyfiles[$ex[0]]['type']])) {
  73. $this->extras[$ex[0]]=$this->folder.$f;
  74. continue;
  75. }
  76. $domain_key='default';
  77. $sort_key=0;
  78. } else {
  79. $domain_key='default';
  80. $sort_key=0;
  81. }
  82. foreach ($this->EXT as $cat=>$endings) {
  83. if (in_array($ext, $endings)) {
  84. $this->structs[$domain_key][$cat][$ex[$sort_key]] = $this->folder.$f;
  85. break;
  86. }
  87. }
  88. }
  89. foreach($this->keyfiles as $key=>$param) {
  90. if(!$this->extras[$key]) {
  91. $this->extras[$key] = self::search_up(
  92. $key,
  93. array($this->folder,$param['until']),
  94. $this->EXT[$param['type']]
  95. );
  96. }
  97. if($this->extras[$key]) {
  98. if ($param['type'] == 'txt') {
  99. $this->read_textfile($this->extras[$key]);
  100. }
  101. }
  102. }
  103. }
  104. ///////////////////////////////////////
  105. // prepare content as per the struct //
  106. ///////////////////////////////////////
  107. function fill_content() {
  108. $md = new \freaParsedown();
  109. $md->deactivate_ol();
  110. //var_dump($md->get_BlockTypes());
  111. foreach($this->domains as $domain_key=>$domain) {
  112. // don't act on hidden files
  113. if ($domain == 'hidden') { continue; }
  114. $this->state['current_domain'] = $domain_key;
  115. foreach($this->structs[$domain_key]['txt'] as $key=>$file) {
  116. $str = $this->read_textfile($file);
  117. $str = self::content_element_dispatcher($str);
  118. $str = $md->text($str);
  119. //$str = sprintf("%s", $str);
  120. $this->content[$domain][$key] = sprintf(
  121. "<div class=\"item %s %s\">%s</div>",
  122. $page,
  123. $key,
  124. $str
  125. );
  126. }
  127. foreach($this->structs[$domain_key]['tpl'] as $key=>$file) {
  128. $str = file_get_contents($file);
  129. $str = \Template::instance()->render($file);
  130. $str = self::linkify($str);
  131. $this->content[$domain][$key] = sprintf(
  132. "<div class=\"item %s %s\">%s</div>",
  133. $page,
  134. $key,
  135. $str
  136. );
  137. }
  138. if (false) { // TODO: make this configurable in main.cfg
  139. foreach($this->structs[$domain_key]['pic'] as $key=>$file) {
  140. $this->content[$domain][$key] = sprintf(
  141. "<img class=\"direct\" src=\"/$file\" />"
  142. );
  143. }
  144. }
  145. foreach($this->structs[$domain_key]['csv'] as $key=>$file) {
  146. $csv = new \Modules\Ography($file,TRUE);
  147. $str="<table>";
  148. foreach($csv->entries as $entry) {
  149. $tmp="";
  150. foreach($entry as $key=>$value) {
  151. $tmp .= sprintf("<td class=\"%s\">%s</td>", $key, $value);
  152. }
  153. $str .= sprintf("<tr>%s</tr>",$tmp);
  154. }
  155. $str.="</table>";
  156. $this->content[$domain][$key] = $str;
  157. }
  158. }
  159. }
  160. //////////////////////
  161. // read config data //
  162. //////////////////////
  163. function read_config($domain=false) {
  164. foreach ($this->domains as $source=>$destination) {
  165. if (is_string($domain)) {
  166. if ($source != $domain) { continue; }
  167. } elseif (is_array($domain)) {
  168. if (!in_array($source,$domain)) { continue; }
  169. }
  170. foreach ($this->structs[$source]['txt'] as $key=>$file) {
  171. $this->read_textfile($file);
  172. }
  173. }
  174. return $this->config;
  175. }
  176. ////////////////
  177. // recursions //
  178. ////////////////
  179. function search_up($key,$paths,$ext) {
  180. $return = "";
  181. if(count($paths) == 2) {
  182. $current = $paths[0];
  183. $last_try = $paths[1];
  184. $ls=scandir($current);
  185. foreach($ls as $f) {
  186. if(!strncmp($f,'.',1)) continue; // ignore hidden files
  187. $ex=explode(".", $f);
  188. if(in_array(strtolower(end($ex)),$ext)) {
  189. if($ex[0]==$key) {
  190. $return = $current.$f;
  191. break;
  192. }
  193. }
  194. }
  195. }
  196. if ($return) {
  197. return $return;
  198. } elseif($current == $last_try) {
  199. return false;
  200. } else {
  201. $p = explode('/',$current);
  202. array_pop($p);
  203. array_pop($p);
  204. return self::search_up($key,array(implode("/",$p)."/",$last_try),$ext);
  205. }
  206. }
  207. ///////////////////////
  208. // Utility functions //
  209. ///////////////////////
  210. function read_textfile($file) {
  211. debug("about to read file: $file");
  212. $str = file_get_contents($file);
  213. debug("read file: $file");
  214. $str = self::linkify($str);
  215. $str = self::strip_comments($str);
  216. $str = self::get_config_from_content($str);
  217. debug("processed file: $file");
  218. return $str;
  219. }
  220. function strip_comments($str) {
  221. $single_line_comments = "/(^;.*\R)/m";
  222. $str = preg_replace($single_line_comments,"",$str);
  223. $multi_line_comments = "/\/\*.*?\*\//s";
  224. $str = preg_replace($multi_line_comments,"",$str);
  225. return $str;
  226. }
  227. function linkify($string) {
  228. $pattern = "/\s@(\w+)[=]([\w,]+)\s/";
  229. $count = 0;
  230. $new = preg_replace_callback
  231. ($pattern,
  232. function($m){
  233. $f3 = \Base::instance();
  234. return $f3->get('SITE_URL')
  235. .$f3->alias($m[1],self::$keyword."=".$m[2])
  236. ;},
  237. $string);
  238. return $new;
  239. }
  240. function get_config_from_content($string) {
  241. $f3 = \Base::instance();
  242. $f = 0;
  243. $pattern = "/#\+(\w+):\s?(.*)/";
  244. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  245. for ($i=0;$i<$f;$i++) {
  246. $string = str_replace($matches[0][$i],"",$string);
  247. $key = $matches[1][$i];
  248. $value = $matches[2][$i];
  249. if(strtolower($value) == "false") {
  250. $value = FALSE;
  251. }
  252. if(!strncmp(trim($value),'@',1)) {
  253. //var_dump($f3->get('DATA'));
  254. if (array_key_exists($key,$f3->get('DATA'))) {
  255. $DATA = $f3->get('DATA.'.$key);
  256. $conf = array($DATA['type'],$DATA['dir']);
  257. $relation = new \Modules\TOC($conf,$f3->get('CONTENT'),substr($value,1));
  258. $relation->dispatch();
  259. $this->config[$key]=array_shift($relation->entries);
  260. } else {
  261. $this->config[$key]=$value;
  262. }
  263. } else {
  264. $this->config[$key]=$value;
  265. }
  266. }
  267. $pattern = "/§>\s*(\w+):(.*?)\R[\011\040]*\R/s";
  268. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  269. for ($i=0;$i<$f;$i++) {
  270. $string = str_replace($matches[0][$i],"",$string);
  271. $key = $matches[1][$i];
  272. $value = trim($matches[2][$i]);
  273. if(strtolower($value) == "false") {
  274. $value = FALSE;
  275. }
  276. if (!strncmp($value,'@',1)) {
  277. # var_dump();
  278. if (array_key_exists($key,$f3->get('DATA'))) {
  279. $entries = explode("@",$value);
  280. array_shift($entries); // first entry is always empty
  281. $DATA = $f3->get('DATA.'.$key);
  282. $conf = array($DATA['type'],$DATA['dir']);
  283. $relation = new \Modules\TOC($conf,$f3->get('CONTENT'),$entries);
  284. $relation->dispatch();
  285. if(/*count($entries) >*/ 1) {
  286. $this->config[$key]= new CMultiple($relation->entries);
  287. } else {
  288. $this->config[$key]=array_shift($relation->entries);
  289. }
  290. } else {
  291. $this->config[$key]=$value;
  292. }
  293. } else {
  294. $this->config[$key]=$value;
  295. }
  296. }
  297. return $string;
  298. }
  299. function content_element_dispatcher($string) {
  300. $f3 = \Base::instance();
  301. $md = new \freaParsedown();
  302. $md->deactivate_ol();
  303. $f0 = 0;
  304. // find occorances of {| keyword |}
  305. $pattern = "/\{\|(.+?)\|\}/s";
  306. $f = preg_match_all($pattern, $string,$matches,PREG_PATTERN_ORDER);
  307. for ($i=0;$i<$f;$i++) {
  308. $body = preg_split("/\R/",trim($matches[1][$i]));
  309. $request = explode(":", trim(array_shift($body)));
  310. $new="";
  311. switch($request[0]) {
  312. case 'test':
  313. $new="seems to work";
  314. break;
  315. case ';':
  316. $new="";
  317. break;
  318. case 'path':
  319. $new="/".$this->folder;
  320. break;
  321. case 'space':
  322. $new=sprintf("<div style=\"height:%s;\"></div>",
  323. $request[1]
  324. );
  325. break;
  326. case 'span':
  327. $new=sprintf("<span class=\"%s\">%s</span>",
  328. $request[1],
  329. $request[2] ? : implode("\n",$body)
  330. );
  331. break;
  332. case 'small-text':
  333. if(count($body)) {
  334. $new=sprintf("<div class=\"smalltext\">%s</div>",
  335. implode("\n",$body)
  336. );
  337. } else {
  338. $new=sprintf("<span class=\"smalltext\">%s</span>",
  339. $request[1]
  340. );
  341. }
  342. break;
  343. case 'TOC':
  344. // throw away TOC part of request, we don't need it
  345. array_shift($request);
  346. $toc = new \Modules\TOC($request,$this->folder,$body);
  347. $toc->dispatch();
  348. $new=sprintf("<div class=\"TOC %s\">%s</div>",
  349. array_shift($request),
  350. $toc);
  351. break;
  352. case 'box':
  353. array_shift($request); //get rid of identifier
  354. $type = array_shift($request);
  355. $pics = explode(":",array_shift($body));
  356. $pic = $pics[0];
  357. $pic_hover = count($pics) > 1 ? $pics[1] : $pic;
  358. if (count($body) >= 3) {
  359. $caption = ['normal'=>['cap1' => array_shift($body),
  360. 'cap2' => array_shift($body)],
  361. 'hover'=> ['cap1' => array_shift($body),
  362. 'cap2' => array_shift($body)]];
  363. $caption_html=[];
  364. foreach ($caption as $state => $set) {
  365. if(!$set['cap2']) {
  366. $caption_html[$state] = [
  367. sprintf('<span class="first">&nbsp;</span>'),
  368. sprintf('<span class="first">%s</span>',$set['cap1'])
  369. ];
  370. } else if (!$set['cap1'] && $set['cap2']) {
  371. $caption_html[$state] = [
  372. sprintf('<span class="second">%s</span>',$set['cap2'])
  373. ];
  374. } else {
  375. $caption_html[$state] = [
  376. sprintf('<span class="first">%s</span>',$set['cap1']?:"&nbsp;"),
  377. sprintf('<span class="second">%s</span>',$set['cap2'])
  378. ];
  379. }
  380. }
  381. $has_caption = TRUE;
  382. } else {
  383. $has_caption = FALSE;
  384. }
  385. if (file_exists($this->folder.$pic)) {
  386. $pic = $this->folder.$pic;
  387. } else {
  388. $pic = $f3->get('RESOURCES')."img/default_img.png";
  389. }
  390. if (file_exists($this->folder.$pic_hover)) {
  391. $pic_hover = $this->folder.$pic_hover;
  392. } else {
  393. $pic_hover = $pic;
  394. }
  395. if(0) {
  396. $PIC = new \Image($pic);
  397. $orientation = $PIC->width() > $PIC->height()
  398. ? 'landscape'
  399. : 'portrait'
  400. ;
  401. unset($PIC);
  402. } else {
  403. list($wwidth, $hheight) = getimagesize($pic);
  404. $orientation = $wwidth > $hheight ? 'landscape' : 'portrait';
  405. }
  406. $pic = new CachedImage($pic);
  407. $pic_hover = new CachedImage($pic_hover);
  408. $class="";
  409. $add="";
  410. switch($type) {
  411. case 'plain':
  412. $link=false;
  413. break;
  414. case 'download':
  415. $file = "/".$this->folder.implode(":",$request);
  416. $link='href="'.$file.'" download ';
  417. break;
  418. case 'lightbox':
  419. $body = implode("\n",$body);
  420. if (count($request) % 2) {
  421. $class = array_pop($request);
  422. }
  423. if (count($request) >= 2) {
  424. $body=str_replace([$request[0],$request[1]],["{|","|}"],$body);
  425. $body=$this->content_element_dispatcher($body);
  426. }
  427. $hash=md5($body);
  428. $add=sprintf("<div id=\"%s\" class=\"content_elment_box_body\">\n%s\n</div>",
  429. $hash,
  430. $md->text($body));
  431. $link='href="#" data-featherlight="#'.$hash.'" ';
  432. break;
  433. case 'internal':
  434. $dest=implode(":",$request);
  435. $data = [];
  436. $base = substr($dest,0,strpos($dest,'?') ? : strlen($dest));
  437. parse_str(parse_url($dest,PHP_URL_QUERY),$data);
  438. $fragment = parse_url($dest,PHP_URL_FRAGMENT);
  439. if ($f3->get('LANG') != $f3->get('default_lang')) {
  440. if (!array_key_exists('lang',$data)) {
  441. $data['lang'] = $f3->get('LANG');
  442. }
  443. }
  444. $new_dest = $base;
  445. if (count($data) > 0) {
  446. $new_dest .= "?" . http_build_query($data);
  447. }
  448. if ($fragment) {
  449. $new_dest .= "#" . $fragment;
  450. }
  451. //if ($f3->get('LANG') != $f3->get('default_lang')) {
  452. // $dest .= "?lang=".$f3->get('LANG');
  453. //}
  454. $link=sprintf('href="%s" ',$f3->get('SITE_URL')."/".$new_dest);
  455. break;
  456. case 'external':
  457. $dest=implode(":",$request);
  458. $target = $f3->get('external_links_open_in_new_window')
  459. ? 'target="_blank"'
  460. : ''
  461. ;
  462. $link=sprintf('href="%s" %s',$dest, $target);
  463. break;
  464. default:
  465. $link='href="#"';
  466. break;
  467. }
  468. $new=sprintf("<div class=\"brick %s\">\n<a class=\"content_element_box\" %s>\n<div class=\"content_element_box\">
  469. <div class=\"image\">
  470. <img class=\"standard\" src=\"%s\" /><img class=\"hover\" src=\"%s\" />
  471. </div>
  472. %s
  473. %s
  474. \n</div>\n</a>\n%s\n</div>",
  475. implode(" ",[$orientation,$class,$type]),
  476. $link,
  477. $pic->get_src(1600),
  478. $pic_hover->get_src(1600),
  479. ($has_caption ? "<div class=\"caption standard\">".implode("<br>",$caption_html['normal'])."</div>": " "),
  480. ($has_caption ? "<div class=\"caption hover\">".implode("<br>",$caption_html['hover'])."</div>" : " " ),
  481. $add
  482. );
  483. break;
  484. case 'brick':
  485. array_shift($request);
  486. $class = array_shift($request);
  487. $new = sprintf("<div class=\"content_element brick %s\">\n%s\n</div>",
  488. $class,
  489. $md->text(implode("\n",$body))
  490. );
  491. break;
  492. case 'calendar':
  493. array_shift($request);
  494. $conf = array('path', $this->folder);
  495. $v = $this->read_config();
  496. $cal = new \Modules\CCalendar($v,$conf);
  497. $new=sprintf("<div class=\"calendar\">\n%s\n</div>",
  498. $cal);
  499. break;
  500. case 'header':
  501. array_shift($request);
  502. $conf = array('path', $this->folder);
  503. $v = $this->read_config();
  504. switch (array_shift($request)) {
  505. case 'event':
  506. $el = new CEvent($v,$conf);
  507. $el->set_layout('archive');
  508. $new = $el;
  509. break;
  510. case 'concert':
  511. $el = new CConcert($v,$conf);
  512. $el->set_layout('header');
  513. $new = $el;
  514. break;
  515. }
  516. break;
  517. case 'image':
  518. $key=$request[1];
  519. $image="";
  520. // if(!$key) {
  521. // $new=self::warn("Content Module \"Image\" needs name of a file (without extension)");
  522. // break;
  523. //}
  524. foreach($this->structs as $domain=>$destination) {
  525. if(array_key_exists($key, $this->structs[$domain]['pic'])) {
  526. $image = $this->structs[$domain]['pic'][$key];
  527. unset($this->structs[$domain]['pic'][$key]);
  528. unset($this->content[$this->domains[$domain]][$key]);
  529. break;
  530. }
  531. }
  532. if ($image) {
  533. if( in_array($request[2],array('left','right','full'))) {
  534. $class = $request[2];
  535. } else {
  536. $class = 'left';
  537. }
  538. $img = new \Image($image);
  539. $ratio = ($img->width() >= $img->height())
  540. ? "landscape"
  541. : "portrait"
  542. ;
  543. $cached = new CachedImage($image);
  544. foreach ($body as $k=>$line) {
  545. if (strpos($line,"©") !== FALSE
  546. || strpos($line,"&copy;") !== FALSE) {
  547. $body[$k] = sprintf("<span class=\"copyright\">%s</span>",$line);
  548. }
  549. }
  550. $new=sprintf("<div class='content_element_image %s'>"
  551. ."<div class=\"media %s\"><a href=\"%s\" data-featherlight=\"image\"><img src=\"%s\" alt=\"user supplied image\" /></a></div>"
  552. ."<img src=\"%s\" style=\"display:none;\" alt=\"user supplied image\" />"
  553. ."<div class=\"caption\">%s</div>"
  554. ."</div>",
  555. $class,
  556. $ratio,
  557. $cached->get_src(1600),
  558. $cached->get_src(1600),
  559. $cached->get_src(1600),
  560. $md->text(implode("\n",$body))
  561. );
  562. } else {
  563. $new=sprintf("<div class='content_element_image %s'>\n"
  564. ."<div class=\"media %s\">\n"
  565. ."<div class=\"caption\">\n%s\n</div>\n"
  566. ."</div>\n",
  567. $class,
  568. $ratio,
  569. $md->text(implode("\n",$body))
  570. );
  571. }
  572. break;
  573. case 'devide':
  574. $contents = explode($request[1],implode("\n",$body));
  575. $c=count($contents);
  576. $str="";
  577. for ($iiii=0;$iiii<$c;$iiii++) {
  578. $str .= sprintf(" %f%%",100/$c);
  579. }
  580. $template = sprintf('grid-template-columns:%s;',
  581. $str);
  582. $counter=0;
  583. $new = sprintf('<div class="content_element_devided" style="%s">',$template);
  584. foreach($contents as $part) {
  585. $counter++;
  586. if (count($request) >= 4) {
  587. $part=str_replace([$request[2],$request[3]],["{|","|}"],$part);
  588. $part=$this->content_element_dispatcher($part);
  589. }
  590. $new .= sprintf("<div>\n%s\n</div>",
  591. $md->text($part)
  592. );
  593. }
  594. $new.="</div>";
  595. break;
  596. case 'page':
  597. array_shift($request);
  598. $target = array_shift($request);
  599. $class = array_shift($request);
  600. $folder = $this->folder.$target."/";
  601. $anchor_name=array_pop(explode("/",$target));
  602. $fff = new \Modules\FilesInFolders(
  603. $folder,
  604. array(
  605. 'content'=>array(
  606. 'secondary'=>'secondary',
  607. 'zzz'=>'hidden',
  608. 'unpublish'=>'hidden'
  609. ))
  610. );
  611. $fff->prepare_files();
  612. $fff->fill_content();
  613. $new = sprintf("<div class=\"content_element_page %s\"><a name=\"%s\"></a>\n%s\n</div>",
  614. $class,
  615. $anchor_name,
  616. implode("\n",$fff->content['default']));
  617. break;
  618. case 'youtube':
  619. $vid=array_shift($request);
  620. $pos= array_shift($request);
  621. if( in_array($pos,array('left','right','full'))) {
  622. $class = $pos;
  623. } else {
  624. $class = 'left';
  625. }
  626. $video=sprintf("<iframe width=\"700\" height=\"394\" class=\"ytvideo\" "
  627. ."src=\"https://www.youtube.com/embed/%s\"></iframe>",
  628. $vid);
  629. $thumbnail = sprintf("<div class=\"video-thumbnail\"><a href=\"%s\" data-featherlight=\"#%s\">"
  630. ."<img class=\"thumbnail\" src=\"https://img.youtube.com/vi/%s/mqdefault.jpg\" alt=\"video-preview\"/>"
  631. ."<img class=\"play-button\" src=\"%s\" alt=\"play-button\" />"
  632. ."</a></div><div class=\"lightbox\" id=\"%s\" >%s</div>",
  633. "https://www.youtube.com/watch?v=".$vid,
  634. $vid,
  635. $vid,
  636. "/rsc/img/play-button.png",
  637. $vid,
  638. $video
  639. );
  640. foreach ($body as $k=>$line) {
  641. if (strpos($line,"©") !== FALSE
  642. || strpos($line,"&copy;") !== FALSE) {
  643. $body[$k] = sprintf("<span class=\"copyright\">%s</span>",$line);
  644. }
  645. }
  646. $new=sprintf("<div class='video-container %s'>"
  647. ."<div class=\"media\">%s</div>"
  648. ."<div class=\"caption\">%s</div>"
  649. ."</div>",
  650. $class,
  651. $thumbnail,
  652. $md->text(implode("\n",$body)));
  653. break;
  654. default:
  655. $new=self::warn("Content Module \"".$request[0]."\" unknown");
  656. break;
  657. }
  658. $string = str_replace($matches[0][$i],$new,$string);
  659. }
  660. return $string;
  661. }
  662. function warn($message) {
  663. return sprintf("<div class=\"warning\">%s</div>",$message);
  664. }
  665. }