diff options
author | vincent-peugnet <v.peugnet@free.fr> | 2020-03-26 17:20:31 +0100 |
---|---|---|
committer | vincent-peugnet <v.peugnet@free.fr> | 2020-03-26 17:20:31 +0100 |
commit | 01795f56b61ab4e6becd959b7a41aad1276cdfca (patch) | |
tree | 0be85f2a811cc0fb7b44985ca3bc09015f706ebd /app | |
parent | adb3d38893acb75b838fc6ccce5edb4321d08bf9 (diff) | |
parent | d17713051ca2fef29de8025fe876d417838cea7f (diff) | |
download | wcms-01795f56b61ab4e6becd959b7a41aad1276cdfca.tar.gz wcms-01795f56b61ab4e6becd959b7a41aad1276cdfca.zip |
Merge branch 'implement-cytoscape.js'
Diffstat (limited to 'app')
-rw-r--r-- | app/class/Config.php | 13 | ||||
-rw-r--r-- | app/class/Controllerhome.php | 74 | ||||
-rw-r--r-- | app/class/Model.php | 11 | ||||
-rw-r--r-- | app/class/Modelhome.php | 218 | ||||
-rw-r--r-- | app/class/Modelpage.php | 26 | ||||
-rw-r--r-- | app/class/Modelrender.php | 7 | ||||
-rw-r--r-- | app/fn/fn.php | 26 | ||||
-rw-r--r-- | app/view/templates/home.php | 57 | ||||
-rw-r--r-- | app/view/templates/homeopt.php | 2 |
9 files changed, 366 insertions, 68 deletions
diff --git a/app/class/Config.php b/app/class/Config.php index fa9082f..2cfdcd3 100644 --- a/app/class/Config.php +++ b/app/class/Config.php @@ -3,6 +3,8 @@ namespace Wcms; +use Http\Client\Common\Plugin\RetryPlugin; + abstract class Config { protected static $pagetable = 'mystore'; @@ -129,9 +131,16 @@ abstract class Config return self::$fontsize; } - public static function basepath() + /** + * @param bool $trailingslash If not empty basepath, add a trailing slash after the basepath + */ + public static function basepath(bool $trailingslash = false) : string { - return self::$basepath; + if($trailingslash && !empty(self::$basepath)) { + return self::$basepath . '/'; + } else { + return self::$basepath; + } } public static function route404() diff --git a/app/class/Controllerhome.php b/app/class/Controllerhome.php index 5a9110a..71f110e 100644 --- a/app/class/Controllerhome.php +++ b/app/class/Controllerhome.php @@ -27,23 +27,15 @@ class Controllerhome extends Controllerpage } else { - $table = $this->modelhome->getlister(); - $this->opt = $this->modelhome->optinit($table); + $pagelist = $this->modelhome->getlister(); + $this->opt = $this->modelhome->optinit($pagelist); $vars['colors'] = new Colors($this->opt->taglist()); - if (!isset($_GET['search'])) { - $searchopt = ['title' => 1, 'description' => 1, 'content' => 1, 'other' => 0, 'casesensitive' => 0]; - } else { - $searchopt['title'] = $_GET['title'] ?? 0; - $searchopt['description'] = $_GET['description'] ?? 0; - $searchopt['content'] = $_GET['content'] ?? 0; - $searchopt['other'] = $_GET['other'] ?? 0; - $searchopt['casesensitive'] = $_GET['case'] ?? 0; - } - $regex = $_GET['search'] ?? ''; + $deepsearch = $this->deepsearch(); + + $vars['pagelistopt'] = $this->modelhome->pagetable($pagelist, $this->opt, $deepsearch['regex'], $deepsearch['searchopt']); - $vars['table2'] = $this->modelhome->table2($table, $this->opt, $regex , $searchopt); $vars['columns'] = $this->modelhome->setcolumns($this->user->columns()); @@ -52,22 +44,58 @@ class Controllerhome extends Controllerpage $vars['editorlist'] = $this->usermanager->getlisterbylevel(2, '>='); $vars['user'] = $this->user; $vars['opt'] = $this->opt; - $vars['deepsearch'] = $regex; - $vars['searchopt'] = $searchopt; + $vars['deepsearch'] = $deepsearch['regex']; + $vars['searchopt'] = $deepsearch['searchopt']; + $vars['display'] = $_GET['display'] ?? 'list'; + + if($vars['display'] === 'map') { + $vars['layout'] = $_GET['layout'] ?? 'cose-bilkent'; + $vars['showorphans'] = boolval($_GET['showorphans'] ?? false); + $vars['showredirection'] = boolval($_GET['showredirection'] ?? false); + $datas = $this->modelhome->cytodata($vars['pagelistopt'], $vars['layout'], $vars['showorphans'], $vars['showredirection']); + $vars['json'] = json_encode($datas, JSON_PRETTY_PRINT); + } - $vars['footer'] = ['version' => getversion(), 'total' => count($table), 'database' => Config::pagetable()]; + $vars['footer'] = ['version' => getversion(), 'total' => count($pagelist), 'database' => Config::pagetable()]; - if (isset($_POST['query']) && $this->user->iseditor()) { - $datas = array_merge($_POST, $_SESSION['opt']); - $this->optlist = $this->modelhome->Optlistinit($table); - $this->optlist->hydrate($datas); - $vars['optlist'] = $this->optlist; - } + $this->listquery($pagelist); + + $vars['optlist'] = $this->optlist ?? null; $this->showtemplate('home', $vars); } } + /** + * Look for GET deepsearch datas and transform it an array + * + * @return array containing `string $regex` and `array $searchopt` + */ + public function deepsearch() : array + { + if (!isset($_GET['search'])) { + $searchopt = ['title' => 1, 'description' => 1, 'content' => 1, 'other' => 0, 'casesensitive' => 0]; + } else { + $searchopt['title'] = $_GET['title'] ?? 0; + $searchopt['description'] = $_GET['description'] ?? 0; + $searchopt['content'] = $_GET['content'] ?? 0; + $searchopt['other'] = $_GET['other'] ?? 0; + $searchopt['casesensitive'] = $_GET['case'] ?? 0; + } + $regex = $_GET['search'] ?? ''; + return ['regex' => $regex, 'searchopt' => $searchopt]; + } + + public function listquery(array $pagelist) + { + if (isset($_POST['query']) && $this->user->iseditor()) { + $datas = array_merge($_POST, $_SESSION['opt']); + $this->optlist = $this->modelhome->Optlistinit($pagelist); + $this->optlist->hydrate($datas); + $vars['optlist'] = $this->optlist; + } + } + public function columns() { if (isset($_POST['columns']) && $this->user->iseditor()) { @@ -109,7 +137,7 @@ class Controllerhome extends Controllerpage $this->routedirect('home'); } } - + /** * Render every pages in the database */ diff --git a/app/class/Model.php b/app/class/Model.php index 5847f74..9e66af4 100644 --- a/app/class/Model.php +++ b/app/class/Model.php @@ -20,6 +20,17 @@ abstract class Model const GLOBAL_DIR = 'assets'. DIRECTORY_SEPARATOR . 'global' . DIRECTORY_SEPARATOR; const DATABASE_DIR = '.' . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR; const PAGES_DIR = self::DATABASE_DIR . 'pages' . DIRECTORY_SEPARATOR; + + const MAP_LAYOUTS = [ + 'cose' => 'cose', + 'cose-bilkent' => 'cose-bilkent', + 'circle' => 'circle', + 'breadthfirst' => 'breadthfirst', + 'concentric' => 'concentric', + 'grid' => 'grid', + 'random' => 'random', + ]; + const MEDIA_EXT = [ 'jpg' => 'image', diff --git a/app/class/Modelhome.php b/app/class/Modelhome.php index 6352282..949c4f7 100644 --- a/app/class/Modelhome.php +++ b/app/class/Modelhome.php @@ -39,64 +39,87 @@ class Modelhome extends Modelpage } + /** + * @param array $pagelist of Pages objects as `id => Page` + * @param Opt $opt + * + * @param string $regex Regex to match. + * @param array $options Option search, could be `content` `title` `description`. + * + * @return array associative array of `Page` objects * + */ + public function pagetable(array $pagelist, Opt $opt, $regex = '', $searchopt = []) : array + { + $pagelist = $this->filter($pagelist, $opt); + if(!empty($regex)) { + $pagelist = $this->deepsearch($pagelist, $regex , $searchopt); + } + $pagelist = $this->sort($pagelist, $opt); + + return $pagelist; + } + /** - * @param array $table + * Filter the pages list acording to the options and invert + * + * @param array $pagelist of `Page` objects * @param Opt $opt - * @param string $regex + * + * @return array of `string` pages id */ - public function table2(array $table, Opt $opt, string $regex = "", array $searchopt = []) - { + public function filter(array $pagelist, Opt $opt) : array + { - $filtertagfilter = $this->filtertagfilter($table, $opt->tagfilter(), $opt->tagcompare()); - $filterauthorfilter = $this->filterauthorfilter($table, $opt->authorfilter(), $opt->authorcompare()); - $filtersecure = $this->filtersecure($table, $opt->secure()); - $filterlinkto = $this->filterlinkto($table, $opt->linkto()); + $filtertagfilter = $this->filtertagfilter($pagelist, $opt->tagfilter(), $opt->tagcompare()); + $filterauthorfilter = $this->filterauthorfilter($pagelist, $opt->authorfilter(), $opt->authorcompare()); + $filtersecure = $this->filtersecure($pagelist, $opt->secure()); + $filterlinkto = $this->filterlinkto($pagelist, $opt->linkto()); $filter = array_intersect($filtertagfilter, $filtersecure, $filterauthorfilter, $filterlinkto); - $table2 = []; - $table2invert = []; - foreach ($table as $page) { - if (in_array($page->id(), $filter)) { - $table2[] = $page; - } else { - $table2invert[] = $page; - } - + if($opt->invert()) { + $idlist = array_keys($pagelist); + $filter = array_diff($idlist, $filter); } - if (!empty($opt->invert())) { - $table2 = $table2invert; - } + return array_intersect_key($pagelist, array_flip($filter)); + } - if(!empty($regex)) { - $table2 = $this->deepsearch($regex, $searchopt, $table2); - } - $this->pagelistsort($table2, $opt->sortby(), $opt->order()); + + /** + * Sort and limit an array of Pages + * + * @param array $pagelist of `Page` objects + * @param Opt $opt + * + * @return array associative array of `Page` objects + */ + public function sort(array $pagelist, Opt $opt) : array + { + $this->pagelistsort($pagelist, $opt->sortby(), $opt->order()); if($opt->limit() !== 0) { - $table2 = array_slice($table2, 0, $opt->limit()); + $pagelist = array_slice($pagelist, 0, $opt->limit()); } - - return $table2; + return $pagelist; } /** * Search for regex and count occurences * + * @param array $page list Array of Pages. * @param string $regex Regex to match. * @param array $options Option search, could be `content` `title` `description`. - * @param array $page list Array of Pages. * - * @return array associative array of Pages + * @return array associative array of `Page` objects */ - public function deepsearch(string $regex, array $options, array $pagelist) : array + public function deepsearch(array $pagelist, string $regex, array $options) : array { if($options['casesensitive']) { $case = ''; @@ -126,11 +149,142 @@ class Modelhome extends Modelpage $count += preg_match($regex, $page->description()); } if ($count !== 0) { - $pageselected[] = $page; + $pageselected[$page->id()] = $page; } } return $pageselected; - } + } + + + + /** + * Transform list of page into list of nodes and edges + * + * @param array $pagelist associative array of pages as `id => Page` + * @param string $layout + * @param bool $showorphans if `false`, remove orphans pages + * @param bool $showredirection if `true`, add redirections + * + * @return array + */ + public function cytodata(array $pagelist, string $layout = 'random', bool $showorphans = false, bool $showredirection = false) : array + { + $datas['elements'] = $this->mapdata($pagelist, $showorphans, $showredirection); + + $datas['layout'] = [ + 'name' => $layout, + 'quality' => 'proof', + 'fit' => true, + 'randomize' => true, + 'nodeDimensionsIncludeLabels' => true, + 'tile' => false, + 'edgeElasticity' => 0.45, + 'gravity' => 0.25, + 'idealEdgeLength' => 60, + 'numIter' => 10000 + ]; + $datas['style'] = [ + [ + 'selector' => 'node', + 'style' => [ + 'label' => 'data(id)', + 'background-image' => 'data(favicon)', + 'background-fit' => 'contain', + 'border-width' => 3, + 'border-color' => '#80b97b' + ], + ], + [ + 'selector' => 'node.not_published', + 'style' => [ + 'shape' => 'round-hexagon', + 'border-color' => '#b97b7b' + ], + ], + [ + 'selector' => 'node.private', + 'style' => [ + 'shape' => 'round-triangle', + 'border-color' => '#b9b67b' + ], + ], + [ + 'selector' => 'edge', + 'style' => [ + 'curve-style' => 'bezier', + 'target-arrow-shape' => 'triangle', + 'arrow-scale' => 1.5 + ], + ], + [ + 'selector' => 'edge.redirect', + 'style' => [ + 'line-style' => 'dashed', + 'label' => 'data(refresh)' + ], + ], + ]; + return $datas; + } + + /** + * Transform list of Pages into cytoscape nodes and edge datas + * + * @param array $pagelist associative array of pages as `id => Page` + * @param bool $showorphans if `false`, remove orphans pages + * @param bool $showredirection if `true`, add redirections + * + * @return array of cytoscape datas + */ + public function mapdata(array $pagelist, bool $showorphans = true, bool $showredirection = false) : array + { + $idlist = array_keys($pagelist); + + $edges = []; + $notorphans = []; + foreach ($pagelist as $page) { + foreach ($page->linkto() as $linkto) { + if(in_array($linkto, $idlist)) { + $edge['group'] = 'edges'; + $edge['data']['id'] = $page->id() . '>' . $linkto; + $edge['data']['source'] = $page->id(); + $edge['data']['target'] = $linkto; + $edges[] = $edge; + $notorphans[] = $linkto; + $notorphans[] = $page->id(); + } + } + // add redirection edge + if($showredirection && key_exists($page->redirection(), $pagelist)) { + $edger['group'] = 'edges'; + $edger['data']['id'] = $page->id() . '>' . $page->redirection(); + $edger['data']['refresh'] = $page->refresh(); + $edger['data']['source'] = $page->id(); + $edger['data']['target'] = $page->redirection(); + $edger['classes'] = 'redirect'; + $edges[] = $edger; + $notorphans[] = $page->redirection(); + $notorphans[] = $page->id(); + } + } + + $notorphans = array_unique($notorphans); + + $nodes = []; + foreach ($pagelist as $id => $page) { + if($showorphans || (!$showorphans && in_array($id, $notorphans))) { + $node['group'] = 'nodes'; + $node['data']['id'] = $page->id(); + $node['data']['edit'] = $page->id() . DIRECTORY_SEPARATOR . 'edit'; + $node['data']['favicon'] = Model::faviconpath() . $page->favicon(); + $node['classes'] = [$page->secure('string')]; + $nodes[] = $node; + } + } + + return array_merge($nodes, $edges); + + } /** diff --git a/app/class/Modelpage.php b/app/class/Modelpage.php index 9344089..43ba117 100644 --- a/app/class/Modelpage.php +++ b/app/class/Modelpage.php @@ -27,7 +27,7 @@ class Modelpage extends Modeldb /** * Scan library for all pages as objects * - * @return array of Pages objects + * @return array of Pages objects as `id => Page` */ public function getlister() { @@ -232,7 +232,7 @@ class Modelpage extends Modeldb public function pagelistsort(&$pagelist, $sortby, $order = 1) { - return usort($pagelist, $this->buildsorter($sortby, $order)); + return uasort($pagelist, $this->buildsorter($sortby, $order)); } @@ -241,7 +241,7 @@ class Modelpage extends Modeldb * @param array $tagchecked list of tags * @param string $tagcompare string, can be 'OR' or 'AND', set the tag filter method * - * @return array $array + * @return array $array of `string` page id */ public function filtertagfilter(array $pagelist, array $tagchecked, $tagcompare = 'OR') @@ -267,6 +267,16 @@ class Modelpage extends Modeldb return $filteredlist; } + + + /** + * @param array $pagelist List of Page + * @param array $authorchecked list of authors + * @param string $authorcompare, can be 'OR' or 'AND', set the author filter method + * + * @return array $array of `string` page id + */ + public function filterauthorfilter(array $pagelist, array $authorchecked, $authorcompare = 'OR') { @@ -290,7 +300,15 @@ class Modelpage extends Modeldb return $filteredlist; } - public function filtersecure(array $pagelist, $secure) : array + /** + * @param array $pagelist List of Page + * @param int $secure secure level + * @param string $authorcompare, can be 'OR' or 'AND', set the author filter method + * + * @return array $array of `string` page id + */ + + public function filtersecure(array $pagelist, int $secure) : array { $filteredlist = []; foreach ($pagelist as $page) { diff --git a/app/class/Modelrender.php b/app/class/Modelrender.php index 2338eb4..638f3e3 100644 --- a/app/class/Modelrender.php +++ b/app/class/Modelrender.php @@ -7,6 +7,7 @@ use Michelf\MarkdownExtra; class Modelrender extends Modelpage { + /** @var \AltoRouter */ protected $router; /** @var Page */ protected $page; @@ -21,7 +22,7 @@ class Modelrender extends Modelpage const RENDER_VERBOSE = 1; - public function __construct($router) + public function __construct(\AltoRouter $router) { parent::__construct(); @@ -640,10 +641,10 @@ class Modelrender extends Modelpage foreach ($matches as $match) { $optlist = $modelhome->Optlistinit($pagelist); $optlist->parsehydrate($match['options']); - $table2 = $modelhome->table2($pagelist, $optlist, '', []); + $pagetable = $modelhome->pagetable($pagelist, $optlist, '', []); $content = '<ul class="pagelist">' . PHP_EOL ; - foreach ($table2 as $page ) { + foreach ($pagetable as $page ) { $content .= '<li>' . PHP_EOL; $content .= '<a href="' . $this->upage($page->id()) . '">' . $page->title() . '</a>' . PHP_EOL; if($optlist->description()) { diff --git a/app/fn/fn.php b/app/fn/fn.php index 6dfeb2c..60e4722 100644 --- a/app/fn/fn.php +++ b/app/fn/fn.php @@ -2,6 +2,8 @@ use Wcms\Medialist; +use function Clue\StreamFilter\fun; + function readablesize($bytes) { $format = ' %d %s'; @@ -292,7 +294,29 @@ function recurse_copy($src,$dst) { } } closedir($dir); -} +} + +/** + * Generate a list of <options> html drop down list + * + * @param array $options as `value => title` + * @param string|int $selected value of actualy selected option + * + * @return string HTML list of options + */ +function options(array $options, $selected = null) : string +{ + $html = ''; + foreach ($options as $value => $title) { + if($value == $selected) { + $attribute = 'selected'; + } else { + $attribute = ''; + } + $html .= '<option value="' . $value . '" ' . $attribute . '>' . $title . '</option>' . PHP_EOL; + } + return $html; +} diff --git a/app/view/templates/home.php b/app/view/templates/home.php index 8078d4a..4784b08 100644 --- a/app/view/templates/home.php +++ b/app/view/templates/home.php @@ -24,13 +24,62 @@ <main class="home"> - <?php $this->insert('homeopt', ['opt' => $opt, 'user' => $user]) ?> + <?php $this->insert('homeopt', ['opt' => $opt, 'user' => $user, 'display' => $display]) ?> <section class="pages"> <div class="block"> - <h2 class="hidephone">Pages (<?= count($table2) ?>)</h2> + <h2 class="hidephone">Pages (<?= count($pagelistopt) ?>) <span class="right"><a href="?display=list" <?= $display === 'list' ? 'style="color: white"' : '' ?> >list</a> / <a href="?display=map" <?= $display === 'map' ? 'style="color: white"' : '' ?> >map</a></span> </h2> + + <?php if($display === 'map') { ?> + + <!-- ___________________ M A P _________________________ --> + + <div id="deepsearchbar"> + <form action="" method="get"> + <input type="hidden" name="display" value="map"> + <input type="checkbox" name="showorphans" value="1" id="showorphans" <?= $showorphans ? 'checked' : '' ?>> + <label for="showorphans">show orphans pages</label> + <input type="checkbox" name="showredirection" value="1" id="showredirection" <?= $showredirection ? 'checked' : '' ?>> + <label for="showredirection">show redirections</label> + <select name="layout" id="layout"> + <?= options(Wcms\Model::MAP_LAYOUTS, $layout) ?> + </select> + <label for="layout">graph layout</label> + <input type="submit" value="update"> + </form> + </div> + + <div id="graph"></div> + + <script> + var data = <?= $json ?>; + console.log(data); + </script> + + <script src="<?= Wcms\Model::jspath() ?>map.bundle.js"></script> + + <?php } else { ?> + + <!-- ___________________ L I S T _________________________ --> + + <div id="deepsearchbar" class="hidephone"> + <form action="<?= $this->url('home') ?>" method="get"> + <input type="text" name="search" value="<?= $deepsearch ?>" id="deepsearch" placeholder="deep search"> + <input type="checkbox" name="title" id="deeptitle" value="1" <?= $searchopt['title'] ? 'checked' : '' ?>> + <label for="deeptitle">title</label> + <input type="checkbox" name="description" id="deepdescription" value="1" <?= $searchopt['description'] ? 'checked' : '' ?>> + <label for="deepdescription">description</label> + <input type="checkbox" name="content" id="deepcontent" value="1" <?= $searchopt['content'] ? 'checked' : '' ?>> + <label for="deepcontent" title="Markdown content : MAIN, HEADER, NAV, ASIDE, FOOTER">content</label> + <input type="checkbox" name="other" id="deepother" value="1" <?= $searchopt['other'] ? 'checked' : '' ?>> + <label for="deepother" title="Structure content : BODY, CSS, Javascript">other</label> + <input type="checkbox" name="case" id="deepcase" value="1" <?= $searchopt['casesensitive'] ? 'checked' : '' ?>> + <label for="deepcase" title="Case sensitive or not">case sensitive</label> + <input type="submit" value="search"> + </form> + </div> <div id="deepsearchbar" class="hidephone"> @@ -105,7 +154,7 @@ </tr> </thead> <tbody> - <?php foreach ($table2 as $item) { ?> + <?php foreach ($pagelistopt as $item) { ?> <tr> <?php if($user->issupereditor()) { ?><td class="hidephone"><input type="checkbox" name="pagesid[]" value="<?= $item->id() ?>" id="id_<?= $item->id() ?>" form="multi"></td><?php } ?> <?php if($columns['favicon']) { ?> @@ -161,6 +210,8 @@ </table> </div> + <?php } ?> + </div> </section> diff --git a/app/view/templates/homeopt.php b/app/view/templates/homeopt.php index d1f70da..ed7315d 100644 --- a/app/view/templates/homeopt.php +++ b/app/view/templates/homeopt.php @@ -159,6 +159,8 @@ </div> + <input type="hidden" name="display" value="<?= $display ?>"> + <input type="submit" name="submit" value="filter"> ⬅<input type="submit" name="submit" value="reset"> |