Comment: | Use INI file instead of JSON for Module metadata (easier to use), allow to add home button and menu item without any code |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA3-256: |
366dbe898749b028e402153dd7cbdc5f |
User & Date: | bohwaz on 2023-02-14 15:02:47 |
Other Links: | branch diff | manifest | tags |
2023-02-14
| ||
22:29 | Refactor plugins to have the same UI for plugins and modules, also modernize code of plugins management check-in: a8251477ac user: bohwaz tags: dev | |
15:02 | Use INI file instead of JSON for Module metadata (easier to use), allow to add home button and menu item without any code check-in: 366dbe8987 user: bohwaz tags: dev | |
14:03 | Make sure we don't request from database in install, and delete if database is created by mistake check-in: e2a27f4268 user: bohwaz tags: dev | |
Modified src/include/lib/Garradin/Entities/Module.php from [adac7a6d80] to [3f935b6b38].
︙ | ︙ | |||
12 13 14 15 16 17 18 | use const Garradin\{ROOT, WWW_URL}; class Module extends Entity { const ROOT = File::CONTEXT_SKELETON . '/modules'; const DIST_ROOT = ROOT . '/skel-dist/modules'; | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | use const Garradin\{ROOT, WWW_URL}; class Module extends Entity { const ROOT = File::CONTEXT_SKELETON . '/modules'; const DIST_ROOT = ROOT . '/skel-dist/modules'; const META_FILE = 'module.ini'; const CONFIG_TEMPLATE = 'config.html'; // Snippets, don't forget to create alias constant in UserTemplate\Modules class const SNIPPET_TRANSACTION = 'snippets/transaction_details.html'; const SNIPPET_USER = 'snippets/user_details.html'; const SNIPPET_HOME_BUTTON = 'snippets/home_button.html'; |
︙ | ︙ | |||
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | /** * Directory name */ protected string $name; protected string $label; protected ?string $description; protected ?\stdClass $config; protected bool $enabled; public function selfCheck(): void { $this->assert(preg_match('/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/', $this->name), 'Nom unique de module invalide: ' . $this->name); $this->assert(trim($this->label) !== '', 'Le libellé ne peut rester vide'); } /** | > > > > > > | | | | > | > > | > > > | | | > > > > > > | 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 | /** * Directory name */ protected string $name; protected string $label; protected ?string $description; protected ?string $author; protected ?string $url; protected ?string $restrict_section; protected ?int $restrict_level; protected bool $home_button; protected bool $menu; protected ?\stdClass $config; protected bool $enabled; public function selfCheck(): void { $this->assert(preg_match('/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/', $this->name), 'Nom unique de module invalide: ' . $this->name); $this->assert(trim($this->label) !== '', 'Le libellé ne peut rester vide'); } /** * Fills information from module.ini file */ public function updateFromINI(bool $use_local = true): bool { if ($use_local && ($file = Files::get($this->path(self::META_FILE)))) { $ini = $file->fetch(); } elseif (file_exists($this->distPath(self::META_FILE))) { $ini = file_get_contents($this->distPath(self::META_FILE)); } else { return false; } $ini = @parse_ini_string($ini, false, \INI_SCANNER_TYPED); if (empty($ini)) { return false; } $ini = (object) $ini; if (!isset($ini->name)) { return false; } $this->set('label', $ini->name); $this->set('description', $ini->description ?? null); $this->set('author', $ini->author ?? null); $this->set('url', $ini->url ?? null); $this->set('home_button', !empty($ini->home_button)); $this->set('menu', !empty($ini->menu)); $this->set('restrict_section', $ini->restrict_section ?? null); $this->set('restrict_level', isset($ini->restrict_section, $ini->restrict_level, Session::ACCESS_WORDS[$ini->restrict_level]) ? Session::ACCESS_WORDS[$ini->restrict_level] : null); return true; } public function updateTemplates(): void { $check = self::SNIPPETS + [self::CONFIG_TEMPLATE => 'Config']; |
︙ | ︙ |
Modified src/include/lib/Garradin/UserTemplate/Modules.php from [968f6c135b] to [f759d31bbe].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php namespace Garradin\UserTemplate; use Garradin\Entities\Module; use Garradin\Files\Files; use Garradin\DB; use Garradin\Utils; use Garradin\UserException; use const Garradin\ROOT; use \KD2\DB\EntityManager as EM; class Modules { | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php namespace Garradin\UserTemplate; use Garradin\Entities\Module; use Garradin\Files\Files; use Garradin\DB; use Garradin\Utils; use Garradin\UserException; use Garradin\Users\Session; use const Garradin\ROOT; use \KD2\DB\EntityManager as EM; class Modules { |
︙ | ︙ | |||
38 39 40 41 42 43 44 | foreach ($delete as $name) { self::get($name)->delete(); } foreach ($existing as $name) { $f = self::get($name); | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | foreach ($delete as $name) { self::get($name)->delete(); } foreach ($existing as $name) { $f = self::get($name); $f->updateFromINI(); $f->save(); $f->updateTemplates(); } } /** * List modules names from locally installed directories |
︙ | ︙ | |||
89 90 91 92 93 94 95 | $list = self::listRaw(false); $out = []; foreach ($list as $name) { $m = new Module; $m->name = $name; | | | | 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 | $list = self::listRaw(false); $out = []; foreach ($list as $name) { $m = new Module; $m->name = $name; if (!$m->updateFromINI(false)) { continue; } $out[$name] = $m; } return $out; } static public function create(string $name): ?Module { $module = new Module; $module->name = $name; if (!$module->updateFromINI()) { return null; } $module->save(); $module->updateTemplates(); return $module; } |
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | static public function listForSnippet(string $snippet): array { return EM::getInstance(Module::class)->all('SELECT f.* FROM @TABLE f INNER JOIN modules_templates t ON t.id_module = f.id WHERE t.name = ? AND f.enabled = 1 ORDER BY f.label COLLATE NOCASE ASC;', $snippet); } static public function get(string $name): ?Module { return EM::findOne(Module::class, 'SELECT * FROM @TABLE WHERE name = ?;', $name); } static public function isEnabled(string $name): bool { return (bool) EM::getInstance(Module::class)->col('SELECT 1 FROM @TABLE WHERE name = ? AND enabled = 1;', $name); } } | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 177 178 179 180 181 182 183 184 185 186 187 | static public function listForSnippet(string $snippet): array { return EM::getInstance(Module::class)->all('SELECT f.* FROM @TABLE f INNER JOIN modules_templates t ON t.id_module = f.id WHERE t.name = ? AND f.enabled = 1 ORDER BY f.label COLLATE NOCASE ASC;', $snippet); } static public function listModulesAndPluginsMenu(): array { $list = []; $session = Session::getInstance(); foreach (DB::getInstance()->get('SELECT name, label, restrict_section, restrict_level FROM modules WHERE menu = 1;') as $m) { if (!$session->canAccess($m->restrict_section, $m->restrict_level)) { continue; } $list[$m->name] = sprintf('<a href="%sm/%s">%s</a>', ADMIN_URL, $m->name, $m->label); } foreach (DB::getInstance()->get('SELECT id, label, restrict_section, restrict_level FROM plugins WHERE menu = 1;') as $p) { if (!$session->canAccess($p->restrict_section, $p->restrict_level)) { continue; } $list[$m->name] = sprintf('<a href="%sp/%s">%s</a>', ADMIN_URL, $p->name, $p->label); } ksort($list); return $list; } static public function get(string $name): ?Module { return EM::findOne(Module::class, 'SELECT * FROM @TABLE WHERE name = ?;', $name); } static public function isEnabled(string $name): bool { return (bool) EM::getInstance(Module::class)->col('SELECT 1 FROM @TABLE WHERE name = ? AND enabled = 1;', $name); } } |
Modified src/include/lib/Garradin/Users/Session.php from [4d9aa4f9ca] to [4b67ea0f8f].
︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | const SECTION_CONFIG = 'config'; const SECTION_SUBSCRIBE = 'subscribe'; const ACCESS_NONE = 0; const ACCESS_READ = 1; const ACCESS_WRITE = 2; const ACCESS_ADMIN = 9; // Personalisation de la config de UserSession protected bool $non_locking = true; protected $cookie_name = 'pko'; protected $remember_me_cookie_name = 'pkop'; protected $remember_me_expiry = '+3 months'; | > > > > > > > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | const SECTION_CONFIG = 'config'; const SECTION_SUBSCRIBE = 'subscribe'; const ACCESS_NONE = 0; const ACCESS_READ = 1; const ACCESS_WRITE = 2; const ACCESS_ADMIN = 9; const ACCESS_WORDS = [ 'none' => self::ACCESS_NONE, 'read' => self::ACCESS_READ, 'write' => self::ACCESS_WRITE, 'admin' => self::ACCESS_ADMIN, ]; // Personalisation de la config de UserSession protected bool $non_locking = true; protected $cookie_name = 'pko'; protected $remember_me_cookie_name = 'pkop'; protected $remember_me_expiry = '+3 months'; |
︙ | ︙ |
Modified src/include/migrations/1.3/1.3.0.sql from [799f3d7106] to [8a8da01d86].
︙ | ︙ | |||
52 53 54 55 56 57 58 | DROP TABLE services_users_old; -- Remove old plugin as it cannot be uninstalled as it no longer exists DELETE FROM plugins_old WHERE nom = 'ouvertures'; DELETE FROM plugins_signaux_old WHERE plugin = 'ouvertures'; -- Rename plugins table columns to English | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | DROP TABLE services_users_old; -- Remove old plugin as it cannot be uninstalled as it no longer exists DELETE FROM plugins_old WHERE nom = 'ouvertures'; DELETE FROM plugins_signaux_old WHERE plugin = 'ouvertures'; -- Rename plugins table columns to English INSERT INTO plugins (id, label, description, author, url, version, config, menu) SELECT id, nom, description, auteur, url, version, config, 0 FROM plugins_old; INSERT INTO plugins_signals SELECT * FROM plugins_signaux_old; DROP TABLE plugins_signaux_old; DROP TABLE plugins_old; INSERT INTO searches SELECT * FROM recherches; UPDATE searches SET target = 'accounting' WHERE target = 'compta'; |
︙ | ︙ |
Modified src/include/migrations/1.3/schema.sql from [5d55bc7389] to [aef26e336a].
︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 | ( id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL, description TEXT NULL, author TEXT NULL, url TEXT NULL, version TEXT NOT NULL, config TEXT NULL ); CREATE TABLE IF NOT EXISTS plugins_signals -- Link between plugins and signals ( signal TEXT NOT NULL, | > > > > | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | ( id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL, description TEXT NULL, author TEXT NULL, url TEXT NULL, version TEXT NOT NULL, menu INT NOT NULL DEFAULT 0, home_button INT NOT NULL DEFAULT 0, restrict_section TEXT NULL, restrict_level INT NULL, config TEXT NULL ); CREATE TABLE IF NOT EXISTS plugins_signals -- Link between plugins and signals ( signal TEXT NOT NULL, |
︙ | ︙ | |||
494 495 496 497 498 499 500 501 502 503 504 505 506 507 | CREATE TABLE IF NOT EXISTS modules -- List of modules ( id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, label TEXT NOT NULL, description TEXT NULL, config TEXT NULL, enabled INTEGER NOT NULL DEFAULT 0 ); CREATE UNIQUE INDEX IF NOT EXISTS modules_name ON modules (name); CREATE TABLE IF NOT EXISTS modules_templates | > > > > > > | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | CREATE TABLE IF NOT EXISTS modules -- List of modules ( id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, label TEXT NOT NULL, description TEXT NULL, author TEXT NULL, url TEXT NULL, menu INT NOT NULL DEFAULT 0, home_button INT NOT NULL DEFAULT 0, restrict_section TEXT NULL, restrict_level INT NULL, config TEXT NULL, enabled INTEGER NOT NULL DEFAULT 0 ); CREATE UNIQUE INDEX IF NOT EXISTS modules_name ON modules (name); CREATE TABLE IF NOT EXISTS modules_templates |
︙ | ︙ |
Added src/skel-dist/modules/bilan_pc/module.ini version [294e350d2a].
> > > > | 1 2 3 4 | name="Bilan expert" description="Bilan annuel selon le modèle du plan comptable des associations 2020" author="Paheko" url="https://paheko.cloud/" |
Deleted src/skel-dist/modules/bilan_pc/module.json version [8f119bbb23].
|
| < < < < |
Added src/skel-dist/modules/carte_membre/module.ini version [e83754f068].
> > > > | 1 2 3 4 | name="Carte de membre" description="Carte de membre, imprimable par membre ou en planche de plusieurs membres" author="Paheko" url="https://paheko.cloud/" |
Deleted src/skel-dist/modules/carte_membre/module.json version [3b6ce77fe3].
|
| < < < < |
Modified src/skel-dist/modules/invoice/module.ini from [2780326c4d] to [5bb5a68af8].
|
| < | | < > > | 1 2 3 4 | name="Devis et factures" description="Permet de créer des devis et des factures, et de les imprimer" author="Paheko" url="https://paheko.cloud/" |
Modified src/skel-dist/modules/ouvertures/module.ini from [80628e2296] to [6324cdc425].
|
| < | | < > > | 1 2 3 4 | name="Horaires d'ouverture" description="Permet d'afficher sur la page d'accueil les jours et horaires d'ouverture" author="Paheko" url="https://paheko.cloud/" |
Modified src/skel-dist/modules/recu_don/module.ini from [cd920ccb31] to [ad2de6e4c1].
|
| < | | < > > | 1 2 3 4 | label="Reçu de don" description="Reçu de don simple, sans valeur fiscale" author="Paheko" url="https://paheko.cloud/" |
Modified src/skel-dist/modules/recu_paiement/module.ini from [972c67d830] to [20efb04279].
|
| < | | < > > | 1 2 3 4 | name="Reçu de paiement" description="Reçu de paiement, pour les écritures liées à un membre" author="Paheko" url="https://paheko.cloud/" |
Modified src/skel-dist/modules/recus_fiscaux/module.ini from [4a0b870aaa] to [5d6ae17b31].
|
| < | | < > > | 1 2 3 4 | name="Reçus fiscaux" description="Permet de générer des reçus fiscaux. Conforme aux exigences fiscales de 2022. Seuls les membres ayant accès à la comptabilité auront accès à ce module." author="Paheko" url="https://paheko.cloud/" |
Deleted src/skel-dist/modules/recus_fiscaux/snippets/home_button.html version [99f1ef488a].
|
| < < < |
Modified src/skel-dist/modules/remise_cheques/module.ini from [a93b07bbd2] to [6dd2247e01].
|
| < | | < > > > > > | 1 2 3 4 5 6 7 | name="Bordereau de remise de chèques" description="Permet d'imprimer un bordereau de remise de chèques à partir d'une écriture de dépôt." author="Paheko" url="https://paheko.cloud/" home_button=true restrict_section="accounting" restrict_level="read" |