Comment: | New file storage is working |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA1: |
0e2782726924e82d51fefbe886440a0b |
User & Date: | bohwaz on 2020-12-13 15:34:35 |
Other Links: | branch diff | manifest | tags |
2020-12-13
| ||
16:11 | Reflect changes in config check-in: c83f057f54 user: bohwaz tags: dev | |
15:34 | New file storage is working check-in: 0e27827269 user: bohwaz tags: dev | |
00:58 | Progress on the rendering side of files check-in: 049b3cf40d user: bohwaz tags: dev | |
Modified src/include/data/1.1.0_migration.sql from [b636c3139c] to [0607d500c4].
1 2 3 4 5 6 7 8 9 10 11 | ALTER TABLE membres_categories RENAME TO membres_categories_old; .read 1.1.0_schema.sql INSERT INTO membres_categories SELECT id, nom, droit_wiki, -- droit_web droit_wiki, -- droit_documents droit_membres, droit_compta, droit_inscription, | > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ALTER TABLE membres_categories RENAME TO membres_categories_old; .read 1.1.0_schema.sql INSERT INTO files_contents (id, hash, content) SELECT id, hash, contenu FROM fichiers_contenu; INSERT INTO files (id, hash, folder_id, name, type, created, content_id, author_id, public) SELECT f.id, c.hash, NULL, nom, type, datetime, c.id, NULL, 0 FROM fichiers f INNER JOIN fichiers_contenu c ON c.id = f.id_contenu; INSERT INTO membres_categories SELECT id, nom, droit_wiki, -- droit_web droit_wiki, -- droit_documents droit_membres, droit_compta, droit_inscription, |
︙ | ︙ | |||
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | INSERT INTO files_links (id, transaction_id) SELECT fichier, id FROM fichiers_acc_transactions; INSERT INTO files_links (id, config) SELECT valeur, cle FROM config WHERE cle = 'image_fond' AND valeur > 0; INSERT INTO files (hash, folder_id, name, type, created, content_id, author_id, public) SELECT hash, NULL, name, type, created, content_id, author_id, 0 FROM files WHERE id = (SELECT new_id FROM wiki_as_files WHERE uri = (SELECT valeur FROM config WHERE cle = 'accueil_connexion')); UPDATE config SET valeur = (SELECT id FROM files WHERE name = 'Accueil_connexion.skriv') WHERE cle = 'accueil_connexion'; UPDATE config SET cle = 'admin_homepage' WHERE cle = 'accueil_connexion'; DELETE FROM config WHERE cle = 'accueil_wiki'; INSERT INTO config (cle, valeur) VALUES ('telephone_asso', NULL); | > > < > > > | 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 | INSERT INTO files_links (id, transaction_id) SELECT fichier, id FROM fichiers_acc_transactions; INSERT INTO files_links (id, config) SELECT valeur, cle FROM config WHERE cle = 'image_fond' AND valeur > 0; UPDATE files SET public = 1 WHERE id = (SELECT valeur FROM config WHERE cle = 'image_fond'); INSERT INTO files (hash, folder_id, name, type, created, content_id, author_id, public) SELECT hash, NULL, name, type, created, content_id, author_id, 0 FROM files WHERE id = (SELECT new_id FROM wiki_as_files WHERE uri = (SELECT valeur FROM config WHERE cle = 'accueil_connexion')); UPDATE config SET valeur = (SELECT id FROM files WHERE name = 'Accueil_connexion.skriv') WHERE cle = 'accueil_connexion'; UPDATE config SET cle = 'admin_homepage' WHERE cle = 'accueil_connexion'; DELETE FROM config WHERE cle = 'accueil_wiki'; INSERT INTO config (cle, valeur) VALUES ('telephone_asso', NULL); DROP TRIGGER wiki_recherche_delete; DROP TRIGGER wiki_recherche_update; DROP TRIGGER wiki_recherche_contenu_insert; DROP TRIGGER wiki_recherche_contenu_chiffre; DROP TABLE wiki_recherche; DROP TABLE wiki_pages; DROP TABLE wiki_revisions; DROP TABLE fichiers_wiki_pages; DROP TABLE fichiers_acc_transactions; DROP TABLE fichiers_membres; DROP TABLE fichiers; DROP TABLE fichiers_contenu; |
Modified src/include/init.php from [e6355f8bdd] to [2c68efc0c5].
︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 175 | 'SMTP_PORT' => 587, 'SMTP_SECURITY' => 'STARTTLS', 'ADMIN_URL' => WWW_URL . 'admin/', 'NTP_SERVER' => 'fr.pool.ntp.org', 'ENABLE_AUTOMATIC_BACKUPS' => true, 'ADMIN_COLOR1' => '#9c4f15', 'ADMIN_COLOR2' => '#d98628', ]; foreach ($default_config as $const => $value) { $const = sprintf('Garradin\\%s', $const); if (!defined($const)) | > > > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | 'SMTP_PORT' => 587, 'SMTP_SECURITY' => 'STARTTLS', 'ADMIN_URL' => WWW_URL . 'admin/', 'NTP_SERVER' => 'fr.pool.ntp.org', 'ENABLE_AUTOMATIC_BACKUPS' => true, 'ADMIN_COLOR1' => '#9c4f15', 'ADMIN_COLOR2' => '#d98628', 'FILE_STORAGE_BACKEND' => null, 'FILE_STORAGE_CONFIG' => [], 'FILE_STORAGE_QUOTA' => null, ]; foreach ($default_config as $const => $value) { $const = sprintf('Garradin\\%s', $const); if (!defined($const)) |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Files/File.php from [e4f0a7a0d2] to [f144751b28].
1 2 3 4 5 6 7 8 9 | <?php namespace Garradin\Entities\Files; use KD2\Image; use Garradin\DB; use Garradin\Entity; use Garradin\UserException; | > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?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 { const TABLE = 'files'; protected $id; protected $folder_id; |
︙ | ︙ | |||
42 43 44 45 46 47 48 | 'storage' => '?string', 'storage_path' => '?string', 'created' => 'DateTime', 'author_id' => '?int', 'content_id' => '?int', ]; | < < | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 'storage' => '?string', 'storage_path' => '?string', 'created' => 'DateTime', 'author_id' => '?int', 'content_id' => '?int', ]; /** * Tailles de miniatures autorisées, pour ne pas avoir 500 fichiers générés avec 500 tailles différentes * @var array */ const ALLOWED_THUMB_SIZES = [200, 500, 1200]; // Link to another file (ie. image included in a HTML file) |
︙ | ︙ | |||
336 337 338 339 340 341 342 | } /** * Envoie le fichier au client HTTP */ public function serve(?Session $session = null): void { | | | | | | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | } /** * Envoie le fichier au client HTTP */ public function serve(?Session $session = null): void { if (!$this->checkReadAccess($session)) { header('HTTP/1.1 403 Forbidden', true, 403); throw new UserException('Vous n\'avez pas accès à ce fichier.'); return; } $path = Files::callStorage('getPath', $this); $content = null === $path ? Files::callStorage('fetch', $this) : null; $this->_serve($path, $content); } /** * Envoie une miniature à la taille indiquée au client HTTP */ public function serveThumbnail(?Session $session = null, ?int $width = null): void { if (!$this->checkReadAccess($session)) { header('HTTP/1.1 403 Forbidden', true, 403); throw new UserException('Accès interdit'); return; } if (!$this->image) { throw new UserException('Il n\'est pas possible de fournir une miniature pour un fichier qui n\'est pas une image.'); |
︙ | ︙ | |||
393 394 395 396 397 398 399 | } } catch (\RuntimeException $e) { throw new UserException('Impossible de créer la miniature'); } } | | | | | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | } } catch (\RuntimeException $e) { throw new UserException('Impossible de créer la miniature'); } } $this->_serve($path, null); } /** * Servir un fichier local en HTTP * @param string $path Chemin vers le fichier local * @param string $type Type MIME du fichier * @param string $name Nom du fichier avec extension * @param integer $size Taille du fichier en octets (facultatif) * @return boolean TRUE en cas de succès */ protected function _serve(?string $path, ?string $content): void { if ($this->public) { Utils::HTTPCache($this->hash, $this->created->getTimestamp()); } else { // Disable browser cache header('Pragma: private'); header('Expires: -1'); header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0'); } |
︙ | ︙ | |||
483 484 485 486 487 488 489 | elseif ($this->type == 'text/vnd.skriv.encrypted') { return \Garradin\Files\Render\EncryptedSkriv::render($this, $content); } throw new \LogicException('Unknown render type'); } | | | < < | | | < < | < < < < < < < < < < < < < < < | | | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | elseif ($this->type == 'text/vnd.skriv.encrypted') { return \Garradin\Files\Render\EncryptedSkriv::render($this, $content); } throw new \LogicException('Unknown render type'); } public function checkReadAccess(Session $session): bool { // Web and config files should be marked as public when not in draft if ($this->public) { return true; } $link = DB::getInstance()->first('SELECT * FROM files_links WHERE id = ?;', $this->id()); // If it's linked to a file, then we want to know what the parent file is linked to if ($link->{self::LINK_FILE}) { $link = DB::getInstance()->first('SELECT * FROM files_links WHERE id = ?;', $link->{LINK_FILE}); } if ($link->{self::LINK_TRANSACTION} && $session->canAccess(Session::SECTION_ACCOUNTING, Membres::DROIT_ACCES)) { return true; } // The user can access his own profile files else if ($link->{self::LINK_USER} && $link->{self::LINK_USER} == $session->getUser()->id) { return true; } // Only users able to manage users can see their profile files else if ($link->{self::LINK_USER} && $session->canAccess(Session::SECTION_USERS, Membres::DROIT_ECRITURE)) { return true; } return $session->canAccess(Session::SECTION_DOCUMENTS, Membres::DROIT_ACCES); } } |
Modified src/include/lib/Garradin/Files/Files.php from [c4da787241] to [f113720eda].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php namespace Garradin\Files; use Garradin\Static_Cache; use Garradin\DB; use Garradin\Entities\Files\File; use KD2\DB\EntityManager as EM; use const Garradin\FILE_STORAGE_BACKEND; class Files { static public function callStorage(string $function, ...$args) { $storage = FILE_STORAGE_BACKEND ?? 'SQLite'; | > > > > > > > > > > > > > | | 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 | <?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; 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[0]) : strlen($args[1]); if (($used + $size) >= $quota) { throw new \OutOfBoundsException('File quota has been exhausted'); } } $storage = FILE_STORAGE_BACKEND ?? 'SQLite'; $class_name = __NAMESPACE__ . '\\Storage\\' . $storage; return call_user_func_array([$class_name, $function], $args); } static public function migrateStorage(string $from, string $to): void { $res = EM::getInstance(File::class)->iterate('SELECT * FROM @TABLE;'); |
︙ | ︙ | |||
76 77 78 79 80 81 82 | } } static public function get(int $id): ?File { return EM::findOneById(File::class, $id); } | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } } static public function get(int $id): ?File { return EM::findOneById(File::class, $id); } static public function serveFromQueryString(): void { $id = isset($_GET['id']) ? $_GET['id'] : null; $filename = !empty($_GET['file']) ? $_GET['file'] : null; $size = null; if (empty($id)) { header('HTTP/1.1 404 Not Found', true, 404); throw new UserException('Fichier inconnu.'); } foreach ($_GET as $key => $value) { if (substr($key, -2) == 'px') { $size = (int)substr($key, 0, -2); break; } } $id = base_convert($id, 36, 10); $file = self::get((int) $id); if (!$file) { header('HTTP/1.1 404 Not Found', true, 404); throw new UserException('Ce fichier n\'existe pas.'); } $session = Session::getInstance(); if ($size) { $file->serveThumbnail($session, $size); } else { $file->serve($session); } } } |
Modified src/include/lib/Garradin/Files/Storage/FileSystem.php from [e05f9c9e78] to [302efe0368].
1 2 3 4 5 6 7 8 9 10 | <?php namespace Garradin\Files\Storage; use const Garradin\FILE_STORAGE_CONFIG; /** * This class provides storage in the file system * You need ton configure FILE_STORAGE_CONFIG to give a file path */ | > > | 1 2 3 4 5 6 7 8 9 10 11 12 | <?php namespace Garradin\Files\Storage; use Garradin\Entities\Files\File; use const Garradin\FILE_STORAGE_CONFIG; /** * This class provides storage in the file system * You need ton configure FILE_STORAGE_CONFIG to give a file path */ |
︙ | ︙ | |||
113 114 115 116 117 118 119 | } self::$_size = (int) $total; return self::$_size; } | < < < < < | 115 116 117 118 119 120 121 122 123 124 125 126 | } self::$_size = (int) $total; return self::$_size; } static public function getQuota(): int { return disk_total_space(self::_getRoot()); } } |
Deleted src/include/lib/Garradin/Files/Storage/FileSystemQuota.php version [66a57977b0].
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Modified src/include/lib/Garradin/Files/Storage/SQLite.php from [e6d2cf42a4] to [bb87054c4e].
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace Garradin\Files\Storage; 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 | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <?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 |
︙ | ︙ | |||
73 74 75 76 77 78 79 | } static public function move(File $old_file, File $new_file): bool { return true; } | | < < < < < | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | } 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(size) FROM files_contents;'); } static public function getQuota(): int { return disk_total_space(dirname(DB_FILE)); } } |
Modified src/include/lib/Garradin/Files/Storage/StorageInterface.php from [1b7b1b4fca] to [e0b5fefef7].
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace Garradin\Files\Storage; interface StorageInterface { static public function store(File $file, ?string $path, ?string $content): bool; /** * List files contained in a path, this must return an array of File instances * If this storage backend wants to leave the directory handling to Garradin, just return NULL. | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin\Files\Storage; use Garradin\Entities\Files\File; interface StorageInterface { static public function store(File $file, ?string $path, ?string $content): bool; /** * List files contained in a path, this must return an array of File instances * If this storage backend wants to leave the directory handling to Garradin, just return NULL. |
︙ | ︙ | |||
27 28 29 30 31 32 33 | static public function delete(File $file): bool; static public function move(File $old_file, File $new_file): bool; static public function getTotalSize(): int; | < < | 29 30 31 32 33 34 35 36 37 | static public function delete(File $file): bool; static public function move(File $old_file, File $new_file): bool; static public function getTotalSize(): int; static public function getQuota(): int; } |
Modified src/include/lib/Garradin/Membres/Session.php from [d8e75a6058] to [d8f65f6ef2].
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | use KD2\Security; use KD2\Security_OTP; use KD2\Graphics\QRCode; use KD2\HTTP; class Session extends \KD2\UserSession { // Personalisation de la config de UserSession protected $cookie_name = 'gdin'; protected $remember_me_cookie_name = 'gdinp'; protected $remember_me_expiry = '+3 months'; const MINIMUM_PASSWORD_LENGTH = 8; | > > > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | use KD2\Security; use KD2\Security_OTP; use KD2\Graphics\QRCode; use KD2\HTTP; class Session extends \KD2\UserSession { const SECTION_WEB = 'web'; const SECTION_DOCUMENTS = 'documents'; const SECTION_USERS = 'membres'; const SECTION_ACCOUNTING = 'compta'; // Personalisation de la config de UserSession protected $cookie_name = 'gdin'; protected $remember_me_cookie_name = 'gdinp'; protected $remember_me_expiry = '+3 months'; const MINIMUM_PASSWORD_LENGTH = 8; |
︙ | ︙ |
Modified src/include/lib/Garradin/Template.php from [b3f70be648] to [f94b6d765a].
︙ | ︙ | |||
92 93 94 95 96 97 98 | $this->register_modifier('date_short', function ($dt) { return Utils::date_fr($dt, 'd/m/Y'); }); $this->register_modifier('html_money', [$this, 'htmlMoney']); $this->register_modifier('money_currency', [$this, 'htmlMoneyCurrency']); | < < < < < < < < < < < < < | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | $this->register_modifier('date_short', function ($dt) { return Utils::date_fr($dt, 'd/m/Y'); }); $this->register_modifier('html_money', [$this, 'htmlMoney']); $this->register_modifier('money_currency', [$this, 'htmlMoneyCurrency']); } protected function htmlMoney($number, bool $hide_empty = true): string { if ($hide_empty && !$number) { return ''; } |
︙ | ︙ |
Modified src/www/file.php from [a75fe87ff3] to [596ad632c6].
1 2 3 4 | <?php namespace Garradin; | < | < < < < < < < | < | < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 | <?php namespace Garradin; use Garradin\Files\Files; require __DIR__ . '/_inc.php'; Files::serveFromQueryString(); |