WikiPage.class.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. <?php
  2. /**
  3. * Define a page.
  4. * @author Valentin CARRUESCO
  5. * @category Plugin
  6. * @license MIT
  7. */
  8. class WikiPage extends Entity{
  9. public $id,$label,$content,$state,$path,$category,$slug,$sort = 0;
  10. const PUBLISHED = 'published';
  11. const DRAFT = 'draft';
  12. protected $TABLE_NAME = 'wiki_page';
  13. public $fields =
  14. array(
  15. 'id' => 'key',
  16. 'label' => 'string',
  17. 'state' => 'string',
  18. 'category' => array('type'=>'int','link'=>'plugin/wiki/WikiCategory.class.php'),
  19. 'slug' => 'string',
  20. 'sort' => 'int',
  21. 'path' => 'string'
  22. );
  23. public static function defaultContent(){
  24. $default = 'Mon contenu ici...';
  25. $defaultFile = File::dir().'wiki'.SLASH.'default.md';
  26. if(file_exists($defaultFile)) $default = file_get_contents($defaultFile);
  27. return $default;
  28. }
  29. public static function workspace(){
  30. return File::dir().'wiki'.SLASH.'pages';
  31. }
  32. public static function uploads(){
  33. return File::dir().'wiki'.SLASH.'uploads';
  34. }
  35. public static function path_from_label($label){
  36. return preg_replace('|[\?\\\/\*\:\|\<\>]|i', '-',$label);
  37. }
  38. public function author(){
  39. return User::byLogin($this->creator, false)->fullName();
  40. }
  41. public function created(){
  42. return relative_time($this->created);
  43. }
  44. public function updater(){
  45. return User::byLogin($this->updater, false)->fullName();
  46. }
  47. public function updated(){
  48. return relative_time($this->updated);
  49. }
  50. public function html(){
  51. $markdown = new WikiPageParsedown();
  52. $markdown->setBreaksEnabled(true);
  53. $rawText = $this->content;
  54. Plugin::callHook('wiki_page_pre_html',array(&$rawText));
  55. $html = $markdown->text($rawText);
  56. Plugin::callHook('wiki_page_post_html',array(&$html));
  57. return $html;
  58. }
  59. public function content(){
  60. $this->content = file_get_contents(self::workspace().SLASH.wiki_os_encode($this->path));
  61. }
  62. }
  63. require_once(__DIR__.SLASH.'lib'.SLASH.'Parsedown.php');
  64. //Etend la classe parsedown pour y ajouter des features (ex:le souligné (__texte souligné__))
  65. class WikiPageParsedown extends Parsedown{
  66. protected function paragraph($Line)
  67. {
  68. if (substr($Line['text'], -1) === '#')
  69. {
  70. $closed = true;
  71. $Line['text'] = substr($Line['text'], 0, -1);
  72. }
  73. $Block = parent::paragraph($Line);
  74. if (isset($closed))
  75. {
  76. $Block['closed'] = true;
  77. }
  78. return $Block;
  79. }
  80. protected function paragraphContinue($Line, array $Block)
  81. {
  82. if (isset($Block['closed']))
  83. {
  84. return;
  85. }
  86. if (isset($Block['interrupted']))
  87. {
  88. $Block['element']['handler']['argument'] .= ' '.str_repeat("\n ", $Block['interrupted']);
  89. unset($Block['interrupted']);
  90. }
  91. if (substr($Line['text'], -1) === '#')
  92. {
  93. $Block['closed'] = true;
  94. $Line['text'] = substr($Line['text'], 0, -1);
  95. }
  96. $Block['element']['handler']['argument'] .= "\n".$Line['text'];
  97. return $Block;
  98. }
  99. protected function blockCode($Line, $Block = null)
  100. {
  101. if (isset($Block) and $Block['type'] === 'Paragraph' and ! isset($Block['interrupted']))
  102. {
  103. return;
  104. }
  105. if ($Line['indent'] >= 4)
  106. {
  107. $text = substr($Line['body'], 4);
  108. $Block = array(
  109. 'element' => array(
  110. 'name' => 'pre',
  111. 'element' => array(
  112. 'name' => 'code',
  113. 'attributes' => array('class' => "block-code"),
  114. 'text' => $text,
  115. ),
  116. ),
  117. );
  118. return $Block;
  119. }
  120. }
  121. protected function blockQuote($Line)
  122. {
  123. if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
  124. {
  125. $Block = array(
  126. 'element' => array(
  127. 'name' => 'blockquote',
  128. 'attributes' => array('class' => "blockquote"),
  129. 'handler' => array(
  130. 'function' => 'linesElements',
  131. 'argument' => (array) $matches[1],
  132. 'destination' => 'elements',
  133. )
  134. ),
  135. );
  136. return $Block;
  137. }
  138. }
  139. protected function blockTable($Line, array $Block = null)
  140. {
  141. if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
  142. {
  143. return;
  144. }
  145. if (
  146. strpos($Block['element']['handler']['argument'], '|') === false
  147. and strpos($Line['text'], '|') === false
  148. and strpos($Line['text'], ':') === false
  149. or strpos($Block['element']['handler']['argument'], "\n") !== false
  150. ) {
  151. return;
  152. }
  153. if (chop($Line['text'], ' -:|') !== '')
  154. {
  155. return;
  156. }
  157. $alignments = array();
  158. $divider = $Line['text'];
  159. $divider = trim($divider);
  160. $divider = trim($divider, '|');
  161. $dividerCells = explode('|', $divider);
  162. foreach ($dividerCells as $dividerCell)
  163. {
  164. $dividerCell = trim($dividerCell);
  165. if ($dividerCell === '')
  166. {
  167. return;
  168. }
  169. $alignment = null;
  170. if ($dividerCell[0] === ':')
  171. {
  172. $alignment = 'left';
  173. }
  174. if (substr($dividerCell, - 1) === ':')
  175. {
  176. $alignment = $alignment === 'left' ? 'center' : 'right';
  177. }
  178. $alignments []= $alignment;
  179. }
  180. # ~
  181. $HeaderElements = array();
  182. $header = $Block['element']['handler']['argument'];
  183. $header = trim($header);
  184. $header = trim($header, '|');
  185. $headerCells = explode('|', $header);
  186. if (count($headerCells) !== count($alignments))
  187. {
  188. return;
  189. }
  190. foreach ($headerCells as $index => $headerCell)
  191. {
  192. $headerCell = trim($headerCell);
  193. $HeaderElement = array(
  194. 'name' => 'th',
  195. 'handler' => array(
  196. 'function' => 'lineElements',
  197. 'argument' => $headerCell,
  198. 'destination' => 'elements',
  199. )
  200. );
  201. if (isset($alignments[$index]))
  202. {
  203. $alignment = $alignments[$index];
  204. $HeaderElement['attributes'] = array(
  205. 'style' => "text-align: $alignment;"
  206. );
  207. }
  208. $HeaderElements []= $HeaderElement;
  209. }
  210. # ~
  211. $Block = array(
  212. 'alignments' => $alignments,
  213. 'identified' => true,
  214. 'element' => array(
  215. 'name' => 'table',
  216. 'attributes' => array('class' => "table"), /* + core */
  217. 'elements' => array(),
  218. ),
  219. );
  220. $Block['element']['elements'] []= array(
  221. 'name' => 'thead',
  222. );
  223. $Block['element']['elements'] []= array(
  224. 'name' => 'tbody',
  225. 'elements' => array(),
  226. );
  227. $Block['element']['elements'][0]['elements'] []= array(
  228. 'name' => 'tr',
  229. 'elements' => $HeaderElements,
  230. );
  231. return $Block;
  232. }
  233. protected function blockFencedCode($Line)
  234. {
  235. $marker = $Line['text'][0];
  236. $openerLength = strspn($Line['text'], $marker);
  237. if ($openerLength < 3)
  238. {
  239. return;
  240. }
  241. $infostring = trim(substr($Line['text'], $openerLength), "\t ");
  242. if (strpos($infostring, '`') !== false)
  243. {
  244. return;
  245. }
  246. $Element = array(
  247. 'name' => 'code',
  248. 'attributes' => array('class' => "block-code"),
  249. 'text' => '',
  250. );
  251. if ($infostring !== '')
  252. {
  253. $Element['attributes'] = array('class' => "language-$infostring");
  254. }
  255. $Block = array(
  256. 'char' => $marker,
  257. 'openerLength' => $openerLength,
  258. 'element' => array(
  259. 'name' => 'pre',
  260. 'element' => $Element,
  261. ),
  262. );
  263. return $Block;
  264. }
  265. protected function inlineCode($Excerpt)
  266. {
  267. $marker = $Excerpt['text'][0];
  268. if (preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(?<!['.$marker.'])\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
  269. {
  270. $text = $matches[2];
  271. $text = preg_replace('/[ ]*+\n/', ' ', $text);
  272. return array(
  273. 'extent' => strlen($matches[0]),
  274. 'element' => array(
  275. 'name' => 'code',
  276. 'attributes' => array('class' => "inline-code"),
  277. 'text' => $text,
  278. ),
  279. );
  280. }
  281. }
  282. protected function inlineEmphasis($Excerpt)
  283. {
  284. if ( ! isset($Excerpt['text'][1]))
  285. {
  286. return;
  287. }
  288. $marker = $Excerpt['text'][0];
  289. if(preg_match('/^__([^__]*)__/us', $Excerpt['text'], $matches)){
  290. $emphasis = 'u';
  291. }else if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
  292. {
  293. $emphasis = 'strong';
  294. }
  295. elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
  296. {
  297. $emphasis = 'em';
  298. }
  299. else
  300. {
  301. return;
  302. }
  303. return array(
  304. 'extent' => strlen($matches[0]),
  305. 'element' => array(
  306. 'name' => $emphasis,
  307. 'attributes' => array(
  308. 'class' => 'emphasis-underscore' ,
  309. ),
  310. 'handler' => array(
  311. 'function' => 'lineElements',
  312. 'argument' => $matches[1],
  313. 'destination' => 'elements',
  314. )
  315. ),
  316. );
  317. }
  318. }
  319. ?>