Comment: | Change of strategy: will do rich text editing later, first let's just migrate the wiki to web |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA1: |
246bb415009482e2c37ce6bc81b28c5b |
User & Date: | bohwaz on 2020-12-14 23:42:34 |
Other Links: | branch diff | manifest | tags |
2020-12-15
| ||
00:42 | Editor is working, now onto preview check-in: 273b468d7d user: bohwaz tags: dev | |
2020-12-14
| ||
23:42 | Change of strategy: will do rich text editing later, first let's just migrate the wiki to web check-in: 246bb41500 user: bohwaz tags: dev | |
02:21 | More work on the rich text editor, but Trix sucks a lot :( check-in: e1d70bedb9 user: bohwaz tags: dev | |
Modified src/VERSION from [4c3ec47899] to [528cf42af9].
|
| | | 1 | 1.1.0 |
Modified src/include/lib/Garradin/Entities/Files/File.php from [0da72de85a] to [ca4754f46f].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php namespace Garradin\Entities\Files; use KD2\Image; use Garradin\DB; use Garradin\Entity; use Garradin\UserException; use Garradin\Membres\Session; use Garradin\Membres; use Garradin\Utils; use Garradin\Files\Files; use const Garradin\{WWW_URL, ENABLE_XSENDFILE}; class File extends Entity { | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php namespace Garradin\Entities\Files; use KD2\Image; use Garradin\DB; use Garradin\Entity; use Garradin\UserException; use Garradin\Membres\Session; use Garradin\Membres; use Garradin\Utils; use Garradin\Entities\Web\Page; use Garradin\Files\Files; use const Garradin\{WWW_URL, ENABLE_XSENDFILE}; class File extends Entity { |
︙ | ︙ | |||
90 91 92 93 94 95 96 | { $return = parent::save(); // Store content in search table if ($return && substr($this->type, 0, 5) == 'text/') { $content = Files::callStorage('fetch', $this); | | | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | { $return = parent::save(); // Store content in search table if ($return && substr($this->type, 0, 5) == 'text/') { $content = Files::callStorage('fetch', $this); if ($this->type == Page::FILE_TYPE_HTML) { $content = strip_tags($content); } if ($this->type == Page::FILE_TYPE_ENCRYPTED) { $content = 'Contenu chiffré'; } DB::getInstance()->preparedQuery('INSERT OR REPLACE INTO files_search (id, content) VALUES (?, ?);', $this->id(), $content); } return $return; } public function store(string $source_path = null, $source_content = null): self { |
︙ | ︙ | |||
146 147 148 149 150 151 152 | throw new \RuntimeException('Le serveur n\'a aucune bibliothèque de gestion d\'image installée, et ne peut donc pas accepter les images. Installez Imagick ou GD.'); } throw new UserException('Fichier image invalide'); } } | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | throw new \RuntimeException('Le serveur n\'a aucune bibliothèque de gestion d\'image installée, et ne peut donc pas accepter les images. Installez Imagick ou GD.'); } throw new UserException('Fichier image invalide'); } } if (!Files::callStorage('store', $this, $source_path, $source_content)) { throw new UserException('Le fichier n\'a pas pu être enregistré.'); } return $this; } static protected function create(string $name, string $source_path = null, $source_content = null): self |
︙ | ︙ | |||
470 471 472 473 474 475 476 | } public function fetch() { return Files::callStorage('fetch', $this); } | < < < < < < < < < < < < < < < < < < < < < < < | 471 472 473 474 475 476 477 478 479 480 481 482 483 484 | } public function fetch() { return Files::callStorage('fetch', $this); } public function checkReadAccess(Session $session): bool { // Web and config files should be marked as public when not in draft if ($this->public) { return true; } |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Web/Page.php from [a9d6f2dd6b] to [a395c53d59].
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | const STATUS_ONLINE = 1; const STATUS_DRAFT = 0; const TYPE_CATEGORY = 1; const TYPE_PAGE = 2; public function url(): string { $url = WWW_URL . $this->uri; if ($this->type == self::TYPE_CATEGORY) { $url .= '/'; } | > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | const STATUS_ONLINE = 1; const STATUS_DRAFT = 0; const TYPE_CATEGORY = 1; const TYPE_PAGE = 2; const FILE_TYPE_HTML = 'text/html'; const FILE_TYPE_ENCRYPTED = 'text/vnd.skriv.encrypted'; const FILE_TYPE_SKRIV = 'text/vnd.skriv'; public function url(): string { $url = WWW_URL . $this->uri; if ($this->type == self::TYPE_CATEGORY) { $url .= '/'; } |
︙ | ︙ | |||
84 85 86 87 88 89 90 | public function selfCheck(): void { $this->assert($this->type === self::TYPE_CATEGORY || $this->type === self::TYPE_PAGE, 'Unknown page type'); $this->assert($this->status === self::STATUS_DRAFT || $this->status === self::STATUS_ONLINE, 'Unknown page status'); $this->assert(trim($this->title) !== '', 'Le titre ne peut rester vide'); $this->assert(trim($this->uri) !== '', 'L\'URI ne peut rester vide'); | | > > > > | > > > > > > > > > > > | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | public function selfCheck(): void { $this->assert($this->type === self::TYPE_CATEGORY || $this->type === self::TYPE_PAGE, 'Unknown page type'); $this->assert($this->status === self::STATUS_DRAFT || $this->status === self::STATUS_ONLINE, 'Unknown page status'); $this->assert(trim($this->title) !== '', 'Le titre ne peut rester vide'); $this->assert(trim($this->uri) !== '', 'L\'URI ne peut rester vide'); $this->assert((bool) $this->_file, 'Fichier manquant'); } public function importForm(array $source = null) { if (null === $source) { $source = $_POST; } if (isset($source['parent_id']) && is_array($source['parent_id'])) { $source['parent_id'] = key($source['parent_id']); } $file = $this->file(); if (isset($source['content']) && sha1($source['content']) != $file->hash) { $file->store(null, $source['content']); } if (isset($source['date']) && isset($source['date_time'])) { $file->importForm(['created' => sprintf('%s %s', $source['date'], $source['date_time'])]); } if (!empty($source['encrypted']) ) { $file->set('type', self::FILE_TYPE_ENCRYPTED); } else { $file->set('type', self::FILE_TYPE_SKRIV); } return $this->import($source); } static public function create(int $type, string $title, string $content, int $status = self::STATUS_ONLINE): Page { |
︙ | ︙ | |||
132 133 134 135 136 137 138 | } $file->store(null, $content); $page->save(); return $page; } | | > > > > > > > > > > > > > > > > > > > > > > > | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | } $file->store(null, $content); $page->save(); return $page; } public function render(): string { static $render_types = [self::FILE_TYPE_SKRIV, self::FILE_TYPE_ENCRYPTED, self::FILE_TYPE_HTML]; if (!in_array($this->type, $render_types)) { throw new \LogicException('Render can not be called on files of type: ' . $this->type); } $content = $this->fetch(); if ($this->type == self::FILE_TYPE_HTML) { return \Garradin\Files\Render\HTML::render($this, $content); } elseif ($this->type == self::FILE_TYPE_SKRIV) { return \Garradin\Files\Render\Skriv::render($this, $content); } elseif ($this->type == self::FILE_TYPE_ENCRYPTED) { return \Garradin\Files\Render\EncryptedSkriv::render($this, $content); } throw new \LogicException('Unknown render type'); } } |
Modified src/include/lib/Garradin/Entity.php from [de03b9076a] to [7ef148b9bf].
︙ | ︙ | |||
44 45 46 47 48 49 50 | elseif (preg_match('!^\d{2}/\d{2}/\d{4}$!', $value)) { return \DateTime::createFromFormat('d/m/Y', $value); } elseif (null !== $value) { throw new ValidationException('Format de date invalide (merci d\'utiliser le format JJ/MM/AAAA) : ' . $value); } } | | > | | > > > | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | elseif (preg_match('!^\d{2}/\d{2}/\d{4}$!', $value)) { return \DateTime::createFromFormat('d/m/Y', $value); } elseif (null !== $value) { throw new ValidationException('Format de date invalide (merci d\'utiliser le format JJ/MM/AAAA) : ' . $value); } } elseif ($type == 'DateTime') { if (preg_match('!^\d{2}/\d{2}/\d{4}\s\d{2}:\d{2}$!', $value)) { return \DateTime::createFromFormat('d/m/Y H:i', $value); } } return parent::filterUserValue($type, $value, $key); } protected function assert(bool $test, string $message = null): void { if ($test) { return; } |
︙ | ︙ |
Modified src/include/lib/Garradin/Files/Files.php from [f113720eda] to [216db749fc].
1 2 3 4 5 6 7 8 9 10 | <?php namespace Garradin\Files; use Garradin\Static_Cache; use Garradin\DB; use Garradin\Membres\Session; use Garradin\Entities\Files\File; use KD2\DB\EntityManager as EM; | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php namespace Garradin\Files; use Garradin\Static_Cache; use Garradin\DB; use Garradin\Membres\Session; use Garradin\Entities\Files\File; use KD2\DB\EntityManager as EM; use const Garradin\{FILE_STORAGE_BACKEND, FILE_STORAGE_QUOTA}; class Files { static public function callStorage(string $function, ...$args) { // Check that we can store this data if ($function == 'store') { $quota = FILE_STORAGE_QUOTA ?: self::callStorage('getQuota'); $used = self::callStorage('getTotalSize'); $size = $args[0] ? filesize($args[1]) : strlen($args[2]); if (($used + $size) >= $quota) { throw new \OutOfBoundsException('File quota has been exhausted'); } } $storage = FILE_STORAGE_BACKEND ?? 'SQLite'; |
︙ | ︙ |
Modified src/include/lib/Garradin/Files/Storage/SQLite.php from [bb87054c4e] to [bd5dafc63d].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php namespace Garradin\Files\Storage; use Garradin\Entities\Files\File; use Garradin\Static_Cache; use Garradin\DB; class SQLite implements StorageInterface { /** * Renvoie le chemin vers le fichier local en cache, et le crée s'il n'existe pas * @return string Chemin local */ | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php namespace Garradin\Files\Storage; use Garradin\Entities\Files\File; use Garradin\Static_Cache; use Garradin\DB; use const Garradin\DB_FILE; class SQLite implements StorageInterface { /** * Renvoie le chemin vers le fichier local en cache, et le crée s'il n'existe pas * @return string Chemin local */ |
︙ | ︙ | |||
26 27 28 29 30 31 32 | return Static_Cache::getPath($cache_id); } static public function store(File $file, ?string $path, ?string $content): bool { $db = DB::getInstance(); | | > > > > > > > > > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | return Static_Cache::getPath($cache_id); } static public function store(File $file, ?string $path, ?string $content): bool { $db = DB::getInstance(); if (!$file->content_id) { $db->preparedQuery('INSERT INTO files_contents (hash, content) VALUES (?, ?, zeroblob(?));', $file->hash, $file->size); $file->content_id = $db->lastInsertId(); } else { $cache_id = 'files.' . $file->content_id; Static_Cache::remove($cache_id); $db->preparedQuery('UPDATE files_contents SET hash = ?, content = zeroblob(?) WHERE id = ?;', $file->hash, $file->size, $file->content_id); } $blob = $db->openBlob('files_contents', 'content', $file->content_id, 'main', SQLITE3_OPEN_READWRITE); if (null !== $content) { fwrite($blob, $content); } else { |
︙ | ︙ | |||
77 78 79 80 81 82 83 | static public function move(File $old_file, File $new_file): bool { return true; } static public function getTotalSize(): int { | | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | static public function move(File $old_file, File $new_file): bool { return true; } static public function getTotalSize(): int { return (int) DB::getInstance()->firstColumn('SELECT SUM(LENGTH(content)) FROM files_contents;'); } static public function getQuota(): int { return disk_total_space(dirname(DB_FILE)); } } |
Modified src/include/lib/Garradin/Template.php from [b8eafe3cb8] to [3a8e6cf21e].
︙ | ︙ | |||
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | $attributes = array_diff_key($params, array_flip($params_list)); $params = array_intersect_key($params, array_flip($params_list)); extract($params, \EXTR_SKIP); if (!isset($name, $type)) { throw new \InvalidArgumentException('Missing name or type'); } $current_value = null; $current_value_from_user = false; if (isset($_POST[$name])) { $current_value = $_POST[$name]; $current_value_from_user = true; } elseif (isset($source) && is_object($source) && isset($source->$name)) { $current_value = $source->$name; } elseif (isset($source) && is_array($source) && isset($source[$name])) { $current_value = $source[$name]; } elseif (isset($default)) { $current_value = $default; } if ($type == 'date' && is_object($current_value) && $current_value instanceof \DateTimeInterface) { $current_value = $current_value->format('d/m/Y'); } elseif ($type == 'date' && is_string($current_value)) { if ($v = \DateTime::createFromFormat('!Y-m-d', $current_value)) { $current_value = $v->format('d/m/Y'); } } | > > > > > > > > > > > > > > | 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | $attributes = array_diff_key($params, array_flip($params_list)); $params = array_intersect_key($params, array_flip($params_list)); extract($params, \EXTR_SKIP); if (!isset($name, $type)) { throw new \InvalidArgumentException('Missing name or type'); } $suffix = null; if ($type == 'datetime') { $type = 'date'; $tparams = func_get_arg(0); $tparams['type'] = 'time'; $tparams['name'] = sprintf('%s_time', $name); unset($tparams['label']); $suffix = self::formInput($tparams); } $current_value = null; $current_value_from_user = false; if (isset($_POST[$name])) { $current_value = $_POST[$name]; $current_value_from_user = true; } elseif (isset($source) && is_object($source) && isset($source->$name)) { $current_value = $source->$name; } elseif (isset($source) && is_array($source) && isset($source[$name])) { $current_value = $source[$name]; } elseif (isset($default)) { $current_value = $default; } if ($type == 'date' && is_object($current_value) && $current_value instanceof \DateTimeInterface) { $current_value = $current_value->format('d/m/Y'); } elseif ($type == 'time' && is_object($current_value) && $current_value instanceof \DateTimeInterface) { $current_value = $current_value->format('H:i'); } elseif ($type == 'date' && is_string($current_value)) { if ($v = \DateTime::createFromFormat('!Y-m-d', $current_value)) { $current_value = $v->format('d/m/Y'); } } |
︙ | ︙ | |||
269 270 271 272 273 274 275 276 277 278 279 280 281 282 | $type = 'text'; $attributes['placeholder'] = 'JJ/MM/AAAA'; $attributes['data-input'] = 'date'; $attributes['size'] = 12; $attributes['maxlength'] = 10; $attributes['pattern'] = '\d\d?/\d\d?/\d{4}'; } // Create attributes string if (array_key_exists('required', $attributes)) { $attributes['required'] = 'required'; } if (!empty($attributes['disabled'])) { | > > > > > > > > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | $type = 'text'; $attributes['placeholder'] = 'JJ/MM/AAAA'; $attributes['data-input'] = 'date'; $attributes['size'] = 12; $attributes['maxlength'] = 10; $attributes['pattern'] = '\d\d?/\d\d?/\d{4}'; } elseif ($type == 'time') { $type = 'text'; $attributes['placeholder'] = 'HH:MM'; $attributes['data-input'] = 'time'; $attributes['size'] = 8; $attributes['maxlength'] = 5; $attributes['pattern'] = '\d\d?:\d\d?'; } // Create attributes string if (array_key_exists('required', $attributes)) { $attributes['required'] = 'required'; } if (!empty($attributes['disabled'])) { |
︙ | ︙ | |||
375 376 377 378 379 380 381 382 383 384 385 386 387 388 | return $input; } if ($type == 'file') { $input .= sprintf('<input type="hidden" name="MAX_FILE_SIZE" value="%d" id="f_maxsize" />', Utils::return_bytes(Utils::getMaxUploadSize())); } $label = sprintf('<label for="%s">%s</label>', $attributes['id'], $this->escape($label)); if ($type == 'radio' || $type == 'checkbox') { $out = sprintf('<dd>%s %s', $input, $label); if (isset($help)) { | > > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | return $input; } if ($type == 'file') { $input .= sprintf('<input type="hidden" name="MAX_FILE_SIZE" value="%d" id="f_maxsize" />', Utils::return_bytes(Utils::getMaxUploadSize())); } $input .= $suffix; $label = sprintf('<label for="%s">%s</label>', $attributes['id'], $this->escape($label)); if ($type == 'radio' || $type == 'checkbox') { $out = sprintf('<dd>%s %s', $input, $label); if (isset($help)) { |
︙ | ︙ | |||
402 403 404 405 406 407 408 409 410 411 412 413 414 415 | $out .= sprintf('<dd class="help">%s</dd>', $this->escape($help)); } } return $out; } protected function formField(array $params, $escape = true) { if (!isset($params['name'])) { throw new \BadFunctionCallException('name argument is mandatory'); } | > > > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | $out .= sprintf('<dd class="help">%s</dd>', $this->escape($help)); } } return $out; } /** * @deprecated */ protected function formField(array $params, $escape = true) { if (!isset($params['name'])) { throw new \BadFunctionCallException('name argument is mandatory'); } |
︙ | ︙ |
Deleted src/templates/admin/wiki/creer.tpl version [e927f05760].
|
| < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/templates/admin/wiki/editer.tpl version [fa476c25ad].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/templates/admin/wiki/historique.tpl version [ad74fa04e5].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/templates/admin/wiki/recent.tpl version [b616432b4b].
|
| < < < < < < < < < < < < < < < < < < < < |
Deleted src/templates/admin/wiki/supprimer.tpl version [0d139503af].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Name change from src/templates/admin/wiki/_fichiers.tpl to src/templates/web/_attach.php.
︙ | ︙ |
Name change from src/templates/admin/wiki/_preview.tpl to src/templates/web/_preview.tpl.
Name change from src/templates/admin/wiki/_chercher_parent.tpl to src/templates/web/_selector.tpl.
︙ | ︙ |
Modified src/templates/web/edit.tpl from [5664c13bce] to [3ef23491dd].
1 2 3 4 5 | {include file="admin/_head.tpl" title="Édition : %s"|args:$page.title current="web"} {form_errors} <form method="post" action="{$self_url}" class="web-edit"> | > | | | > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | {include file="admin/_head.tpl" title="Édition : %s"|args:$page.title current="web"} {form_errors} <form method="post" action="{$self_url}" class="web-edit"> <fieldset class="wikiMain"> <legend>Informations générales</legend> <dl> {input type="text" name="title" source=$page required=true label="Titre"} {input type="text" name="uri" source=$page required=true label="Adresse unique URI" help="Utilisée pour désigner l'adresse de la page sur le site. Ne peut comporter que des lettres, des chiffres, des tirets et des tirets bas." pattern="[A-Za-z0-9_-]+"} {input type="list" name="parent_id" label="Catégorie" default=$parent target="web/_selector.php?current=%d"|args:$page.parent_id required=true} {input type="datetime" name="date" label="Date" required=true default=$page->file()->created} <dt>Statut</dt> {input type="radio" name="status" value=$page::STATUS_ONLINE label="En ligne" source=$page} {input type="radio" name="status" value=$page::STATUS_DRAFT label="Brouillon" source=$page} </dl> </fieldset> <fieldset class="wikiEncrypt"> <dl> <dt> <input type="checkbox" name="encryption" id="f_encryption" {if $encrypted} checked="checked"{/if} value="1" onchange="checkEncryption(this);" /> <label for="f_encryption">Chiffrer le contenu</label> <i>(facultatif)</i> </dt> <noscript> <dd>Nécessite JavaScript activé pour fonctionner !</dd> </noscript> <dd>Mot de passe : <i id="encryptPasswordDisplay" title="Chiffrement désactivé">désactivé</i></dd> <dd class="help">Le mot de passe n'est ni transmis ni enregistré, vous seul le connaissez, il n'est pas possible de retrouver le contenu si vous l'oubliez.</dd> </dl> </fieldset> <fieldset class="wikiText"> <div class="textEditor"> {input type="textarea" name="content" cols="70" rows="35" default=$page->file()->fetch() required=true} </div> </fieldset> <p class="submit"> {csrf_field key=$csrf_key} <input type="hidden" name="editing_started" value="{$editing_started}" /> {button type="submit" name="save" label="Enregistrer" shape="upload" class="main"} {button type="submit" name="cancel" label="Retourner à la liste" shape="reset"} </p> </form> <script type="text/javascript"> var page_id = '{$page.id}'; {literal} (function() { window.changeParent = function(parent, title) { if (parent == page_id) { return false; } $('#f_parent').value = parent; $('#current_parent_name').innerHTML = title; return true; }; window.browseWikiForParent = function() { window.open('_chercher_parent.php?parent=' + $('#f_parent').value, 'browseParent', 'width=500,height=600,top=150,left=150,scrollbars=1,location=false'); }; if ($('#f_encryption').checked) { wikiDecrypt(true); } if (location.hash == '#saved') { location.hash = ''; let c = document.createElement('p'); c.className = 'block confirm'; c.id = 'confirm_saved'; c.innerText = 'Enregistré'; c.style.right = '-10em'; document.querySelector('#f_content').parentNode.appendChild(c); window.setTimeout(() => { c.style.right = ''; }, 200); window.setTimeout(() => { c.style.opacity = 0; }, 3000); } }()); </script> {/literal} {include file="admin/_foot.tpl"} |
Name change from src/templates/admin/wiki/page.tpl to src/templates/web/page.tpl.
︙ | ︙ |
Name change from src/templates/admin/wiki/chercher.tpl to src/templates/web/search.tpl.
︙ | ︙ |
Modified src/www/admin/static/scripts/wiki-encryption.js from [2f0eae2745] to [78239d4188].
︙ | ︙ | |||
70 71 72 73 74 75 76 | encryptPassword = window.prompt('Mot de passe ?'); if (!encryptPassword) { encryptPassword = null; | | | | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | encryptPassword = window.prompt('Mot de passe ?'); if (!encryptPassword) { encryptPassword = null; if (document.getElementById('f_content')) { if (window.confirm("Aucun mot de passe entré.\nDésactiver le chiffrement et effacer le contenu ?")) { document.getElementById('f_content').value = ''; document.getElementById('f_encrypted').checked = false; checkEncryption(document.getElementById('f_encrypted')); } else { wikiDecrypt(); } } |
︙ | ︙ | |||
108 109 110 111 112 113 114 | } iteration++; window.setTimeout(decrypt, 500); return; } | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | } iteration++; window.setTimeout(decrypt, 500); return; } var content = document.getElementById('f_content'); var edit = true; if (!content) { content = document.getElementById('wikiEncryptedContent'); edit = false; } |
︙ | ︙ | |||
144 145 146 147 148 149 150 | content.style.display = 'block'; document.getElementById('wikiEncryptedMessage').style.display = 'none'; content.innerHTML = formatContent(wikiContent); } else { content.value = wikiContent; | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | content.style.display = 'block'; document.getElementById('wikiEncryptedMessage').style.display = 'none'; content.innerHTML = formatContent(wikiContent); } else { content.value = wikiContent; checkEncryption(document.getElementById('f_encrypted')); } }; window.checkEncryption = function(elm) { String.prototype.repeat = function(num) { |
︙ | ︙ | |||
197 198 199 200 201 202 203 | { if (typeof GibberishAES == 'undefined') { alert("Le chargement de la bibliothèque AES n'est pas terminé.\nLe chiffrement est impossible pour le moment, recommencez dans quelques instants ou désactivez le chiffrement."); return false; } | | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | { if (typeof GibberishAES == 'undefined') { alert("Le chargement de la bibliothèque AES n'est pas terminé.\nLe chiffrement est impossible pour le moment, recommencez dans quelques instants ou désactivez le chiffrement."); return false; } var content = document.getElementById('f_content'); content.value = GibberishAES.enc(content.value, encryptPassword); content.readOnly = true; return true; }; } else { |
︙ | ︙ |
Added src/www/admin/static/scripts/wiki_editor.css version [30c3c98262].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | .textEditor { border-radius: .5em; background: #ccc; padding: 1%; overflow: hidden; position: relative; } .textEditor textarea { border: none; margin: 0; background: #eee; border-radius: .5em; height: 96%; width: 98%; } nav.te { margin-bottom: .5em; height: 30px; } nav.te button { text-decoration: none; cursor: pointer; background: #eee no-repeat center center; display: inline-block; vertical-align: bottom; transition: all .2s; border: 1px solid #999; box-shadow: 2px 2px 5px #999; } nav.te .bold, nav.te .italic, nav.te .title, nav.te .link { font-family: Georgia, "Times New Roman", serif; } nav.te .bold { font-weight: bold; } nav.te .italic { font-style: italic; } nav.te .link { text-decoration: underline; color: blue; } nav.te .fullscreen { text-indent: -70em; width: 32px; overflow: hidden; } nav.te .icnl { font-size: 18px; } nav.te .ext.icnl { width: 24px; line-height: 5px; overflow: hidden; } nav.te .file { margin-left: 2em; } nav.te .fullscreen { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEUAAABMTFFOTlBOTlCQ1uMHAAAAA3RSTlMAOcKBmOr4AAAAQUlEQVQI12P4/4D7P8N/B0Yo8XsC23+GZw+4pzM4TmBzYsAGgBKODNcecM9m+D+B7T3DPwfG/Qz/G5iABlzg+g8ANzMax/3kkQoAAAAASUVORK5CYII="); float: right; } nav.te .preview { margin-left: 2em; } .textEditor.fullscreen nav.te .fullscreen { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEUAAABNTVFOTlBOTlBLB/faAAAAA3RSTlMAOsPdsomtAAAAQElEQVQI12NIEWCRZNi5gTePIe8D/08G6Q/8Txj0P/B/YVj/gf8fAzawHyQhD1ICJvI/8O9k2FnAl8eQosAiCQCgixb13aKGIwAAAABJRU5ErkJggg=="); } .textEditor nav button.close { display: none; float: right; } .textEditor nav button.reload { display: none; float: left; } .textEditor.fullscreen { position: fixed; top: 0; left: 0; right: 0; bottom: 0; width: 98%; height: 98%; padding: 1%; border-radius: 0; z-index: 100000; } .textEditor.fullscreen textarea { height: 90%; } .textEditor.iframe textarea { display: none; } .textEditor.iframe nav button { display: none; } .textEditor.iframe nav button.close, .textEditor.iframe nav button.reload { display: inline-block; } .textEditor iframe { border: none; background: #eee; border-radius: .5em; padding: 1%; width: 98%; } .textEditor iframe.hidden { visibility: hidden; width: 0px; height: 0px; position: absolute; top: -1000px; } |
Added src/www/admin/static/scripts/wiki_editor.js version [888ea259ae].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | (function () { var wiki_id = window.location.search.match(/id=(\d+)/)[1]; g.onload(function () { g.style('scripts/wiki_editor.css'); g.script('scripts/text_editor.min.js', function () { var t = new textEditor('f_content'); t.parent = t.textarea.parentNode; var toolbar = document.createElement('nav'); toolbar.className = 'te'; var toggleFullscreen = function (e) { var classes = t.parent.className.split(' '); for (var i = 0; i < classes.length; i++) { if (classes[i] == 'fullscreen') { classes.splice(i, 1); t.parent.className = classes.join(' '); t.fullscreen = false; return true; } } classes.push('fullscreen'); t.parent.className = classes.join(' '); t.fullscreen = true; return true; }; var openPreview = function () { openIFrame(''); var form = document.createElement('form'); form.appendChild(t.textarea.cloneNode(true)); form.firstChild.value = t.textarea.value; form.target = 'editorFrame'; form.action = g.admin_url + 'wiki/_preview.php?id=' + wiki_id; form.style.display = 'none'; form.method = 'post'; document.body.appendChild(form); form.submit(); //document.body.removeChild(form); }; var openSyntaxHelp = function () { openIFrame(g.admin_url + 'wiki/_syntaxe.html'); }; var openFileInsert = function () { openIFrame(g.admin_url + 'wiki/_fichiers.php?page=' + wiki_id); }; window.te_insertFile = function (file) { var tag = '<<fichier|'+file+'>>'; t.insertAtPosition(t.getSelection().start, tag); closeIFrame(); }; window.te_insertImage = function (file, position, caption) { var tag = '<<image|' + file; if (position) tag += '|' + position; if (caption) tag += '|' + caption; tag += '>>'; t.insertAtPosition(t.getSelection().start, tag); closeIFrame(); }; var openIFrame = function(url) { if (t.iframe && t.iframe.src == t.base_url + url) { t.iframe.className = ''; t.parent.className += ' iframe'; return true; } else if (t.iframe) { t.parent.removeChild(t.iframe); t.iframe = null; } var w = t.textarea.offsetWidth, h = t.textarea.offsetHeight; var iframe = document.createElement('iframe'); iframe.width = w; iframe.height = h; iframe.src = url; iframe.name = 'editorFrame'; iframe.frameborder = '0'; iframe.scrolling = 'yes'; t.parent.appendChild(iframe); t.parent.className += ' iframe'; t.iframe = iframe; }; var closeIFrame = function () { t.parent.className = t.parent.className.replace(/ iframe$/, ''); t.iframe.className = 'hidden'; }; var appendButton = function (name, title, action, altTitle) { var btn = document.createElement('button'); btn.type = 'button'; btn.title = altTitle ? altTitle : title; if (title.length == 1) { btn.dataset.icon = title; } else { btn.innerText = title; } btn.className = 'icn-btn ' +name; btn.onclick = function () { action.call(); return false; }; toolbar.appendChild(btn); return btn; }; var wrapTags = function (left, right) { t.wrapSelection(t.getSelection(), left, right); return true; }; appendButton('title', "== Titre", function () { wrapTags("== ", ""); } ); appendButton('bold', '**gras**', function () { wrapTags('**', '**'); } ); appendButton('italic', "''italique''", function () { wrapTags("''", "''"); } ); appendButton('link', "[[lien|http://]]", function () { if (url = window.prompt('Adresse URL ?')) wrapTags("[[", "|" + url + ']]'); } ); appendButton('file', "📎", openFileInsert, 'Insérer fichier / image'); appendButton('ext preview', '👁', openPreview, 'Prévisualiser'); appendButton('ext help', '❓', openSyntaxHelp, 'Aide sur la syntaxe'); appendButton('ext fullscreen', 'Plein écran', toggleFullscreen, 'Plein écran'); appendButton('ext close', 'Fermer', closeIFrame); t.parent.insertBefore(toolbar, t.parent.firstChild); t.shortcuts.push({key: 'F11', callback: toggleFullscreen}); t.shortcuts.push({ctrl: true, key: 'b', callback: function () { return wrapTags('**', '**'); } }); t.shortcuts.push({ctrl: true, key: 'g', callback: function () { return wrapTags('**', '**'); } }); t.shortcuts.push({ctrl: true, key: 'i', callback: function () { return wrapTags("''", "''"); } }); if (window.location.hash.match(/fullscreen/)) { t.toggleFullscreen(); window.location.hash = ''; } }); }); }()); |
Modified src/www/admin/static/wiki.css from [c1552b949b] to [2c20ca7f50].
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 | background: #fff; padding: 1em; max-height: 90%; max-width: 90%; border-radius: .5em; cursor: e-resize; } fieldset.wikiText { border: none; padding: 0; } | > > > > > > > > > | < | < < < | > > | > | > > > > | < < < < < < < < < < < | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | background: #fff; padding: 1em; max-height: 90%; max-width: 90%; border-radius: .5em; cursor: e-resize; } .web-edit { display: grid; grid-template-columns: 1fr 30%; grid-template-rows: .3fr .3fr .3fr; grid-gap: 2em; } fieldset.wikiText { grid-column: 1; grid-row: 1/3; border: none; padding: 0; } .web-edit input[type=text] { min-width: 0; } @media screen and (max-width: 980px) { .web-edit { display: block; } } #encryptPasswordDisplay { cursor: help; background: #ddd; } fieldset.wikiEncrypt, fieldset.wikiMain, .web-edit p.submit { grid-column: 2; } .web-edit dd.help { font-size: .9em; } fieldset.wikiMain #f_title { width: 90%; font-size: 14pt; } fieldset.wikiMain #f_uri { width: 90%; } .wikiFiles { text-align: center; } .wikiFooter, .wikiFiles { font-size: 0.8em; |
︙ | ︙ | |||
267 268 269 270 271 272 273 | } .wikiResults p { margin-bottom: .8em; font-size: .9em; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | } .wikiResults p { margin-bottom: .8em; font-size: .9em; } form#f_upload fieldset { position: relative; } form .fileUpload .uploadHelper_progress { position: absolute; bottom: 1em; |
︙ | ︙ | |||
442 443 444 445 446 447 448 | } form#insertImage .align input:hover, form#insertImage .cancel input:hover { cursor: pointer; background-color: #eee; color: darkred; } | > > > > > > > > | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | } form#insertImage .align input:hover, form#insertImage .cancel input:hover { cursor: pointer; background-color: #eee; color: darkred; } #confirm_saved { position: absolute; top: .5em; right: 4em; text-align: center; transition: all .5s, opacity 3s; } |
Name change from src/www/admin/wiki/_fichiers.php to src/www/admin/web/_attach.php.
︙ | ︙ |
Name change from src/www/admin/wiki/_preview.php to src/www/admin/web/_preview.php.
︙ | ︙ |
Name change from src/www/admin/wiki/_chercher_parent.php to src/www/admin/web/_selector.php.
︙ | ︙ |
Name change from src/www/admin/wiki/_syntaxe.html to src/www/admin/web/_syntaxe.html.
︙ | ︙ |
Name change from src/www/admin/wiki/supprimer.php to src/www/admin/web/delete.php.
︙ | ︙ |
Modified src/www/admin/web/edit.php from [4a4a219729] to [53c2dc299d].
︙ | ︙ | |||
11 12 13 14 15 16 17 18 | $page = Web::get($id); if (!$page) { throw new UserException('Page inconnue'); } $csrf_key = 'edit_' . $page->id(); | > > > > > | | > > > | < | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | $page = Web::get($id); if (!$page) { throw new UserException('Page inconnue'); } $csrf_key = 'edit_' . $page->id(); $editing_started = f('editing_started') ?: date('Y-m-d H:i:s'); if (f('cancel')) { Utils::redirect(ADMIN_URL . 'web/?parent=' . $page->parent_id); } $form->runIf('save', function () use ($page) { $page->importForm(); $page->save(); }, $csrf_key, Utils::getSelfURI() . '#saved'); $parent = $page->parent_id ? [$page->parent_id => Web::get($page->parent_id)->title] : null; $encrypted = f('encrypted') || $page->file()->type == Page::FILE_TYPE_ENCRYPTED; $tpl->assign(compact('page', 'parent', 'editing_started', 'encrypted', 'csrf_key')); $tpl->assign('custom_js', ['wiki_editor.js', 'wiki-encryption.js']); $tpl->display('web/edit.tpl'); |
Name change from src/www/admin/wiki/index.php to src/www/admin/web/page.php.
︙ | ︙ |
Name change from src/www/admin/wiki/chercher.php to src/www/admin/web/search.php.
︙ | ︙ |
Deleted src/www/admin/wiki/_inc.php version [7e46f0d30d].
|
| < < < < < < < < < < < < |
Deleted src/www/admin/wiki/creer.php version [fcf6294705].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/www/admin/wiki/editer.php version [85c2033cca].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/www/admin/wiki/historique.php version [0c8cd47a0e].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/www/admin/wiki/recent.php version [8a00987ce2].
|
| < < < < < < < < < < < < < < |