diff --git a/MANUAL.md b/MANUAL.md index 7ca064e4..acbfa0a7 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -150,7 +150,7 @@ This panel is also usefull to set up a [page list](#page-list) to include the sa The Deep Search bar help you to look for words or regular expressions in your pages. -By default, searching only look in [title](#page-title), [description](#description) and [contents (markdown elements)](#markdown-elements), but the "other" checkbox will allow you to look up in [css](#css), [javascript](#javascript) and [BODY](#body) contents. +By default, searching only look in [title](#page-title), [description](#description) and [contents (markdown elements)](#content-elements), but the "other" checkbox will allow you to look up in [css](#css), [javascript](#javascript) and [BODY](#body) contents. Unlike the [filterings options](#options) below, searches can't be saved or used for [page lists](#page-list). @@ -513,7 +513,7 @@ Where `` and `` are integers beetwen `1` to `6`. You can set `` and `` values to filters beetwen `` and `` headlines to take care of. -You can specify an [element](#markdown-elements) to target with the `element` parameter. The summary will only refer to this `element` headlines. +You can specify an [element](#content-elements) to target with the `element` parameter. The summary will only refer to this `element` headlines. @@ -534,7 +534,7 @@ It use the same logic as the page filters in the [home view](#home). *For example, this will print all public pages sorted by creation date in descending order.* -When you are in the [home view](#home), ajust the filters using the [options panel](#options) to achieve the page selection you desire. Then select "filters" in the [menu](#home-menu), choose options you prefer and hit "generate". You can now copy and paste the code obtainded that way in one of the [elements](#markdown-elements) of a page. +When you are in the [home view](#home), ajust the filters using the [options panel](#options) to achieve the page selection you desire. Then select "filters" in the [menu](#home-menu), choose options you prefer and hit "generate". You can now copy and paste the code obtainded that way in one of the [elements](#content-elements) of a page. Display options are : @@ -594,7 +594,7 @@ This will include a `div` HTML tag and some javascript.
-When you are in the [home view](#home), ajust the filters using the [options panel](#options) to achieve the page selection you desire. Then select "filters" in the [menu](#home-menu), under the "map" title, you can now copy and paste the code obtainded that way in one of the [elements](#markdown-elements) of a page. +When you are in the [home view](#home), ajust the filters using the [options panel](#options) to achieve the page selection you desire. Then select "filters" in the [menu](#home-menu), under the "map" title, you can now copy and paste the code obtainded that way in one of the [elements](#content-elements) of a page. **⚠️ this feature is limited to one map per page** @@ -675,15 +675,19 @@ The most common usage is tu use it as a link `href`. For example, with a publish The BODY tab allow you to create more complex canvas for your pages. -It use only HTML. +It cannot interpret Markdown language, you have to use HTML. But still you can use all the W [inclusions codes](#inclusions). + +Depending on whitch page version you use, BODY synthax may be slightly different. #### Element inclusion -The main purpose of BODY, is to display [Markdown elements](#markdown-elements) of your page. +The main purpose of BODY, is to display [contents](#content-elements) of your page. %% -Where `` can be `MAIN`, `HEADER`, `ASIDE`, `NAV` or `FOOTER`. This will invoke the selected element into your page's BODY. +**In pages V1** `` can be `MAIN`, `HEADER`, `ASIDE`, `NAV` or `FOOTER`. This will invoke the selected element into your page's BODY. + +**In page V2** `` can only be `CONTENT` #### External Element inclusion @@ -691,24 +695,34 @@ Where `` can be `MAIN`, `HEADER`, `ASIDE`, `NAV` or `FOOTER`. This will %?id=% -Just specify the [ID](#page-id) of the page [elements](#markdown-elements) you want to include. +Just specify the [ID](#page-id) of the page [elements](#content-elements) you want to include. ##### concatenate elements -You can even concatenate differents pages [elements](#markdown-elements), using `+` symbol separating [pages IDs](#page-id). All contents of differents pages elements will be concatenated inside one element. + +|page versions |1 |2 | +|---------------|---|---| +|support |✔️ |❌ | + +**In pages V1**, ou can concatenate differents pages [elements](#content-elements), using `+` symbol separating [pages IDs](#page-id). All contents of differents pages elements will be concatenated inside one element. %?id=++*% Where `*` is the page ID of the rendered page. - +**In page V2** this is useless as you can simply repeat the simple inclusion syntax multiple times. #### Rendering options ##### HTML tags + +|page versions |1 |2 | +|---------------|---|---| +|support |✔️ |❌ | + %?tag=(0|1)% This will determine if HTML tags will be printed around included element. This may be usefull for advanced users to achieve more precise HTML editing. @@ -723,6 +737,11 @@ This will include the content of the *nav* element of your page, but without any ##### Markdown disable + +|page versions |1 |2 | +|---------------|---|---| +|support |✔️ |✔️ | + %?mardown=(0|1)% Activate or desactivate [markdonw](#markdown) parser in called ``. By default, Markdown is set to `1`. @@ -730,6 +749,11 @@ Activate or desactivate [markdonw](#markdown) parser in called ``. By d ##### Header ID + +|page versions |1 |2 | +|---------------|---|---| +|support |✔️ |✔️ | + %?headerid=-% By default, HTML `#id` are generated for every `

` to `

` headings. You can specify a range of headers outside which no ID will be added. @@ -739,6 +763,11 @@ You can also set `headerid=0` to totaly disable ID generation for this element. ##### Anchor links in title + +|page versions |1 |2 | +|---------------|---|---| +|support |✔️ |✔️ | + %?headeranchor=(0|1|2)% This render option is used to add automatic target link to title anchors. @@ -750,12 +779,22 @@ This settings is deactivated by default. ##### URL linker + +|page versions |1 |2 | +|---------------|---|---| +|support |✔️ |✔️ | + %?urllinker=(0|1)% URL Linker is a tool that will transform a plain text URL into a link. This can be enabled or disabled specificly for each elements. The default behavior can be set globaly in the [admin panel](#admin). ##### Everylink + +|page versions |1 |2 | +|---------------|---|---| +|support |✔️ |✔️ | + Everylink is an powerfull but surprising feature that will replace everything you type with links. %?everylink=% @@ -983,9 +1022,11 @@ This is not an editable metadata. It's a list of all pages that are linked from #### Content -##### markdown elements +##### content elements + +**In pages V1**, there are 5 content elements : Main, Nav, Aside, Header, Footer. -Main, Nav, Aside, Header, Footer +**In pages V2**, there is only one content element called "content". ##### CSS diff --git a/app/class/Application.php b/app/class/Application.php index d50847eb..d698c2ff 100644 --- a/app/class/Application.php +++ b/app/class/Application.php @@ -122,6 +122,18 @@ protected function configform() indicate the subfolder(s) in witch you installed the CMS

+
+

+ Page version +

+

+ Select the page version you want to use. If you don't know what it means, keep version 2. +

+ +

diff --git a/app/class/Config.php b/app/class/Config.php index a7fc71cd..21963e9b 100644 --- a/app/class/Config.php +++ b/app/class/Config.php @@ -19,7 +19,8 @@ abstract class Config protected static $privatepass = false; protected static $notpublishedpass = false; protected static $alertcss = false; - protected static $defaultbody = "%HEADER%\n\n%NAV%\n\n%ASIDE%\n\n%MAIN%\n\n%FOOTER%"; + protected static $defaultv1body = "%HEADER%\n\n%NAV%\n\n%ASIDE%\n\n%MAIN%\n\n%FOOTER%"; + protected static $defaultv2body = "%CONTENT%"; protected static $defaultfavicon = ''; protected static $defaultthumbnail = ''; protected static string $suffix = ""; @@ -50,6 +51,9 @@ abstract class Config /** @var string $lang Default string for pages */ protected static $lang = "en"; + /** Page version during creation */ + protected static int $pageversion = Page::V1; + public const LANG_MIN = 2; public const LANG_MAX = 16; @@ -75,12 +79,16 @@ public static function hydrate(array $datas) } } - public static function readconfig() + public static function readconfig(): bool { if (file_exists(Model::CONFIG_FILE)) { $current = file_get_contents(Model::CONFIG_FILE); $datas = json_decode($current, true); self::hydrate($datas); + // Setup old config file to user page version 1 + if (isset($datas['pageaversion'])) { + self::$pageversion = Page::V1; + } return true; } else { return false; @@ -227,9 +235,23 @@ public static function alertcss() return self::$alertcss; } - public static function defaultbody() + /** + * @return string Default BODY corrsponding to current Config's page version + */ + public static function defaultbody(): string + { + $fn = 'defaultv' . self::$pageversion . 'body'; + return self::$$fn; + } + + public static function defaultv1body(): string + { + return self::$defaultv1body; + } + + public static function defaultv2body(): string { - return self::$defaultbody; + return self::$defaultv2body; } public static function defaultfavicon() @@ -322,6 +344,11 @@ public static function disablejavascript(): bool return self::$disablejavascript; } + public static function pageversion(): int + { + return self::$pageversion; + } + // __________________________________________ S E T ______________________________________ @@ -414,11 +441,31 @@ public static function setalertcss($alertcss) self::$alertcss = boolval($alertcss); } + /** + * Used to convert old Config version. Save + * `defaultbody` param as `defaultv1body`. + */ public static function setdefaultbody($defaultbody) { if (is_string($defaultbody)) { $defaultbody = crlf2lf($defaultbody); - self::$defaultbody = $defaultbody; + self::$defaultv1body = $defaultbody; + } + } + + public static function setdefaultv1body($defaultbody) + { + if (is_string($defaultbody)) { + $defaultbody = crlf2lf($defaultbody); + self::$defaultv1body = $defaultbody; + } + } + + public static function setdefaultv2body($defaultbody) + { + if (is_string($defaultbody)) { + $defaultbody = crlf2lf($defaultbody); + self::$defaultv2body = $defaultbody; } } @@ -540,4 +587,11 @@ public static function setdisablejavascript($disablejavascript) { self::$disablejavascript = boolval($disablejavascript); } + + public static function setpageversion($pageversion): void + { + if (key_exists($pageversion, Page::VERSIONS)) { + self::$pageversion = $pageversion; + } + } } diff --git a/app/class/Controllerapipage.php b/app/class/Controllerapipage.php index 463ea913..39359988 100644 --- a/app/class/Controllerapipage.php +++ b/app/class/Controllerapipage.php @@ -74,8 +74,7 @@ public function update(string $page) } } $oldpage = clone $this->page; - $update = new Page($datas); - if (!is_null($update->id()) && $update->id() !== $this->page->id()) { + if (isset($datas['id']) && $datas['id'] !== $this->page->id()) { $this->shortresponse(400, "Page ID and datas ID doesn't match"); } $this->page->hydrate($datas); @@ -111,7 +110,8 @@ public function add(string $page) http_response_code(405); exit; } - $this->page = new Page(["id" => $page]); + + $this->page = $this->pagemanager->newpage(["id" => $page]); $this->page->reset(); $this->page->addauthor($this->user->id()); if ($this->pagemanager->add($this->page)) { @@ -133,7 +133,7 @@ public function put(string $page) if ($exist && !$this->canedit($this->page)) { $this->shortresponse(401, 'Page already exist but user cannot update it'); } - $this->page = new Page(array_merge($this->recievejson(), ['id' => $page])); + $this->page = $this->pagemanager->newpage(array_merge($this->recievejson(), ['id' => $page])); if ($this->pagemanager->update($this->page)) { http_response_code($exist ? 200 : 201); } else { diff --git a/app/class/Controllerinfo.php b/app/class/Controllerinfo.php index 15bbbf7e..b947507b 100644 --- a/app/class/Controllerinfo.php +++ b/app/class/Controllerinfo.php @@ -29,7 +29,7 @@ public function desktop() } catch (RuntimeException $e) { try { $mansrc = Fs::readfile(Model::MAN_FILE); - $render = new Servicerender($this->router, $this->pagemanager, true); + $render = new Servicerenderv2($this->router, $this->pagemanager, true); $manual = $render->rendermanual($mansrc); $sum = new Summary(['min' => 2, 'max' => 4, 'sum' => $render->sum()]); diff --git a/app/class/Controllerpage.php b/app/class/Controllerpage.php index 9ebe664b..29e50396 100644 --- a/app/class/Controllerpage.php +++ b/app/class/Controllerpage.php @@ -32,7 +32,7 @@ public function setpage(string $id, string $route) http_response_code(308); $this->routedirect($route, ['page' => $cleanid]); } else { - $this->page = new Page(['id' => $cleanid]); + $this->page = $this->pagemanager->newpage(['id' => $cleanid]); } } @@ -43,17 +43,18 @@ public function setpage(string $id, string $route) */ public function importpage(): bool { - if (isset($_SESSION['pageupdate']['id']) && $_SESSION['pageupdate']['id'] == $this->page->id()) { - $this->page = new Page($_SESSION['pageupdate']); - unset($_SESSION['pageupdate']); - return true; - } else { - try { + try { + if (isset($_SESSION['pageupdate']['id']) && $_SESSION['pageupdate']['id'] == $this->page->id()) { + $this->page = $this->pagemanager->parsepage($_SESSION['pageupdate']); + unset($_SESSION['pageupdate']); + return true; + } else { $this->page = $this->pagemanager->get($this->page); return true; - } catch (RuntimeException $e) { - return false; } + } catch (RuntimeException $e) { + Logger::errorex($e); + return false; } } @@ -239,16 +240,7 @@ public function edit($page) exit; } - $datas['tablist'] = [ - 'main' => $this->page->main(), - 'css' => $this->page->css(), - 'header' => $this->page->header(), - 'nav' => $this->page->nav(), - 'aside' => $this->page->aside(), - 'footer' => $this->page->footer(), - 'body' => $this->page->body(), - 'javascript' => $this->page->javascript() - ]; + $datas['tablist'] = $this->page->tabs(); $datas['faviconlist'] = $this->mediamanager->listfavicon(); $datas['thumbnaillist'] = $this->mediamanager->listthumbnail(); diff --git a/app/class/Element.php b/app/class/Element.php index 53a814bd..fc84fba6 100644 --- a/app/class/Element.php +++ b/app/class/Element.php @@ -2,17 +2,13 @@ namespace Wcms; -use DomainException; - /** * HTML Element used in pages */ -class Element extends Item +abstract class Element extends Item { - protected $fullmatch; - protected string $type; - protected $options; - protected $sources = []; + protected string $fullmatch; + protected string $options; protected int $everylink = 0; protected bool $markdown = true; protected string $content = ''; @@ -23,8 +19,6 @@ class Element extends Item protected bool $urllinker; protected int $headeranchor = self::NOHEADERANCHOR; - /** @var bool Include element with HTML tags */ - protected bool $tag; public const NOHEADERANCHOR = 0; public const HEADERANCHORLINK = 1; @@ -37,32 +31,7 @@ class Element extends Item - public function __construct($pageid, $datas = []) - { - $this->urllinker = Config::urllinker(); - $this->tag = Config::htmltag(); - $this->hydrate($datas); - $this->analyse($pageid); - } - - private function analyse(string $pageid) - { - if (!empty($this->options)) { - $this->options = str_replace('*', $pageid, $this->options); - parse_str($this->options, $datas); - if (isset($datas['id'])) { - $this->sources = explode(' ', $datas['id']); - } else { - $this->sources = [$pageid]; - } - $this->hydrate($datas); - } else { - $this->sources = [$pageid]; - } - } - - - + abstract protected function analyse(string $pageid); // ______________________________________________ G E T ________________________________________________________ @@ -73,21 +42,11 @@ public function fullmatch(): string return $this->fullmatch; } - public function type(): string - { - return $this->type; - } - public function options(): string { return $this->options; } - public function sources(): array - { - return $this->sources; - } - public function everylink(): int { return $this->everylink; @@ -128,11 +87,6 @@ public function urllinker(): bool return $this->urllinker; } - public function tag(): bool - { - return $this->tag; - } - @@ -146,19 +100,6 @@ public function setfullmatch(string $fullmatch) $this->fullmatch = $fullmatch; } - /** - * @throws DomainException if given type is not an HTML element - */ - public function settype(string $type) - { - $type = strtolower($type); - if (in_array($type, Model::HTML_ELEMENTS)) { - $this->type = $type; - } else { - throw new DomainException("$type is not a valid Page HTML Element Type"); - } - } - public function setoptions(string $options) { if (!empty($options)) { @@ -208,9 +149,4 @@ public function seturllinker($urllinker) { $this->urllinker = boolval($urllinker); } - - public function settag($tag) - { - $this->tag = boolval($tag); - } } diff --git a/app/class/Elementv1.php b/app/class/Elementv1.php new file mode 100644 index 00000000..1f330ad3 --- /dev/null +++ b/app/class/Elementv1.php @@ -0,0 +1,69 @@ +tag = Config::htmltag(); + $this->urllinker = Config::urllinker(); + $this->fullmatch = $fullmatch; + $type = strtolower($type); + if (in_array($type, Pagev1::HTML_ELEMENTS)) { + $this->type = $type; + } else { + throw new DomainException("$type is not a valid Page HTML Element Type"); + } + $this->options = $options; + $this->analyse($pageid); + } + + protected function analyse(string $pageid) + { + if (!empty($this->options)) { + $this->options = str_replace('*', $pageid, $this->options); + parse_str($this->options, $datas); + if (isset($datas['id'])) { + $this->sources = explode(' ', $datas['id']); + } else { + $this->sources = [$pageid]; + } + $this->hydrate($datas); + } else { + $this->sources = [$pageid]; + } + } + + + // ______________________________________________ G E T ________________________________________________________ + + public function sources(): array + { + return $this->sources; + } + + public function type(): string + { + return $this->type; + } + + public function tag(): bool + { + return $this->tag; + } + + public function settag($tag) + { + $this->tag = boolval($tag); + } +} diff --git a/app/class/Elementv2.php b/app/class/Elementv2.php new file mode 100644 index 00000000..c1d9fc16 --- /dev/null +++ b/app/class/Elementv2.php @@ -0,0 +1,37 @@ +urllinker = Config::urllinker(); + $this->fullmatch = $fullmatch; + $this->id = $pageid; + $this->options = $options; + $this->analyse($pageid); + } + + protected function analyse(string $pageid) + { + parse_str($this->options, $datas); + $this->hydrate($datas); + } + + + + // ______________________________________________ G E T ________________________________________________________ + + public function id(): string + { + return $this->id; + } + + public function setid($id): void + { + $this->id = $id; + } +} diff --git a/app/class/Model.php b/app/class/Model.php index 1d41acc0..19160c6d 100644 --- a/app/class/Model.php +++ b/app/class/Model.php @@ -60,11 +60,10 @@ abstract class Model 'linkto', 'visitcount', 'displaycount', - 'editcount' + 'editcount', + 'version', ]; - public const HTML_ELEMENTS = ['header', 'nav', 'main', 'aside', 'footer']; - public const ID_REGEX = "%[^a-z0-9-_]%"; public const MAX_ID_LENGTH = 64; public const PASSWORD_MIN_LENGTH = 4; diff --git a/app/class/Modelpage.php b/app/class/Modelpage.php index 672cf544..49413739 100644 --- a/app/class/Modelpage.php +++ b/app/class/Modelpage.php @@ -6,7 +6,9 @@ use JamesMoss\Flywheel\Document; use DateTimeImmutable; use DateTimeInterface; +use DomainException; use InvalidArgumentException; +use RangeException; use RuntimeException; class Modelpage extends Modeldb @@ -37,7 +39,12 @@ public function pagelist(): array if (empty($this->pagelist)) { $list = $this->repo->findAll(); foreach ($list as $pagedata) { - $this->pagelist[$pagedata->id] = new Page($pagedata); + $id = $pagedata->id; + try { + $this->pagelist[$id] = $this->parsepage($pagedata); + } catch (RuntimeException $e) { + Logger::error("Could not load Page with ID \"$id\" : $e"); + } } } return $this->pagelist; @@ -59,7 +66,11 @@ public function pagelistbyid(array $idlist = []): array $pagelist = []; foreach ($pagedatalist as $id => $pagedata) { - $pagelist[$id] = new Page($pagedata); + try { + $this->pagelist[$id] = $this->parsepage($pagedata); + } catch (RuntimeException $e) { + Logger::error("Could not load Page with ID \"$id\" : $e"); + } } return $pagelist; } @@ -98,11 +109,14 @@ public function get($id): Page } if (is_string($id)) { $pagedata = $this->repo->findById($id); - if ($pagedata !== false) { - return new Page($pagedata); - } else { + if ($pagedata === false) { throw new RuntimeException("Could not find Page with the following ID: \"$id\""); } + try { + return $this->parsepage($pagedata); + } catch (RuntimeException $e) { + throw new RuntimeException("Could not load Page with ID \"$id\": $e"); + } } else { throw new InvalidArgumentException("argument of Modelpage->get() should be a ID string or Page"); } @@ -141,8 +155,6 @@ public function getfromfile() return false; } - $files = $_FILES; - $json = file_get_contents($_FILES['pagefile']['tmp_name']); $pagedata = json_decode($json, true); @@ -150,9 +162,11 @@ public function getfromfile() return false; } - $page = new Page($pagedata); - - return $page; + try { + return $this->parsepage($pagedata); + } catch (RuntimeException $e) { + return false; + } } /** @@ -381,7 +395,18 @@ public function renderpage(Page $page, AltoRouter $router): Page { $now = new DateTimeImmutable("now", timezone_open("Europe/Paris")); - $renderengine = new Servicerender($router, $this, Config::externallinkblank(), Config::internallinkblank()); + $params = [$router, $this, Config::externallinkblank(), Config::internallinkblank()]; + + switch ($page->version()) { + case Page::V1: + $renderengine = new Servicerenderv1(...$params); + break; + case Page::V2: + $renderengine = new Servicerenderv2(...$params); + break; + default: + throw new DomainException('Page version is out of range'); + } try { $html = $renderengine->render($page); @@ -455,7 +480,8 @@ protected function filter(array $pagelist, Opt $opt): array $this->flinkto($page, $opt->linkto()) && $this->fsince($page, $opt->since()) && $this->funtil($page, $opt->until()) && - $this->fgeo($page, $opt->geo()) + $this->fgeo($page, $opt->geo()) && + $this->fversion($page, $opt->version()) ) { $filter[] = $page->id(); } @@ -656,6 +682,15 @@ protected function fgeo(Page $page, bool $geo) } } + protected function fversion(Page $page, int $version) + { + if ($version === 0) { + return true; + } else { + return $page->version() === $version; + } + } + /** * Search for regex and count occurences @@ -678,11 +713,9 @@ protected function deepsearch(array $pagelist, string $regex, array $options): a foreach ($pagelist as $page) { $count = 0; if ($options['content']) { - $count += preg_match($regex, $page->main()); - $count += preg_match($regex, $page->nav()); - $count += preg_match($regex, $page->aside()); - $count += preg_match($regex, $page->header()); - $count += preg_match($regex, $page->footer()); + foreach ($page->contents() as $content) { + $count += preg_match($regex, $page->$content()); + } } if ($options['other']) { $count += preg_match($regex, $page->body()); @@ -704,4 +737,49 @@ protected function deepsearch(array $pagelist, string $regex, array $options): a } return $pageselected; } + + /** + * Create a page + * + * @param array $datas Page's datas + * @return Page V1 or V2 depending of config file setting + */ + public function newpage(array $datas = []): Page + { + $pageversion = Config::pageversion(); + switch ($pageversion) { + case Page::V1: + return new Pagev1($datas); + + case Page::V2: + return new Pagev2($datas); + } + throw new DomainException("Invalid page version allowed to be set in config"); + } + + + /** + * This function will check for page version in datas and will retrun coresponding page version object + * If no version is specified and `content` field is not used, it will return Pagev1 + * + * @param array|object $datas Page's datas + * @return Page V1 or V2 + * @throws RangeException If page version is defined but out of range + */ + public function parsepage($datas = []): Page + { + $metadatas = is_object($datas) ? get_object_vars($datas) : $datas; + if (isset($metadatas['version'])) { + switch ($metadatas['version']) { + case Page::V1: + return new Pagev1($datas); + + case Page::V2: + return new Pagev2($datas); + } + throw new RangeException('Version is specified but out of range'); + } else { + return new Pagev1($datas); + } + } } diff --git a/app/class/Opt.php b/app/class/Opt.php index 5cc8517c..58017d8f 100644 --- a/app/class/Opt.php +++ b/app/class/Opt.php @@ -37,6 +37,8 @@ class Opt extends Item protected bool $geo = false; + protected int $version = 0; + protected $pageidlist = []; /** @var array $pagevarlist List fo every properties of an Page object */ @@ -63,6 +65,7 @@ class Opt extends Item 'since', 'until', 'geo', + 'version', 'invert', 'limit' ]; @@ -71,8 +74,7 @@ class Opt extends Item public function __construct(array $data = []) { - $page = new Page(); - $this->pagevarlist = ($page->getobjectvars()); + $this->pagevarlist = Page::getclassvars(); $this->hydrate($data); } @@ -372,6 +374,11 @@ public function geo(): bool return $this->geo; } + public function version(): int + { + return $this->version; + } + public function invert(): bool { return $this->invert; @@ -548,6 +555,14 @@ public function setgeo($geo) $this->geo = boolval($geo); } + public function setversion($version): void + { + $version = intval($version); + if ($version === 0 || key_exists($version, Page::VERSIONS)) { + $this->version = $version; + } + } + public function setinvert($invert) { $this->invert = boolval($invert); diff --git a/app/class/Page.php b/app/class/Page.php index 10cfdd3a..06d01562 100644 --- a/app/class/Page.php +++ b/app/class/Page.php @@ -6,7 +6,7 @@ use DateTimeImmutable; use DateTimeZone; -class Page extends Item +abstract class Page extends Item { protected $id; protected $title; @@ -22,11 +22,6 @@ class Page extends Item protected $css; protected $javascript; protected $body; - protected $header; - protected $main; - protected $nav; - protected $aside; - protected $footer; protected $externalcss; protected $customhead; protected $secure; @@ -51,17 +46,27 @@ class Page extends Item protected $password; protected $postprocessaction; + protected int $version; + public const LATITUDE_MIN = -90; public const LATITUDE_MAX = 90; public const LONGITUDE_MIN = -180; public const LONGITUDE_MAX = 180; + public const V1 = 1; + public const V2 = 2; + + public const VERSIONS = [ + self::V1 => "version 1", + self::V2 => "version 2" + ]; + public const PUBLIC = 0; public const PRIVATE = 1; public const NOT_PUBLISHED = 2; public const SECUREMAX = 2; - public const TABS = ['main', 'css', 'header', 'body', 'nav', 'aside', 'footer', 'javascript']; + public const TABS = ['css', 'body', 'javascript']; public const VAR_DATE = ['date', 'datecreation', 'datemodif', 'daterender']; public const TEMPLATE_OPTIONS = ['externalcss', 'externaljavascript', 'favicon', 'thumbnail', 'recursivecss']; @@ -76,13 +81,13 @@ public function __construct($datas = []) } /** - * Return a list of all object vars name as strings + * Return a list of all class vars name as strings * * @return string[] */ - public function getobjectvars(): array + public static function getclassvars(): array { - return array_keys(get_object_vars($this)); + return array_keys(get_class_vars(self::class)); } public function reset() @@ -103,15 +108,9 @@ public function reset() $this->setcss(''); $this->setjavascript(''); $this->setbody(Config::defaultbody()); - $this->setheader(''); - $this->setmain(''); - $this->setnav(''); - $this->setaside(''); - $this->setfooter(''); $this->setexternalcss([]); $this->setcustomhead(''); $this->setsecure(Config::defaultprivacy()); - $this->setinterface('main'); $this->setlinkto([]); $this->settemplatebody(''); $this->settemplatecss(''); @@ -156,6 +155,26 @@ public function isgeo(): bool return (!is_null($this->latitude) && !is_null($this->longitude)); } + /** + * @return string[] Page fields containting content + */ + public function contents(): array + { + return array_diff($this::TABS, Page::TABS); + } + + /** + * @return string[] Tabs contents to be included in edit page + */ + public function tabs(): array + { + $tabs = []; + foreach ($this::TABS as $tab) { + $tabs[$tab] = $this->$tab; + } + return $tabs; + } + // _____________________________________________________ G E T ____________________________________________________ public function id($type = 'string') @@ -228,6 +247,11 @@ public function daterender($option = 'date') return $this->datetransform('daterender', $option); } + public function primary($type = ''): string + { + return ''; + } + public function css($type = 'string') { return $this->css; @@ -242,27 +266,6 @@ public function body($type = 'string') { return $this->body; } - - public function header($type = 'string') - { - return $this->header; - } - - public function main($type = 'string') - { - return $this->main; - } - - public function nav($type = "string") - { - return $this->nav; - } - - public function aside($type = "string") - { - return $this->aside; - } - public function externalcss($type = "array") { return $this->externalcss; @@ -277,11 +280,6 @@ public function customhead($type = "string") } } - public function footer($type = "string") - { - return $this->footer; - } - public function secure($type = 'int') { if ($type == 'string') { @@ -430,6 +428,11 @@ public function postprocessaction($type = 'int'): bool return $this->postprocessaction; } + public function version($type = 'int'): int + { + return $this->version; + } + @@ -608,38 +611,6 @@ public function setbody($body) } } - public function setheader($header) - { - if (strlen($header) < self::LENGTH_LONG_TEXT && is_string($header)) { - $header = crlf2lf($header); - $this->header = $header; - } - } - - public function setmain($main) - { - if (strlen($main) < self::LENGTH_LONG_TEXT and is_string($main)) { - $main = crlf2lf($main); - $this->main = $main; - } - } - - public function setnav($nav) - { - if (strlen($nav) < self::LENGTH_LONG_TEXT and is_string($nav)) { - $nav = crlf2lf($nav); - $this->nav = $nav; - } - } - - public function setaside($aside) - { - if (strlen($aside) < self::LENGTH_LONG_TEXT and is_string($aside)) { - $aside = crlf2lf($aside); - $this->aside = $aside; - } - } - public function setexternalcss($externalcss) { if (is_array($externalcss)) { @@ -655,14 +626,6 @@ public function setcustomhead(string $customhead) } } - public function setfooter($footer) - { - if (strlen($footer) < self::LENGTH_LONG_TEXT and is_string($footer)) { - $footer = crlf2lf($footer); - $this->footer = $footer; - } - } - public function setsecure($secure) { if ($secure >= 0 and $secure <= self::SECUREMAX) { @@ -672,7 +635,7 @@ public function setsecure($secure) public function setinterface($interface) { - if (in_array($interface, self::TABS)) { + if (in_array($interface, $this::TABS)) { $this->interface = $interface; } } diff --git a/app/class/Pagev1.php b/app/class/Pagev1.php new file mode 100644 index 00000000..ff3ca5b2 --- /dev/null +++ b/app/class/Pagev1.php @@ -0,0 +1,102 @@ +setheader(''); + $this->setmain(''); + $this->setnav(''); + $this->setaside(''); + $this->setfooter(''); + + $this->setinterface('main'); + } + + + public function header($type = 'string') + { + return $this->header; + } + + public function main($type = 'string') + { + return $this->main; + } + + public function primary($type = ''): string + { + return $this->main; + } + + public function nav($type = "string") + { + return $this->nav; + } + + public function aside($type = "string") + { + return $this->aside; + } + + public function footer($type = "string") + { + return $this->footer; + } + + + public function setheader($header) + { + if (strlen($header) < self::LENGTH_LONG_TEXT && is_string($header)) { + $header = crlf2lf($header); + $this->header = $header; + } + } + + public function setmain($main) + { + if (strlen($main) < self::LENGTH_LONG_TEXT and is_string($main)) { + $main = crlf2lf($main); + $this->main = $main; + } + } + + public function setnav($nav) + { + if (strlen($nav) < self::LENGTH_LONG_TEXT and is_string($nav)) { + $nav = crlf2lf($nav); + $this->nav = $nav; + } + } + + public function setaside($aside) + { + if (strlen($aside) < self::LENGTH_LONG_TEXT and is_string($aside)) { + $aside = crlf2lf($aside); + $this->aside = $aside; + } + } + + public function setfooter($footer) + { + if (strlen($footer) < self::LENGTH_LONG_TEXT and is_string($footer)) { + $footer = crlf2lf($footer); + $this->footer = $footer; + } + } +} diff --git a/app/class/Pagev2.php b/app/class/Pagev2.php new file mode 100644 index 00000000..e820441c --- /dev/null +++ b/app/class/Pagev2.php @@ -0,0 +1,38 @@ +setcontent(''); + + $this->setinterface('content'); + } + + public function content($type = ''): string + { + return $this->content; + } + + public function primary($type = ''): string + { + return $this->content; + } + + public function setcontent($content) + { + if (is_string($content)) { + $this->content = $content; + } + } +} diff --git a/app/class/Servicerender.php b/app/class/Servicerender.php index 58f1abdb..f840f7b1 100644 --- a/app/class/Servicerender.php +++ b/app/class/Servicerender.php @@ -15,7 +15,7 @@ use VStelmakh\UrlHighlight\UrlHighlight; use VStelmakh\UrlHighlight\Validator\Validator; -class Servicerender +abstract class Servicerender { /** @var AltoRouter */ protected ?AltoRouter $router; @@ -23,10 +23,7 @@ class Servicerender /** @var Modelpage */ protected Modelpage $pagemanager; - /** - * @var Page Actual page being rendered - * */ - protected $page; + protected Page $page; /** @var string[] */ protected $linkto = []; @@ -82,21 +79,15 @@ public function render(Page $page): string } /** - * Render a page MAIN content to be used in RSS feed + * Render page's primary content to be used in RSS feed * * @param Page $page Page to render * - * @return string HTML Parsed MAIN content of a page + * @return string HTML Parsed primary content of a page * * @todo render absolute media links */ - public function rendermain(Page $page): string - { - $this->page = $page; - $element = new Element($page->id(), ['content' => $page->main(), 'type' => "main"]); - $html = $this->elementparser($element); - return $this->bodyparser($html); - } + abstract public function renderprimary(Page $page): string; /** * Used to convert the markdown user manual to html document @@ -107,7 +98,7 @@ public function rendermain(Page $page): string public function rendermanual(string $text): string { $text = $this->markdown($text); - $text = $this->headerid($text, 1, 5, 'main', 0); + $text = $this->headerid($text, 1, 5, 0); return $text; } @@ -117,7 +108,7 @@ public function rendermanual(string $text): string * * @return string html string */ - private function gethmtl() + protected function gethmtl() { $body = $this->bodyconstructor($this->readbody()); @@ -134,7 +125,7 @@ private function gethmtl() } - private function readbody() + protected function readbody() { if (!empty($this->page->templatebody())) { $templateid = $this->page->templatebody(); @@ -153,41 +144,22 @@ private function readbody() /** - * Analyse BODY, call the corresponding CONTENTs and render everything + * Analyse BODY, include basic inclusions * * @param string $body as the string BODY of the page * * @return string as the full rendered BODY of the page */ - private function bodyconstructor(string $body): string + protected function bodyconstructor(string $body): string { $body = $this->winclusions($body); - - // Elements that can be detected - $types = array_map("strtoupper", Model::HTML_ELEMENTS); - - // First level regex - $regex = implode("|", $types); - - $matches = $this->match($body, $regex); - - // First, analyse the synthax and call the corresponding methods - if (!empty($matches)) { - foreach ($matches as $match) { - $element = new Element($this->page->id(), $match); - $element->setcontent($this->getelementcontent($element->sources(), $element->type())); - $element->setcontent($this->elementparser($element)); - $body = str_replace($element->fullmatch(), $element->content(), $body); - } - } - return $body; } /** * Return HEAD html element of a page */ - private function gethead(): string + protected function gethead(): string { $id = $this->page->id(); $globalpath = Model::dirtopath(Model::GLOBAL_CSS_FILE); @@ -284,7 +256,7 @@ private function gethead(): string * @param Page $page Page being rendered * @return string HTML to insert into of page */ - private function recursivecss(Page $page): string + protected function recursivecss(Page $page): string { $head = ""; try { @@ -305,38 +277,10 @@ private function recursivecss(Page $page): string } - /** - * Foreach $sources (pages), this will get the corresponding $type element content - * - * @param string[] $sources Array of pages ID - * @param string $type Type of element - */ - private function getelementcontent(array $sources, string $type) - { - if (!in_array($type, Model::HTML_ELEMENTS)) { - throw new InvalidArgumentException(); - } - $content = ''; - $subseparator = "\n\n"; - foreach ($sources as $source) { - if ($source !== $this->page->id()) { - try { - $subcontent = $this->pagemanager->get($source)->$type(); - } catch (RuntimeException $e) { - $subcontent = $this->page->$type(); - } - } else { - $subcontent = $this->page->$type(); - } - $content .= $subseparator . $subcontent; - } - return $content . $subseparator; - } - /** * Perfom W syntax inclusions */ - private function winclusions($text) + protected function winclusions($text) { $text = $this->date($text); $text = $this->thumbnail($text); @@ -354,38 +298,7 @@ private function winclusions($text) return $text; } - private function elementparser(Element $element) - { - $content = $element->content(); - $content = $this->winclusions($content); - if ($element->everylink() > 0) { - $content = $this->everylink($content, $element->everylink()); - } - if ($element->markdown()) { - $content = $this->markdown($content); - } - if ($element->headerid()) { - $content = $this->headerid( - $content, - $element->minheaderid(), - $element->maxheaderid(), - $element->type(), - $element->headeranchor() - ); - } - if ($element->urllinker()) { - $content = $this->autourl($content); - } - if ($element->tag()) { - $type = $element->type(); - $content = "\n<{$type}>\n{$content}\n\n"; - } - - return $content; - } - - - private function bodyparser(string $html) + protected function bodyparser(string $html) { $html = $this->summary($html); @@ -402,7 +315,7 @@ private function bodyparser(string $html) /** * Replace `%TITLE%` code with page's title */ - private function title($text) + protected function title($text) { return str_replace('%TITLE%', $this->page->title(), $text); } @@ -411,7 +324,7 @@ private function title($text) /** * Replace `%DESCRIPTION%` code with page's description */ - private function description($text) + protected function description($text) { return str_replace('%DESCRIPTION%', $this->page->description(), $text); } @@ -421,7 +334,7 @@ private function description($text) * * @param string $text the page text as html */ - private function richlink(string $text): string + protected function richlink(string $text): string { $text = preg_replace('#\2#', "$3", $text); return $text; @@ -432,7 +345,7 @@ private function richlink(string $text): string * * This will also include `target=_blank` and `class=external` attributes. */ - private function autourl($text): string + protected function autourl($text): string { $options = ["class" => "external"]; if ($this->externallinkblank) { @@ -456,7 +369,7 @@ private function autourl($text): string * * Keep existing class and remove duplicates or useless spaces in class attribute */ - private function htmlparser(string $html): string + protected function htmlparser(string $html): string { $dom = new DOMDocument('1.0', 'UTF-8'); /** Force UTF-8 encoding for loadHTML by defining it in the content itself with an XML tag that need to be removed later */ @@ -533,7 +446,7 @@ private function htmlparser(string $html): string /** * Edit `src` attributes in media HTML tags */ - private function sourceparser(DOMNodeList $sourcables): void + protected function sourceparser(DOMNodeList $sourcables): void { foreach ($sourcables as $sourcable) { assert($sourcable instanceof DOMElement); @@ -563,7 +476,7 @@ private function sourceparser(DOMNodeList $sourcables): void /** * Replace wiki links [[page_id]] with HTML link */ - private function wikiurl(string $text): string + protected function wikiurl(string $text): string { $rend = $this; $text = preg_replace_callback( @@ -591,14 +504,17 @@ function ($matches) use ($rend) { * @param string $text Input html document to scan * @param int $min Maximum header deepness to look for. Min = 1 Max = 6 Default = 1 * @param int $max Maximum header deepness to look for. Min = 1 Max = 6 Default = 6 - * @param string $element Name of element being analysed + * @param string $element Name of element being analysed. Leave empty if using Markdown field * @param int $anchormode Mode of anchor link display. see Element HEADERANCHORMODES * * @return string text with id in header */ - private function headerid(string $text, int $min, int $max, string $element, int $anchormode): string + protected function headerid(string $text, int $min, int $max, int $anchormode, string $element = ''): string { + if (empty($element)) { + $element = md5($text); + } if ($min > 6 || $min < 1) { $min = 6; } @@ -635,7 +551,7 @@ function ($matches) use ($element, $anchormode) { return $text; } - private function markdown($text) + protected function markdown($text) { $fortin = new MarkdownExtra(); // id in headers @@ -655,7 +571,7 @@ private function markdown($text) * * @return array Ordered array containing an array of `fullmatch`, `type` and `options` */ - private function match(string $text, string $include): array + protected function match(string $text, string $include): array { preg_match_all('~\%(' . $include . ')(\?([a-zA-Z0-9:\[\]\&=\-_\/\%\+\*\;]*))?\%~', $text, $out); @@ -673,7 +589,7 @@ private function match(string $text, string $include): array * * @return string Output text */ - private function automedialist(string $text): string + protected function automedialist(string $text): string { $matches = $this->match($text, 'MEDIA'); @@ -693,7 +609,7 @@ private function automedialist(string $text): string * * @return string Output text */ - private function summary(string $text): string + protected function summary(string $text): string { $matches = $this->match($text, 'SUMMARY'); @@ -712,7 +628,7 @@ private function summary(string $text): string /** * Render pages list */ - private function pageoptlist(string $text): string + protected function pageoptlist(string $text): string { $matches = $this->match($text, 'LIST'); foreach ($matches as $match) { @@ -742,7 +658,7 @@ private function pageoptlist(string $text): string /** * Render page maps */ - private function pageoptmap(string $text): string + protected function pageoptmap(string $text): string { $matches = $this->match($text, 'MAP'); foreach ($matches as $match) { @@ -775,7 +691,7 @@ private function pageoptmap(string $text): string /** * Render Random links */ - private function randomopt(string $text): string + protected function randomopt(string $text): string { $matches = $this->match($text, 'RANDOM'); foreach ($matches as $match) { @@ -810,7 +726,7 @@ private function randomopt(string $text): string * * @return string Text with replaced valid %RSS% inclusions */ - private function rss(string $text): string + protected function rss(string $text): string { $this->rsslist = $this->rssmatch($text); foreach ($this->rsslist as $fullmatch => $bookmark) { @@ -827,7 +743,7 @@ private function rss(string $text): string * * @return Bookmark[] Associative array of bookmarks, using fullmatch as key */ - private function rssmatch(string $text): array + protected function rssmatch(string $text): array { $rsslist = []; $matches = $this->match($text, "RSS"); @@ -850,7 +766,7 @@ private function rssmatch(string $text): array - private function date(string $text): string + protected function date(string $text): string { $dateregex = implode('|', array_keys(Clock::TYPES)); $matches = $this->match($text, $dateregex); @@ -877,7 +793,7 @@ private function date(string $text): string * * @return string The rendered output */ - private function thumbnail(string $text): string + protected function thumbnail(string $text): string { $src = Model::thumbnailpath() . $this->page->thumbnail(); $alt = $this->page->title(); @@ -893,7 +809,7 @@ private function thumbnail(string $text): string * @param string $text input text * @return string output text with replaced elements */ - private function pageid(string $text): string + protected function pageid(string $text): string { return str_replace(['%PAGEID%', '%ID%'], $this->page->id(), $text); } @@ -903,7 +819,7 @@ private function pageid(string $text): string * @param string $text input text * @return string output text with replaced elements */ - private function url(string $text): string + protected function url(string $text): string { return str_replace('%URL%', Config::domain() . $this->upage($this->page->id()), $text); } @@ -913,7 +829,7 @@ private function url(string $text): string * @param string $text input text * @return string output text with replaced elements */ - private function path(string $text): string + protected function path(string $text): string { return str_replace('%PATH%', $this->upage($this->page->id()), $text); } @@ -921,7 +837,7 @@ private function path(string $text): string /** * Replace `%AUTHORS%` with a rendered list of authors */ - private function authors(string $text): string + protected function authors(string $text): string { $page = $this->page; return preg_replace_callback("~\%AUTHORS\%~", function () use ($page) { @@ -934,7 +850,7 @@ private function authors(string $text): string /** * Check if the page need post processing by looking for patterns */ - private function checkpostprocessaction(string $text): bool + protected function checkpostprocessaction(string $text): bool { $counterpaterns = Servicepostprocess::COUNTERS; $pattern = implode('|', $counterpaterns); @@ -948,7 +864,7 @@ private function checkpostprocessaction(string $text): bool * * @return string Conversion output */ - private function everylink(string $text, int $limit): string + protected function everylink(string $text, int $limit): string { $regex = '~([\w\-_éêèùïüîçà]{' . $limit . ',})(?![^<]*>|[^<>]*<\/)~'; $text = preg_replace_callback($regex, function ($matches) { @@ -964,7 +880,7 @@ private function everylink(string $text, int $limit): string * * @return string text ouput */ - private function authenticate(string $text): string + protected function authenticate(string $text): string { $id = $this->page->id(); $regex = '~\%CONNECT(\?dir=([a-zA-Z0-9-_]+))?\%~'; @@ -1011,7 +927,7 @@ public function user(User $user): string * @param User[] $users List of User * @return string List of user in HTML */ - private function userlist(array $users): string + protected function userlist(array $users): string { $html = ""; foreach ($users as $user) { diff --git a/app/class/Servicerenderv1.php b/app/class/Servicerenderv1.php new file mode 100644 index 00000000..2c1dbc1f --- /dev/null +++ b/app/class/Servicerenderv1.php @@ -0,0 +1,136 @@ +page = $page; + $html = $this->bodyconstructor('%MAIN%'); + return $this->bodyparser($html); + } + + /** + * Analyse BODY, call the corresponding CONTENTs and render everything + * + * @param string $body as the string BODY of the page + * + * @return string as the full rendered BODY of the page + */ + protected function bodyconstructor(string $body): string + { + $body = parent::bodyconstructor($body); + + // Elements that can be detected + $types = array_map("strtoupper", Pagev1::HTML_ELEMENTS); + + // First level regex + $regex = implode("|", $types); + + $matches = $this->match($body, $regex); + + // First, analyse the synthax and call the corresponding methods + if (!empty($matches)) { + foreach ($matches as $match) { + $element = new Elementv1($this->page->id(), $match['fullmatch'], $match['type'], $match['options']); + $element->setcontent($this->getelementcontent($element->sources(), $element->type())); + $element->setcontent($this->elementparser($element)); + $body = str_replace($element->fullmatch(), $element->content(), $body); + } + } + + return $body; + } + + protected function elementparser(Elementv1 $element) + { + $content = $element->content(); + $content = $this->winclusions($content); + if ($element->everylink() > 0) { + $content = $this->everylink($content, $element->everylink()); + } + if ($element->markdown()) { + $content = $this->markdown($content); + } + if ($element->headerid()) { + $content = $this->headerid( + $content, + $element->minheaderid(), + $element->maxheaderid(), + $element->headeranchor(), + $element->type() + ); + } + if ($element->urllinker()) { + $content = $this->autourl($content); + } + if ($element->tag()) { + $type = $element->type(); + $content = "\n<{$type}>\n{$content}\n\n"; + } + + return $content; + } + + + /** + * Foreach $sources (pages), this will get the corresponding $type element content + * If ID is not used or if Page is not version 1: fallback to current page Markdown field + * + * @param string[] $sources Array of pages ID + * @param string $type Type of element + */ + protected function getelementcontent(array $sources, string $type): string + { + if (!in_array($type, Pagev1::HTML_ELEMENTS)) { + throw new DomainException("$type is not a valid HTML element type"); + } + $content = ''; + $subseparator = "\n\n"; + foreach ($sources as $source) { + if ($source !== $this->page->id()) { + try { + $page = $this->pagemanager->get($source); + if ($page instanceof Pagev1) { + $subcontent = $page->$type(); + } else { + $subcontent = $this->page->$type(); + } + } catch (RuntimeException $e) { + // Page ID is not used + $subcontent = $this->page->$type(); + } + } else { + $subcontent = $this->page->$type(); + } + $content .= $subseparator . $subcontent; + } + return $content . $subseparator; + } +} diff --git a/app/class/Servicerenderv2.php b/app/class/Servicerenderv2.php new file mode 100644 index 00000000..ed458891 --- /dev/null +++ b/app/class/Servicerenderv2.php @@ -0,0 +1,116 @@ +page = $page; + $html = $this->bodyconstructor('%CONTENT%'); + return $this->bodyparser($html); + } + + + /** + * Analyse BODY, call the corresponding CONTENTs and render everything + * + * @param string $body as the string BODY of the page + * + * @return string as the full rendered BODY of the page + */ + protected function bodyconstructor(string $body): string + { + $body = parent::bodyconstructor($body); + + $matches = $this->match($body, 'CONTENT'); + + // First, analyse the synthax and call the corresponding methods + if (!empty($matches)) { + foreach ($matches as $match) { + $element = new Elementv2($this->page->id(), $match['fullmatch'], $match['options']); + $element->setcontent($this->getelementcontent($element->id())); + $element->setcontent($this->elementparser($element)); + $body = str_replace($element->fullmatch(), $element->content(), $body); + } + } + + return $body; + } + + protected function elementparser(Elementv2 $element) + { + $content = $element->content(); + $content = $this->winclusions($content); + if ($element->everylink() > 0) { + $content = $this->everylink($content, $element->everylink()); + } + if ($element->markdown()) { + $content = $this->markdown($content); + } + if ($element->headerid()) { + $content = $this->headerid( + $content, + $element->minheaderid(), + $element->maxheaderid(), + $element->headeranchor(), + ); + } + if ($element->urllinker()) { + $content = $this->autourl($content); + } + + return $content; + } + + + /** + * Get element content by looking for source page ID. + * If ID is not used: return empty string + * If source page is V1, it will use the MAIN content. + * + * @param string $source Source Page ID + * + * @return string Source Page primary content or empty string + * + * @todo Log errors somewhere + */ + protected function getelementcontent(string $source): string + { + if ($source === $this->page->id()) { + return $this->page->content(); + } else { + try { + $page = $this->pagemanager->get($source); + return $page->primary(); + } catch (RuntimeException $e) { + // page ID is not used + return ''; + } + } + } +} diff --git a/app/class/Servicerss.php b/app/class/Servicerss.php index 8f5f5496..28385f2f 100644 --- a/app/class/Servicerss.php +++ b/app/class/Servicerss.php @@ -4,9 +4,11 @@ use AltoRouter; use DateTime; +use DomainException; use DOMDocument; use DOMException; use DOMText; +use Exception; use LogicException; use RuntimeException; use Wcms\Exception\Database\Notfoundexception; @@ -15,7 +17,6 @@ class Servicerss { protected AltoRouter $router; - protected Servicerender $render; protected Modelpage $pagemanager; protected Modelbookmark $bookmarkmanager; @@ -24,7 +25,6 @@ public function __construct(AltoRouter $router) $this->router = $router; $this->pagemanager = new Modelpage(Config::pagetable()); $this->bookmarkmanager = new Modelbookmark(); - $this->render = new Servicerender($this->router, $this->pagemanager); } /** @@ -175,7 +175,7 @@ protected function render(array $pagelist, Bookmark $bookmark): string $content = $xml->createElement("content"); $content->appendChild( - new DOMText(html_entity_decode($this->mainhtml($page), ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8")) + new DOMText(html_entity_decode($this->primaryhtml($page), ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8")) ); $content->setAttribute("type", "html"); $entry->appendChild($content); @@ -195,19 +195,33 @@ protected function render(array $pagelist, Bookmark $bookmark): string */ protected function href(Page $page): string { - return Config::domain() . $this->render->upage($page->id()); + try { + $path = $this->router->generate('pageread', ['page' => $page->id()]); + return Config::domain() . $path; + } catch (Exception $e) { + throw new LogicException($e->getMessage(), $e->getCode(), $e); + } } /** - * Get the HTML output of a page + * Get the HTML output of the primary content of a page * * @param Page $page - * @return string HTML content parsed from page MAIN + * @return string HTML content parsed from page primary field */ - protected function mainhtml(Page $page): string + protected function primaryhtml(Page $page): string { - $render = new Servicerender($this->router, $this->pagemanager); - return $render->rendermain($page); + switch ($page->version()) { + case Page::V1: + $render = new Servicerenderv1($this->router, $this->pagemanager); + break; + case Page::V2: + $render = new Servicerenderv2($this->router, $this->pagemanager); + break; + default: + throw new DomainException('Page version is out of range'); + } + return $render->renderprimary($page); } /** diff --git a/app/class/Summary.php b/app/class/Summary.php index 2c890640..74c69bac 100644 --- a/app/class/Summary.php +++ b/app/class/Summary.php @@ -142,7 +142,7 @@ public function setsum(array $sum) public function setelement(string $element) { - if (in_array($element, Model::HTML_ELEMENTS)) { + if (in_array($element, Pagev1::HTML_ELEMENTS)) { $this->element = $element; } } diff --git a/app/view/templates/admin.php b/app/view/templates/admin.php index 33d99d30..671bfb9f 100644 --- a/app/view/templates/admin.php +++ b/app/view/templates/admin.php @@ -74,6 +74,8 @@

Page creation

What really happend when you create a new page

+ +

Privacy of new pages

+

Page version

+ +

Choose W page version you want to use when a new page is created.

+ + + +

Default BODY

- - + + + diff --git a/app/view/templates/edit.php b/app/view/templates/edit.php index 3ee97b8a..2a991c41 100644 --- a/app/view/templates/edit.php +++ b/app/view/templates/edit.php @@ -29,6 +29,7 @@ + +
<a href="https://club1.fr" class="total  shell">dans un codeblock</a>
+
+ + diff --git a/tests/data/Servicerenderv2Test/external-links-test-v2.json b/tests/data/Servicerenderv2Test/external-links-test-v2.json new file mode 100644 index 00000000..fa6941a3 --- /dev/null +++ b/tests/data/Servicerenderv2Test/external-links-test-v2.json @@ -0,0 +1,49 @@ +{ + "id": "external-links-test-v2", + "title": "external-links-test-v2", + "description": "", + "lang": "", + "tag": [], + "latitude": null, + "longitude": null, + "date": "2023-08-06T19:33:00+0200", + "datecreation": "2023-08-06T19:33:34+0200", + "datemodif": "2023-08-06T19:33:37+0200", + "daterender": "2023-08-06T19:33:38+0200", + "css": "", + "javascript": "", + "body": "%CONTENT%", + "externalcss": [], + "customhead": "", + "secure": 0, + "interface": "content", + "linkto": [], + "templatebody": "", + "templatecss": "", + "templatejavascript": "", + "templateoptions": [ + "thumbnail", + "recursivecss", + "externalcss", + "favicon", + "externaljavascript" + ], + "favicon": "", + "thumbnail": "", + "authors": [ + "vincent" + ], + "invites": [], + "readers": [], + "displaycount": 1, + "visitcount": 0, + "editcount": 1, + "editby": [], + "sleep": 0, + "redirection": "", + "refresh": 0, + "password": null, + "postprocessaction": false, + "version": 2, + "content": "\r\n\r\n[link to my external life using Michel Fortin's Markdown extra](https:\/\/club1.fr) {.testclass#testid}\r\n\r\nhttps:\/\/club1.fr\r\n\r\ntest de lien vers CLUB1<\/a>\r\n\r\ntest de lien vers CLUB1<\/a>\r\n\r\n