pupupu

simple static CMS for crappy servers
git clone https://git.ce9e.org/pupupu.git

commit
a73915b5e62164510f3b825e7f91d04524a79deb
parent
8f1d98cd01db9e25f066c6bc58f36d15692704a0
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2018-10-22 18:49
split into separate files

Diffstat

A api.php 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M index.php 399 +------------------------------------------------------------
A utils.php 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A views.php 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

4 files changed, 407 insertions, 397 deletions


diff --git a/api.php b/api.php

@@ -0,0 +1,232 @@
   -1     1 <?php declare(strict_types=1);
   -1     2 
   -1     3 require __DIR__ . '/vendor/autoload.php';
   -1     4 use Symfony\Component\Yaml\Yaml;
   -1     5 use Michelf\MarkdownExtra;
   -1     6 
   -1     7 include_once('utils.php');
   -1     8 
   -1     9 function shiftHeadings($html, $offset)
   -1    10 {
   -1    11     return preg_replace_callback('|(</?h)([1-6])([ >])|', function ($match) use ($offset) {
   -1    12         $target = max(1, min(6, (intval($match[2]) + $offset)));
   -1    13         return $match[1] . $target . $match[3];
   -1    14     }, $html);
   -1    15 }
   -1    16 
   -1    17 class Pupupu
   -1    18 {
   -1    19     public function __construct($srcDir, $targetDir, $targetUrl)
   -1    20     {
   -1    21         $this->srcDir = $srcDir;
   -1    22         $this->targetDir = $targetDir;
   -1    23         $this->targetUrl = $targetUrl;
   -1    24 
   -1    25         $loader = new Twig_Loader_Filesystem($srcDir . '/_templates');
   -1    26         $this->twig = new Twig_Environment($loader);
   -1    27         $this->twig->addFilter(new Twig_Filter('md', function ($string) {
   -1    28             return MarkdownExtra::defaultTransform($string);
   -1    29         }));
   -1    30         $this->twig->addFilter(new Twig_Filter('shift_headings', 'shiftHeadings'));
   -1    31 
   -1    32         $this->cache = array();
   -1    33     }
   -1    34 
   -1    35     protected function pathIsFile($path)
   -1    36     {
   -1    37         return $path === '/_site' || $path === '/_users' || strpos($path, '.') !== false;
   -1    38     }
   -1    39 
   -1    40     protected function getSrc($path, $ext)
   -1    41     {
   -1    42         if ($this->pathIsFile($path)) {
   -1    43             return $this->srcDir . '/_content' . $path . '.' . $ext;
   -1    44         } else {
   -1    45             return $this->srcDir . '/_content' . $path . '/index' . '.' . $ext;
   -1    46         }
   -1    47     }
   -1    48 
   -1    49     protected function getTarget($path)
   -1    50     {
   -1    51         if ($this->pathIsFile($path)) {
   -1    52             return $this->targetDir . $path;
   -1    53         } else {
   -1    54             return $this->targetDir . $path . '/index.html';
   -1    55         }
   -1    56     }
   -1    57 
   -1    58     public function get($path, $ext)
   -1    59     {
   -1    60         $p = $this->getSrc($path, $ext);
   -1    61         if (file_exists($p)) {
   -1    62             return file_get_contents($p);
   -1    63         } else {
   -1    64             return '';
   -1    65         }
   -1    66     }
   -1    67 
   -1    68     public function put($path, $ext, $content)
   -1    69     {
   -1    70         $p = $this->getSrc($path, $ext);
   -1    71         mkdirp(dirname($p));
   -1    72         _file_put_contents($p, $content);
   -1    73     }
   -1    74 
   -1    75     public function rm($path)
   -1    76     {
   -1    77         rmfile($this->getSrc($path, 'yml'));
   -1    78         rmfile($this->getSrc($path, 'md'));
   -1    79         rmfile($this->getTarget($path));
   -1    80     }
   -1    81 
   -1    82     public function getYaml($path)
   -1    83     {
   -1    84         $key = "yml:$path";
   -1    85         if (!in_array($key, $this->cache)) {
   -1    86             $v = Yaml::parse($this->get($path, 'yml'));
   -1    87             $this->cache[$key] = $v;
   -1    88         }
   -1    89         return $this->cache[$key];
   -1    90     }
   -1    91 
   -1    92     public function putYaml($path, $data)
   -1    93     {
   -1    94         $key = "yml:$path";
   -1    95         $this->cache[$key] = $data;
   -1    96         $this->put($path, 'yml', Yaml::dump($data));
   -1    97     }
   -1    98 
   -1    99     public function getSubpages($path)
   -1   100     {
   -1   101         $subpages = array();
   -1   102         $p = dirname($this->getSrc($path, 'yml'));
   -1   103         foreach (scandir($p) as $name) {
   -1   104             if ($name !== '.' && $name !== '..' && $name[0] !== '_') {
   -1   105                 if (substr($name, -4) === '.yml') {
   -1   106                     $name = substr($name, 0, -4);
   -1   107                 }
   -1   108                 if (file_exists($this->getSrc("$path/$name", 'yml'))) {
   -1   109                     $subpages[$name] = "$path/$name";
   -1   110                 }
   -1   111             }
   -1   112         }
   -1   113         return $subpages;
   -1   114     }
   -1   115 
   -1   116     public function getPages($path = '')
   -1   117     {
   -1   118         $result = array();
   -1   119         $result[] = $path;
   -1   120         foreach ($this->getSubpages($path) as $name => $p) {
   -1   121             foreach ($this->getPages($p) as $pp) {
   -1   122                 $result[] = $pp;
   -1   123             }
   -1   124         }
   -1   125         return $result;
   -1   126     }
   -1   127 
   -1   128     public function getUrl($path)
   -1   129     {
   -1   130         return $this->targetUrl . $path . '/';
   -1   131     }
   -1   132 
   -1   133     public function uploadFile($path, $file)
   -1   134     {
   -1   135         $p = $this->targetDir . '/files' . $path . '/' . $file['name'];
   -1   136         mkdirp(dirname($p));
   -1   137         move_uploaded_file($file['tmp_name'], $p);
   -1   138     }
   -1   139 
   -1   140     public function createFileFolder($path, $name)
   -1   141     {
   -1   142         $p = $this->targetDir . '/files' . $path . '/' . $name;
   -1   143         mkdirp($p);
   -1   144     }
   -1   145 
   -1   146     public function getFiles($path)
   -1   147     {
   -1   148         $files = array();
   -1   149         $p = $this->targetDir . '/files' . $path;
   -1   150         $u = $this->targetUrl . '/files' . $path;
   -1   151         foreach (scandir($p) as $name) {
   -1   152             if ($name === '..' && $path !== '') {
   -1   153                 $files[] = array(
   -1   154                     'name' => $name,
   -1   155                     'path' => pathDirname($path),
   -1   156                     'is_file' => false,
   -1   157                 );
   -1   158             } elseif ($name !== '.' && $name !== '..') {
   -1   159                 $files[] = array(
   -1   160                     'name' => $name,
   -1   161                     'path' => "$path/$name",
   -1   162                     'url' => "$u/$name",
   -1   163                     'is_file' => is_file("$p/$name"),
   -1   164                     'is_image' => getimagesize("$p/$name"),
   -1   165                 );
   -1   166             }
   -1   167         }
   -1   168         return $files;
   -1   169     }
   -1   170 
   -1   171     public function rmFile($path)
   -1   172     {
   -1   173         rmr($this->targetDir . '/files' . $path);
   -1   174     }
   -1   175 
   -1   176     public function render($path, $verbose=false)
   -1   177     {
   -1   178         if ($verbose) {
   -1   179             echo trans('rendering') . " $path\n";
   -1   180         }
   -1   181 
   -1   182         $page = $this->getYaml($path);
   -1   183         $site = $this->getYaml('/_site');
   -1   184         $body = $this->get($path, 'md');
   -1   185 
   -1   186         $template = $page['_template'] ?? 'default.html';
   -1   187         $html = $this->twig->render($template, array(
   -1   188             'page' => $page,
   -1   189             'site' => $site,
   -1   190             'body' => $body,
   -1   191             'pupupu' => $this,
   -1   192         ));
   -1   193 
   -1   194         $target = $this->getTarget($path);
   -1   195         mkdirp(dirname($target));
   -1   196         _file_put_contents($target, $html);
   -1   197     }
   -1   198 
   -1   199     public function renderAll($verbose=false, $path='')
   -1   200     {
   -1   201         $this->render($path, $verbose);
   -1   202         foreach ($this->getSubpages($path) as $name => $p) {
   -1   203             $this->renderAll($verbose, $p);
   -1   204         }
   -1   205     }
   -1   206 
   -1   207     public function renderDynamic($verbose=false)
   -1   208     {
   -1   209         $site = $this->getYaml('/_site');
   -1   210         $dynamic = $site['_dynamic'] ?? array();
   -1   211         foreach ($dynamic as $path) {
   -1   212             $this->render($path, $verbose);
   -1   213         }
   -1   214     }
   -1   215 
   -1   216     public function checkPassword($name, $password)
   -1   217     {
   -1   218         $users = $this->getYaml('/_users');
   -1   219         return password_verify($password, $users[$name] ?? '');
   -1   220     }
   -1   221 
   -1   222     public function setPassword($name, $password=false)
   -1   223     {
   -1   224         $users = $this->getYaml('/_users');
   -1   225         if ($password) {
   -1   226             $users[$name] = password_hash($password, PASSWORD_DEFAULT);
   -1   227         } else {
   -1   228             unset($users[$name]);
   -1   229         }
   -1   230         $this->putYaml('/_users', $users);
   -1   231     }
   -1   232 }

diff --git a/index.php b/index.php

@@ -1,402 +1,7 @@
    1     1 <?php declare(strict_types=1);
    2     2 
    3    -1 require __DIR__ . '/vendor/autoload.php';
    4    -1 use Symfony\Component\Yaml\Yaml;
    5    -1 use Michelf\MarkdownExtra;
    6    -1 
    7    -1 class HttpException extends Exception {}
    8    -1 
    9    -1 function trans($s)
   10    -1 {
   11    -1     return $s;
   12    -1 }
   13    -1 
   14    -1 function rmdirs($path)
   15    -1 {
   16    -1     if ($path !== '.' && file_exists($path)) {
   17    -1         try {
   18    -1             rmdir($path);
   19    -1             rmdirs(dirname($path));
   20    -1         } finally {
   21    -1         }
   22    -1     }
   23    -1 }
   24    -1 
   25    -1 function rmfile($path)
   26    -1 {
   27    -1     if (file_exists($path)) {
   28    -1         unlink($path);
   29    -1     }
   30    -1     rmdirs(dirname($path));
   31    -1 }
   32    -1 
   33    -1 function rmr($path)
   34    -1 {
   35    -1     if (file_exists($path)) {
   36    -1         if (is_dir($path)) {
   37    -1             foreach (scandir($path) as $name) {
   38    -1                 if ($name !== '.' && $name !== '..') {
   39    -1                     rmr("$path/$name");
   40    -1                 }
   41    -1             }
   42    -1             rmdir($path);
   43    -1         } else {
   44    -1             unlink($path);
   45    -1         }
   46    -1     }
   47    -1 }
   48    -1 
   49    -1 function mkdirp($path)
   50    -1 {
   51    -1     if (!file_exists($path)) {
   52    -1         mkdir($path, 0777, true);
   53    -1     }
   54    -1 }
   55    -1 
   56    -1 function _file_put_contents($path, $content)
   57    -1 {
   58    -1     $normalized = preg_replace("/\r\n/", "\n", $content);
   59    -1     file_put_contents($path, $normalized);
   60    -1 }
   61    -1 
   62    -1 function shiftHeadings($html, $offset)
   63    -1 {
   64    -1     return preg_replace_callback('|(</?h)([1-6])([ >])|', function ($match) use ($offset) {
   65    -1         $target = max(1, min(6, (intval($match[2]) + $offset)));
   66    -1         return $match[1] . $target . $match[3];
   67    -1     }, $html);
   68    -1 }
   69    -1 
   70    -1 function pathDirname($path)
   71    -1 {
   72    -1     return implode('/', array_slice(explode('/', $path), 0, -1));
   73    -1 }
   74    -1 
   75    -1 function validatePath($path)
   76    -1 {
   77    -1     if (
   78    -1         (strlen($path) > 1 && $path[0] !== '/') ||
   79    -1         substr($path, -1) === '/' ||
   80    -1         strpos($path, '..') !== false
   81    -1     ) {
   82    -1         throw new HttpException(trans('Not Found'), 404);
   83    -1     } else {
   84    -1         return $path;
   85    -1     }
   86    -1 }
   87    -1 
   88    -1 class Pupupu
   89    -1 {
   90    -1     public function __construct($srcDir, $targetDir, $targetUrl)
   91    -1     {
   92    -1         $this->srcDir = $srcDir;
   93    -1         $this->targetDir = $targetDir;
   94    -1         $this->targetUrl = $targetUrl;
   95    -1 
   96    -1         $loader = new Twig_Loader_Filesystem($srcDir . '/_templates');
   97    -1         $this->twig = new Twig_Environment($loader);
   98    -1         $this->twig->addFilter(new Twig_Filter('md', function ($string) {
   99    -1             return MarkdownExtra::defaultTransform($string);
  100    -1         }));
  101    -1         $this->twig->addFilter(new Twig_Filter('shift_headings', 'shiftHeadings'));
  102    -1 
  103    -1         $this->cache = array();
  104    -1     }
  105    -1 
  106    -1     protected function pathIsFile($path)
  107    -1     {
  108    -1         return $path === '/_site' || $path === '/_users' || strpos($path, '.') !== false;
  109    -1     }
  110    -1 
  111    -1     protected function getSrc($path, $ext)
  112    -1     {
  113    -1         if ($this->pathIsFile($path)) {
  114    -1             return $this->srcDir . '/_content' . $path . '.' . $ext;
  115    -1         } else {
  116    -1             return $this->srcDir . '/_content' . $path . '/index' . '.' . $ext;
  117    -1         }
  118    -1     }
  119    -1 
  120    -1     protected function getTarget($path)
  121    -1     {
  122    -1         if ($this->pathIsFile($path)) {
  123    -1             return $this->targetDir . $path;
  124    -1         } else {
  125    -1             return $this->targetDir . $path . '/index.html';
  126    -1         }
  127    -1     }
  128    -1 
  129    -1     public function get($path, $ext)
  130    -1     {
  131    -1         $p = $this->getSrc($path, $ext);
  132    -1         if (file_exists($p)) {
  133    -1             return file_get_contents($p);
  134    -1         } else {
  135    -1             return '';
  136    -1         }
  137    -1     }
  138    -1 
  139    -1     public function put($path, $ext, $content)
  140    -1     {
  141    -1         $p = $this->getSrc($path, $ext);
  142    -1         mkdirp(dirname($p));
  143    -1         _file_put_contents($p, $content);
  144    -1     }
  145    -1 
  146    -1     public function rm($path)
  147    -1     {
  148    -1         rmfile($this->getSrc($path, 'yml'));
  149    -1         rmfile($this->getSrc($path, 'md'));
  150    -1         rmfile($this->getTarget($path));
  151    -1     }
  152    -1 
  153    -1     public function getYaml($path)
  154    -1     {
  155    -1         $key = "yml:$path";
  156    -1         if (!in_array($key, $this->cache)) {
  157    -1             $v = Yaml::parse($this->get($path, 'yml'));
  158    -1             $this->cache[$key] = $v;
  159    -1         }
  160    -1         return $this->cache[$key];
  161    -1     }
  162    -1 
  163    -1     public function putYaml($path, $data)
  164    -1     {
  165    -1         $key = "yml:$path";
  166    -1         $this->cache[$key] = $data;
  167    -1         $this->put($path, 'yml', Yaml::dump($data));
  168    -1     }
  169    -1 
  170    -1     public function getSubpages($path)
  171    -1     {
  172    -1         $subpages = array();
  173    -1         $p = dirname($this->getSrc($path, 'yml'));
  174    -1         foreach (scandir($p) as $name) {
  175    -1             if ($name !== '.' && $name !== '..' && $name[0] !== '_') {
  176    -1                 if (substr($name, -4) === '.yml') {
  177    -1                     $name = substr($name, 0, -4);
  178    -1                 }
  179    -1                 if (file_exists($this->getSrc("$path/$name", 'yml'))) {
  180    -1                     $subpages[$name] = "$path/$name";
  181    -1                 }
  182    -1             }
  183    -1         }
  184    -1         return $subpages;
  185    -1     }
  186    -1 
  187    -1     public function getPages($path = '')
  188    -1     {
  189    -1         $result = array();
  190    -1         $result[] = $path;
  191    -1         foreach ($this->getSubpages($path) as $name => $p) {
  192    -1             foreach ($this->getPages($p) as $pp) {
  193    -1                 $result[] = $pp;
  194    -1             }
  195    -1         }
  196    -1         return $result;
  197    -1     }
  198    -1 
  199    -1     public function getUrl($path)
  200    -1     {
  201    -1         return $this->targetUrl . $path . '/';
  202    -1     }
  203    -1 
  204    -1     public function uploadFile($path, $file)
  205    -1     {
  206    -1         $p = $this->targetDir . '/files' . $path . '/' . $file['name'];
  207    -1         mkdirp(dirname($p));
  208    -1         move_uploaded_file($file['tmp_name'], $p);
  209    -1     }
  210    -1 
  211    -1     public function createFileFolder($path, $name)
  212    -1     {
  213    -1         $p = $this->targetDir . '/files' . $path . '/' . $name;
  214    -1         mkdirp($p);
  215    -1     }
  216    -1 
  217    -1     public function getFiles($path)
  218    -1     {
  219    -1         $files = array();
  220    -1         $p = $this->targetDir . '/files' . $path;
  221    -1         $u = $this->targetUrl . '/files' . $path;
  222    -1         foreach (scandir($p) as $name) {
  223    -1             if ($name === '..' && $path !== '') {
  224    -1                 $files[] = array(
  225    -1                     'name' => $name,
  226    -1                     'path' => pathDirname($path),
  227    -1                     'is_file' => false,
  228    -1                 );
  229    -1             } elseif ($name !== '.' && $name !== '..') {
  230    -1                 $files[] = array(
  231    -1                     'name' => $name,
  232    -1                     'path' => "$path/$name",
  233    -1                     'url' => "$u/$name",
  234    -1                     'is_file' => is_file("$p/$name"),
  235    -1                     'is_image' => getimagesize("$p/$name"),
  236    -1                 );
  237    -1             }
  238    -1         }
  239    -1         return $files;
  240    -1     }
  241    -1 
  242    -1     public function rmFile($path)
  243    -1     {
  244    -1         rmr($this->targetDir . '/files' . $path);
  245    -1     }
  246    -1 
  247    -1     public function render($path, $verbose=false)
  248    -1     {
  249    -1         if ($verbose) {
  250    -1             echo trans('rendering') . " $path\n";
  251    -1         }
  252    -1 
  253    -1         $page = $this->getYaml($path);
  254    -1         $site = $this->getYaml('/_site');
  255    -1         $body = $this->get($path, 'md');
  256    -1 
  257    -1         $template = $page['_template'] ?? 'default.html';
  258    -1         $html = $this->twig->render($template, array(
  259    -1             'page' => $page,
  260    -1             'site' => $site,
  261    -1             'body' => $body,
  262    -1             'pupupu' => $this,
  263    -1         ));
  264    -1 
  265    -1         $target = $this->getTarget($path);
  266    -1         mkdirp(dirname($target));
  267    -1         _file_put_contents($target, $html);
  268    -1     }
  269    -1 
  270    -1     public function renderAll($verbose=false, $path='')
  271    -1     {
  272    -1         $this->render($path, $verbose);
  273    -1         foreach ($this->getSubpages($path) as $name => $p) {
  274    -1             $this->renderAll($verbose, $p);
  275    -1         }
  276    -1     }
  277    -1 
  278    -1     public function renderDynamic($verbose=false)
  279    -1     {
  280    -1         $site = $this->getYaml('/_site');
  281    -1         $dynamic = $site['_dynamic'] ?? array();
  282    -1         foreach ($dynamic as $path) {
  283    -1             $this->render($path, $verbose);
  284    -1         }
  285    -1     }
  286    -1 
  287    -1     public function checkPassword($name, $password)
  288    -1     {
  289    -1         $users = $this->getYaml('/_users');
  290    -1         return password_verify($password, $users[$name] ?? '');
  291    -1     }
  292    -1 
  293    -1     public function setPassword($name, $password=false)
  294    -1     {
  295    -1         $users = $this->getYaml('/_users');
  296    -1         if ($password) {
  297    -1             $users[$name] = password_hash($password, PASSWORD_DEFAULT);
  298    -1         } else {
  299    -1             unset($users[$name]);
  300    -1         }
  301    -1         $this->putYaml('/_users', $users);
  302    -1     }
  303    -1 }
  304    -1 
  305    -1 function pagesView($pupupu, $twig)
  306    -1 {
  307    -1     echo $twig->render('pages.html', array(
  308    -1         'pages' => $pupupu->getPages(),
  309    -1     ));
  310    -1 }
  311    -1 
  312    -1 function filesView($pupupu, $twig)
  313    -1 {
  314    -1     $path = validatePath(substr($_GET['path'], 7));
  315    -1 
  316    -1     if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  317    -1         echo $twig->render('files.html', array(
  318    -1             'files' => $pupupu->getFiles($path),
  319    -1         ));
  320    -1     } elseif (isset($_FILES['file'])) {
  321    -1         $pupupu->uploadFile($path, $_FILES['file']);
  322    -1         header('Location: ', true, 302);
  323    -1     } elseif (isset($_POST['folder'])) {
  324    -1         $pupupu->createFileFolder($path, $_POST['folder']);
  325    -1         header('Location: ', true, 302);
  326    -1     } elseif (isset($_POST['delete'])) {
  327    -1         $pupupu->rmFile($path . '/' . $_POST['name']);
  328    -1         header('Location: ', true, 302);
  329    -1     } else {
  330    -1         throw new HttpException(trans('Invalid request'), 400);
  331    -1     }
  332    -1 }
  333    -1 
  334    -1 function siteView($pupupu, $twig)
  335    -1 {
  336    -1     if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  337    -1         echo $twig->render('site.html', array(
  338    -1             'yml' => $pupupu->get('/_site', 'yml'),
  339    -1         ));
  340    -1     } else {
  341    -1         $pupupu->put('/_site', 'yml', $_POST['yml']);
  342    -1         $pupupu->renderAll();
  343    -1         header('Location: ', true, 302);
  344    -1     }
  345    -1 }
  346    -1 
  347    -1 function pageView($pupupu, $twig)
  348    -1 {
  349    -1     if (isset($_GET['add'])) {
  350    -1         $path = $_GET['path'] . '/' . $_GET['add'];
  351    -1         header('Location: ?path=' . urlencode($path), true, 302);
  352    -1     } else {
  353    -1         $path = validatePath($_GET['path']);
  354    -1 
  355    -1         if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  356    -1             echo $twig->render('page.html', array(
  357    -1                 'yml' => $pupupu->get($path, 'yml'),
  358    -1                 'md' => $pupupu->get($path, 'md'),
  359    -1                 'url' => $pupupu->getUrl($path),
  360    -1             ));
  361    -1         } elseif (isset($_POST['delete'])) {
  362    -1             if ($path === '') {
  363    -1                 throw new HttpException(trans('Cannot delete root'), 400);
  364    -1             }
  365    -1             $pupupu->rm($path);
  366    -1             $target = pathDirname($path);
  367    -1             header('Location: ?', true, 302);
  368    -1         } else {
  369    -1             $pupupu->put($path, 'yml', $_POST['yml']);
  370    -1             $pupupu->put($path, 'md', $_POST['md']);
  371    -1             $pupupu->render($path);
  372    -1             $pupupu->renderDynamic();
  373    -1             header('Location: ', true, 302);
  374    -1         }
  375    -1     }
  376    -1 }
  377    -1 
  378    -1 function usersView($pupupu, $twig)
  379    -1 {
  380    -1     if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  381    -1         echo $twig->render('users.html', array(
  382    -1             'users' => $pupupu->getYaml('/_users'),
  383    -1         ));
  384    -1     } elseif (isset($_POST['delete'])) {
  385    -1         $pupupu->setPassword($_POST['name'], false);
  386    -1         header('Location: ', true, 302);
  387    -1     } else {
  388    -1         $pupupu->setPassword($_POST['name'], $_POST['password']);
  389    -1         header('Location: ', true, 302);
  390    -1     }
  391    -1 }
  392    -1 
  393    -1 function errorView($pupupu, $twig, $error)
  394    -1 {
  395    -1     http_response_code($error->getCode());
  396    -1     echo $twig->render('error.html', array(
  397    -1         'error' => $error,
  398    -1     ));
  399    -1 }
   -1     3 include_once('api.php');
   -1     4 include_once('views.php');
  400     5 
  401     6 function getAuth()
  402     7 {

diff --git a/utils.php b/utils.php

@@ -0,0 +1,59 @@
   -1     1 <?php declare(strict_types=1);
   -1     2 
   -1     3 function trans($s)
   -1     4 {
   -1     5     return $s;
   -1     6 }
   -1     7 
   -1     8 function rmdirs($path)
   -1     9 {
   -1    10     if ($path !== '.' && file_exists($path)) {
   -1    11         try {
   -1    12             rmdir($path);
   -1    13             rmdirs(dirname($path));
   -1    14         } finally {
   -1    15         }
   -1    16     }
   -1    17 }
   -1    18 
   -1    19 function rmfile($path)
   -1    20 {
   -1    21     if (file_exists($path)) {
   -1    22         unlink($path);
   -1    23     }
   -1    24     rmdirs(dirname($path));
   -1    25 }
   -1    26 
   -1    27 function rmr($path)
   -1    28 {
   -1    29     if (file_exists($path)) {
   -1    30         if (is_dir($path)) {
   -1    31             foreach (scandir($path) as $name) {
   -1    32                 if ($name !== '.' && $name !== '..') {
   -1    33                     rmr("$path/$name");
   -1    34                 }
   -1    35             }
   -1    36             rmdir($path);
   -1    37         } else {
   -1    38             unlink($path);
   -1    39         }
   -1    40     }
   -1    41 }
   -1    42 
   -1    43 function mkdirp($path)
   -1    44 {
   -1    45     if (!file_exists($path)) {
   -1    46         mkdir($path, 0777, true);
   -1    47     }
   -1    48 }
   -1    49 
   -1    50 function _file_put_contents($path, $content)
   -1    51 {
   -1    52     $normalized = preg_replace("/\r\n/", "\n", $content);
   -1    53     file_put_contents($path, $normalized);
   -1    54 }
   -1    55 
   -1    56 function pathDirname($path)
   -1    57 {
   -1    58     return implode('/', array_slice(explode('/', $path), 0, -1));
   -1    59 }

diff --git a/views.php b/views.php

@@ -0,0 +1,114 @@
   -1     1 <?php declare(strict_types=1);
   -1     2 
   -1     3 include_once('utils.php');
   -1     4 
   -1     5 class HttpException extends Exception {}
   -1     6 
   -1     7 function validatePath($path)
   -1     8 {
   -1     9     if (
   -1    10         (strlen($path) > 1 && $path[0] !== '/') ||
   -1    11         substr($path, -1) === '/' ||
   -1    12         strpos($path, '..') !== false
   -1    13     ) {
   -1    14         throw new HttpException(trans('Not Found'), 404);
   -1    15     } else {
   -1    16         return $path;
   -1    17     }
   -1    18 }
   -1    19 
   -1    20 function pagesView($pupupu, $twig)
   -1    21 {
   -1    22     echo $twig->render('pages.html', array(
   -1    23         'pages' => $pupupu->getPages(),
   -1    24     ));
   -1    25 }
   -1    26 
   -1    27 function filesView($pupupu, $twig)
   -1    28 {
   -1    29     $path = validatePath(substr($_GET['path'], 7));
   -1    30 
   -1    31     if ($_SERVER['REQUEST_METHOD'] == 'GET') {
   -1    32         echo $twig->render('files.html', array(
   -1    33             'files' => $pupupu->getFiles($path),
   -1    34         ));
   -1    35     } elseif (isset($_FILES['file'])) {
   -1    36         $pupupu->uploadFile($path, $_FILES['file']);
   -1    37         header('Location: ', true, 302);
   -1    38     } elseif (isset($_POST['folder'])) {
   -1    39         $pupupu->createFileFolder($path, $_POST['folder']);
   -1    40         header('Location: ', true, 302);
   -1    41     } elseif (isset($_POST['delete'])) {
   -1    42         $pupupu->rmFile($path . '/' . $_POST['name']);
   -1    43         header('Location: ', true, 302);
   -1    44     } else {
   -1    45         throw new HttpException(trans('Invalid request'), 400);
   -1    46     }
   -1    47 }
   -1    48 
   -1    49 function siteView($pupupu, $twig)
   -1    50 {
   -1    51     if ($_SERVER['REQUEST_METHOD'] == 'GET') {
   -1    52         echo $twig->render('site.html', array(
   -1    53             'yml' => $pupupu->get('/_site', 'yml'),
   -1    54         ));
   -1    55     } else {
   -1    56         $pupupu->put('/_site', 'yml', $_POST['yml']);
   -1    57         $pupupu->renderAll();
   -1    58         header('Location: ', true, 302);
   -1    59     }
   -1    60 }
   -1    61 
   -1    62 function pageView($pupupu, $twig)
   -1    63 {
   -1    64     if (isset($_GET['add'])) {
   -1    65         $path = $_GET['path'] . '/' . $_GET['add'];
   -1    66         header('Location: ?path=' . urlencode($path), true, 302);
   -1    67     } else {
   -1    68         $path = validatePath($_GET['path']);
   -1    69 
   -1    70         if ($_SERVER['REQUEST_METHOD'] == 'GET') {
   -1    71             echo $twig->render('page.html', array(
   -1    72                 'yml' => $pupupu->get($path, 'yml'),
   -1    73                 'md' => $pupupu->get($path, 'md'),
   -1    74                 'url' => $pupupu->getUrl($path),
   -1    75             ));
   -1    76         } elseif (isset($_POST['delete'])) {
   -1    77             if ($path === '') {
   -1    78                 throw new HttpException(trans('Cannot delete root'), 400);
   -1    79             }
   -1    80             $pupupu->rm($path);
   -1    81             $target = pathDirname($path);
   -1    82             header('Location: ?', true, 302);
   -1    83         } else {
   -1    84             $pupupu->put($path, 'yml', $_POST['yml']);
   -1    85             $pupupu->put($path, 'md', $_POST['md']);
   -1    86             $pupupu->render($path);
   -1    87             $pupupu->renderDynamic();
   -1    88             header('Location: ', true, 302);
   -1    89         }
   -1    90     }
   -1    91 }
   -1    92 
   -1    93 function usersView($pupupu, $twig)
   -1    94 {
   -1    95     if ($_SERVER['REQUEST_METHOD'] == 'GET') {
   -1    96         echo $twig->render('users.html', array(
   -1    97             'users' => $pupupu->getYaml('/_users'),
   -1    98         ));
   -1    99     } elseif (isset($_POST['delete'])) {
   -1   100         $pupupu->setPassword($_POST['name'], false);
   -1   101         header('Location: ', true, 302);
   -1   102     } else {
   -1   103         $pupupu->setPassword($_POST['name'], $_POST['password']);
   -1   104         header('Location: ', true, 302);
   -1   105     }
   -1   106 }
   -1   107 
   -1   108 function errorView($pupupu, $twig, $error)
   -1   109 {
   -1   110     http_response_code($error->getCode());
   -1   111     echo $twig->render('error.html', array(
   -1   112         'error' => $error,
   -1   113     ));
   -1   114 }