Comment: | Merge avec trunk |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dev |
Files: | files | file ages | folders |
SHA1: |
eea005f85527f0cd5f4421929650c52b |
User & Date: | bohwaz on 2020-06-23 00:32:10 |
Other Links: | branch diff | manifest | tags |
2020-09-03
| ||
16:19 | Renommer payment_reference en reference, et ajout libellé par ligne pour les saisies avancées check-in: d6206cc5c2 user: bohwaz tags: dev | |
2020-06-23
| ||
00:32 | Merge avec trunk check-in: eea005f855 user: bohwaz tags: dev | |
00:21 | Afficher le lien vers l'historique des rappels dans tous les cas check-in: cba6b145a6 user: bohwaz tags: trunk, stable | |
2020-06-22
| ||
21:27 | Simplification des cibles check-in: a3184ce2f1 user: bohwaz tags: dev | |
Modified src/include/lib/Garradin/Compta/Import.php from [f263dfbb6d] to [f437eb905d].
︙ | ︙ | |||
86 87 88 89 90 91 92 | } $db = DB::getInstance(); $db->begin(); $cats = new Categories; $journal = new Journal; | | > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | } $db = DB::getInstance(); $db->begin(); $cats = new Categories; $journal = new Journal; $liste_cats = $db->getAssoc('SELECT type || intitule, id FROM compta_categories;'); // Liste des moyens sous la forme nom -> code $liste_moyens = array_flip($cats->listMoyensPaiement(true)); $liste_moyens = array_change_key_case($liste_moyens, \CASE_LOWER); // Liste associative des projets $liste_projets = $db->getAssoc('SELECT libelle, id FROM compta_projets;'); $col = function($column) use (&$row, &$columns) { if (!isset($columns[$column])) |
︙ | ︙ | |||
164 165 166 167 168 169 170 | continue; } $debit = $col('Compte de débit - numéro'); $credit = $col('Compte de crédit - numéro'); $cat = $col('Catégorie'); | | > > > > > > > > > > > > > > > > | | < > > > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | continue; } $debit = $col('Compte de débit - numéro'); $credit = $col('Compte de crédit - numéro'); $cat = $col('Catégorie'); $moyen = strtolower($col('Moyen de paiement')); $type = $col('Type de mouvement'); if ('Recette' === $type) { $type = 1; } elseif ('Dépense' === $type) { $type = -1; } else { $type = 0; } // Association du moyen de paiement par nom if ($moyen && array_key_exists($moyen, $liste_moyens)) { $moyen = $liste_moyens[$moyen]; } // Sinon on estime que c'est juste le code qui est fourni else { $moyen = substr(strtoupper($moyen), 0, 2); } // Vérification de l'existence du moyen de paiement // s'il n'est pas valide, on ne peut pas avoir de catégorie non plus if (!trim($moyen) || !in_array($moyen, $liste_moyens, true)) { $moyen = false; $cat = false; } if ($cat && array_key_exists($type . $cat, $liste_cats)) { $cat = $liste_cats[$type . $cat]; } else { $cat = $moyen = false; } $id_projet = null; if (!empty($col('Projet'))) { if (!array_key_exists($col('Projet'), $liste_projets)) { |
︙ | ︙ | |||
210 211 212 213 214 215 216 | 'id_projet' => $id_projet, ]; if ($cat) { $data['moyen_paiement'] = $moyen; $data['numero_cheque'] = $col('Numéro de chèque'); | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | 'id_projet' => $id_projet, ]; if ($cat) { $data['moyen_paiement'] = $moyen; $data['numero_cheque'] = $col('Numéro de chèque'); $data['id_categorie'] = $cat; } try { if (empty($id)) { $journal->add($data); } else { $journal->edit($id, $data); } } catch (UserException $e) { throw new UserException(sprintf('Ligne %s: %s', $line, $e->getMessage())); } } $db->commit(); fclose($fp); return true; } } |
Modified src/include/lib/Garradin/Membres.php from [c6e8b8584f] to [06d3eb225c].
︙ | ︙ | |||
46 47 48 49 50 51 52 53 54 55 56 57 58 59 | if (isset($data[$key])) { if ($config->type == 'datetime' && trim($data[$key]) !== '') { $dt = new \DateTime($data[$key]); $data[$key] = $dt->format('Y-m-d H:i'); } elseif ($config->type == 'tel') { $data[$key] = Utils::normalizePhoneNumber($data[$key]); } elseif ($config->type == 'country') { $data[$key] = strtoupper(substr($data[$key], 0, 2)); | > > > > > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | if (isset($data[$key])) { if ($config->type == 'datetime' && trim($data[$key]) !== '') { $dt = new \DateTime($data[$key]); $data[$key] = $dt->format('Y-m-d H:i'); } elseif ($config->type == 'date' && trim($data[$key]) !== '') { $dt = new \DateTime($data[$key]); $data[$key] = $dt->format('Y-m-d'); } elseif ($config->type == 'tel') { $data[$key] = Utils::normalizePhoneNumber($data[$key]); } elseif ($config->type == 'country') { $data[$key] = strtoupper(substr($data[$key], 0, 2)); |
︙ | ︙ | |||
141 142 143 144 145 146 147 | } } $this->_checkFields($data, true, $require_password); if (isset($data[$id]) && $db->test('membres', $id . ' = ? COLLATE NOCASE', $data[$id])) { | | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | } } $this->_checkFields($data, true, $require_password); if (isset($data[$id]) && $db->test('membres', $id . ' = ? COLLATE NOCASE', $data[$id])) { throw new UserException('La valeur du champ '.$id.' est déjà utilisée par un autre membre, or ce champ doit être unique à chaque membre.'); } if (isset($data['passe']) && trim($data['passe']) != '') { Session::checkPasswordValidity($data['passe']); $data['passe'] = Session::hashPassword($data['passe']); } |
︙ | ︙ | |||
180 181 182 183 184 185 186 | $this->_checkFields($data, $check_editable, false); $champ_id = $config->get('champ_identifiant'); if (!empty($data[$champ_id]) && $db->firstColumn('SELECT 1 FROM membres WHERE '.$champ_id.' = ? COLLATE NOCASE AND id != ? LIMIT 1;', $data[$champ_id], (int)$id)) { | | | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | $this->_checkFields($data, $check_editable, false); $champ_id = $config->get('champ_identifiant'); if (!empty($data[$champ_id]) && $db->firstColumn('SELECT 1 FROM membres WHERE '.$champ_id.' = ? COLLATE NOCASE AND id != ? LIMIT 1;', $data[$champ_id], (int)$id)) { throw new UserException('La valeur du champ '.$champ_id.' est déjà utilisée par un autre membre, or ce champ doit être unique à chaque membre.'); } if (isset($data['numero'])) { if (!preg_match('/^\d+$/', $data['numero'])) { throw new UserException('Le numéro de membre ne doit contenir que des chiffres.'); |
︙ | ︙ |
Modified src/include/lib/Garradin/Membres/Champs.php from [1e1a261c03] to [2d0b2b43df].
︙ | ︙ | |||
492 493 494 495 496 497 498 | if ($initial_setup) { $this->champs = $champs; return true; } | | | | | | | 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 523 524 525 526 527 528 529 530 531 532 533 | if ($initial_setup) { $this->champs = $champs; return true; } if (!property_exists($champs, 'email')) { throw new UserException('Le champ E-Mail ne peut être supprimé des fiches membres.'); } if (!property_exists($champs, 'passe')) { throw new UserException('Le champ Mot de passe ne peut être supprimé des fiches membres.'); } if (!property_exists($champs, 'numero')) { throw new UserException('Le champ numéro de membre ne peut être supprimé des fiches membres.'); } $config = Config::getInstance(); $identite = $config->get('champ_identite'); if ($identite != 'id' && !property_exists($champs, $identite)) { throw new UserException('Le champ '.$config->get('champ_identite') .' est défini comme identité des membres et ne peut donc être supprimé des fiches membres.'); } $identifiant = $config->get('champ_identifiant'); if ($identifiant != 'id' && !property_exists($champs, $identifiant)) { throw new UserException('Le champ '.$config->get('champ_identifiant') .' est défini comme identifiant à la connexion et ne peut donc être supprimé des fiches membres.'); } $this->champs = $champs; |
︙ | ︙ |
Modified src/include/lib/Garradin/Membres/Cotisations.php from [181b05f93d] to [0f169947fd].
︙ | ︙ | |||
285 286 287 288 289 290 291 | $desc = $desc ? 'DESC' : 'ASC'; // Renvoyer la liste avec tous les membres des catégories dont la cotisation obligatoire est celle-ci if ($include_category) { $cats_obligatoires = $db->getAssoc('SELECT id, id FROM membres_categories WHERE id_cotisation_obligatoire = ? AND cacher = 0;', $id); | | | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | $desc = $desc ? 'DESC' : 'ASC'; // Renvoyer la liste avec tous les membres des catégories dont la cotisation obligatoire est celle-ci if ($include_category) { $cats_obligatoires = $db->getAssoc('SELECT id, id FROM membres_categories WHERE id_cotisation_obligatoire = ? AND cacher = 0;', $id); return $db->get('SELECT m.id AS id_membre, MAX(cm.date) AS date, cm.id, m.numero, m.'.$champ_id.' AS nom, c.montant, CASE WHEN cm.id IS NULL THEN 0 WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date() WHEN c.fin IS NOT NULL THEN (cm.date <= c.fin AND cm.date >= c.debut) ELSE 1 END AS a_jour FROM membres AS m LEFT JOIN cotisations_membres AS cm ON cm.id_membre = m.id AND cm.id_cotisation = ? LEFT JOIN cotisations AS c ON c.id = cm.id_cotisation WHERE '.$db->where('m.id_categorie', $cats_obligatoires) . ' GROUP BY m.id ORDER BY '.$order.' '.$desc.' LIMIT ?,?;', $id, $begin, self::ITEMS_PER_PAGE); } return $db->get('SELECT cm.id_membre, MAX(cm.date) AS date, cm.id, m.numero, m.'.$champ_id.' AS nom, c.montant, CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date() WHEN c.fin IS NOT NULL THEN (cm.date <= c.fin AND cm.date >= c.debut) ELSE 1 END AS a_jour FROM cotisations_membres AS cm INNER JOIN cotisations AS c ON c.id = cm.id_cotisation INNER JOIN membres AS m ON m.id = cm.id_membre |
︙ | ︙ |
Modified src/include/lib/Garradin/Template.php from [b681c34e8f] to [e69b34a2ee].
︙ | ︙ | |||
433 434 435 436 437 438 439 440 441 442 443 444 445 446 | $attributes .= 'disabled="disabled" '; } if (!empty($config->mandatory)) { $attributes .= 'required="required" '; } if (!empty($params['user_mode']) && empty($config->editable)) { $out = '<dt>' . htmlspecialchars($config->title, ENT_QUOTES, 'UTF-8') . '</dt>'; $out .= '<dd>' . (trim($value) === '' ? 'Non renseigné' : $this->displayChampMembre($value, $config)) . '</dd>'; return $out; } | > > | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | $attributes .= 'disabled="disabled" '; } if (!empty($config->mandatory)) { $attributes .= 'required="required" '; } $attributes .= 'autocomplete="off" '; if (!empty($params['user_mode']) && empty($config->editable)) { $out = '<dt>' . htmlspecialchars($config->title, ENT_QUOTES, 'UTF-8') . '</dt>'; $out .= '<dd>' . (trim($value) === '' ? 'Non renseigné' : $this->displayChampMembre($value, $config)) . '</dd>'; return $out; } |
︙ | ︙ |
Modified src/templates/admin/compta/banques/rapprocher.tpl from [8d52e0242d] to [77a239871a].
︙ | ︙ | |||
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 | <col width="3%" /> <col width="3%" /> <col width="3%" /> <col width="12%" /> <col width="10%" /> <col width="12%" /> <col /> </colgroup> <thead> <tr> <td class="check"><input type="checkbox" title="Tout cocher / décocher" /></td> <td></td> <td></td> <td>Date</td> <td>Montant</td> <td>Solde cumulé</td> <th>Libellé</th> </tr> </thead> <tbody> <tr> <td colspan="5"></td> <td>{$solde_initial|escape|html_money} {$config.monnaie}</td> <th>Solde au {$debut|format_sqlite_date_to_french}</th> </tr> {foreach from=$journal item="ligne"} <tr> <td class="check"><input type="checkbox" name="rapprocher[{$ligne.id}]" value="1" {if $ligne.date_rapprochement}checked="checked"{/if} /></td> <td class="num"><a href="{$admin_url}compta/operations/voir.php?id={$ligne.id}">{$ligne.id}</a></td> <td class="actions"> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <a class="icn" href="{$admin_url}compta/operations/modifier.php?id={$ligne.id}" title="Modifier cette opération">✎</a> {/if} </td> <td>{$ligne.date|date_fr:'d/m/Y'}</td> <td>{if $ligne.compte_credit == $compte.id}-{else}+{/if}{$ligne.montant|escape|html_money}</td> <td>{$ligne.solde|escape|html_money}</td> <th>{$ligne.libelle}</th> </tr> {/foreach} </tbody> <tfoot> <tr> <td colspan="5"></td> <td>{$solde_final|escape|html_money} {$config.monnaie}</td> <th>Solde au {$fin|format_sqlite_date_to_french}</th> </tr> </tfoot> </table> <p class="submit"> {csrf_field key="compta_rapprocher_%s"|args:$compte.id} <input type="submit" name="save" value="Enregistrer" /> <input type="submit" name="save_next" value="Enregistrer et aller au mois suivant →" class="minor" /> </p> </form> {include file="admin/_foot.tpl"} | > > > > > > > > | 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 | <col width="3%" /> <col width="3%" /> <col width="3%" /> <col width="12%" /> <col width="10%" /> <col width="12%" /> <col /> <col width="5%" /> <col width="3%" /> </colgroup> <thead> <tr> <td class="check"><input type="checkbox" title="Tout cocher / décocher" /></td> <td></td> <td></td> <td>Date</td> <td>Montant</td> <td>Solde cumulé</td> <th>Libellé</th> <th>Numéro pièce</th> <th>Numéro chèque</th> </tr> </thead> <tbody> <tr> <td colspan="5"></td> <td>{$solde_initial|escape|html_money} {$config.monnaie}</td> <th>Solde au {$debut|format_sqlite_date_to_french}</th> <td colspan="2"></td> </tr> {foreach from=$journal item="ligne"} <tr> <td class="check"><input type="checkbox" name="rapprocher[{$ligne.id}]" value="1" {if $ligne.date_rapprochement}checked="checked"{/if} /></td> <td class="num"><a href="{$admin_url}compta/operations/voir.php?id={$ligne.id}">{$ligne.id}</a></td> <td class="actions"> {if $session->canAccess('compta', Membres::DROIT_ADMIN)} <a class="icn" href="{$admin_url}compta/operations/modifier.php?id={$ligne.id}" title="Modifier cette opération">✎</a> {/if} </td> <td>{$ligne.date|date_fr:'d/m/Y'}</td> <td>{if $ligne.compte_credit == $compte.id}-{else}+{/if}{$ligne.montant|escape|html_money}</td> <td>{$ligne.solde|escape|html_money}</td> <th>{$ligne.libelle}</th> <td>{$ligne.numero_piece}</td> <td>{$ligne.numero_cheque}</td> </tr> {/foreach} </tbody> <tfoot> <tr> <td colspan="5"></td> <td>{$solde_final|escape|html_money} {$config.monnaie}</td> <th>Solde au {$fin|format_sqlite_date_to_french}</th> <td colspan="2"></td> </tr> </tfoot> </table> <p class="submit"> {csrf_field key="compta_rapprocher_%s"|args:$compte.id} <input type="submit" name="save" value="Enregistrer" /> <input type="submit" name="save_next" value="Enregistrer et aller au mois suivant →" class="minor" /> </p> </form> {include file="admin/_foot.tpl"} |
Modified src/templates/admin/compta/import.tpl from [d8c04dcc34] to [5bf6707f89].
︙ | ︙ | |||
27 28 29 30 31 32 33 | <label for="f_type">Export CSV de Garradin</label> </dd> <dd class="help"> Export du journal comptable au format CSV provenant de Garradin. Les lignes comportant un numéro d'opération mettront à jour les opérations existantes, les lignes sans numéro créeront de nouvelles opérations. </dd> | < < < < < < < < < < < | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <label for="f_type">Export CSV de Garradin</label> </dd> <dd class="help"> Export du journal comptable au format CSV provenant de Garradin. Les lignes comportant un numéro d'opération mettront à jour les opérations existantes, les lignes sans numéro créeront de nouvelles opérations. </dd> </dl> </fieldset> <p class="alert"> Si le fichier comporte des opérations dont la date est en dehors de l'exercice courant, elles seront ignorées. </p> |
︙ | ︙ |
Modified src/templates/admin/config/membres.tpl from [6c8773201e] to [b07fc3c940].
1 2 3 4 5 6 7 8 9 | {include file="admin/_head.tpl" current="config" js=1} {include file="admin/config/_menu.tpl" current="fiches_membres"} {if isset($status) && $status == 'OK'} <p class="confirm"> La configuration a bien été enregistrée. </p> {elseif isset($status) && $status == 'ADDED'} | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | {include file="admin/_head.tpl" current="config" js=1} {include file="admin/config/_menu.tpl" current="fiches_membres"} {if isset($status) && $status == 'OK'} <p class="confirm"> La configuration a bien été enregistrée. </p> {elseif isset($status) && $status == 'ADDED'} <p class="alert"> Le champ a été ajouté à la fin de la liste. Pour sauvegarder les modifications de la fiche membre cliquer sur le bouton « Enregistrer » en base de page. </p> {/if} {form_errors} {if $review} <p class="help"> |
︙ | ︙ |
Modified src/templates/admin/install.tpl from [2088579102] to [dd863dea3a].
︙ | ︙ | |||
40 41 42 43 44 45 46 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> <dd><input type="password" name="passe" id="f_passe_membre" value="{form_field name=passe}" pattern="{$password_pattern}" required="required" autocomplete="off" /></dd> <dt><label for="f_repasse_membre">Encore le mot de passe</label> (vérification) <b title="(Champ obligatoire)">obligatoire</b></dt> <dd><input type="password" name="passe_confirmed" id="f_repasse_membre" value="{form_field name=passe_confirmed}" pattern="{$password_pattern}" required="required" autocomplete="off" /></dd> </dl> </fieldset> <p class="submit"> {csrf_field key="install"} <input type="submit" id="f_submit" name="save" value="Terminer l'installation →" /> </p> |
︙ | ︙ |
Modified src/templates/admin/login.tpl from [51528da26b] to [74da885549].
︙ | ︙ | |||
22 23 24 25 26 27 28 | <fieldset> <legend>Connexion</legend> <dl> <dt><label for="f_id">{$champ.title}</label></dt> <dd><input type="text" name="_id" id="f_id" value="{form_field name=_id}" /></dd> <dt><label for="f_passe">Mot de passe</label></dt> | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <fieldset> <legend>Connexion</legend> <dl> <dt><label for="f_id">{$champ.title}</label></dt> <dd><input type="text" name="_id" id="f_id" value="{form_field name=_id}" /></dd> <dt><label for="f_passe">Mot de passe</label></dt> <dd><input type="password" name="passe" id="f_passe" value="" autocomplete="current-password" /> {if $ssl_enabled} <b class="icn confirm" title="Connexion chiffrée">🔒</b> <span class="confirm">Connexion sécurisée</span> {else} <b class="icn error" title="Connexion non chiffrée">🔓</b> {if $prefer_ssl} <span class="error">Connexion non-sécurisée !</span> |
︙ | ︙ |
Modified src/templates/admin/membres/ajouter.tpl from [e23f736695] to [0fe46b0cf5].
︙ | ︙ | |||
21 22 23 24 25 26 27 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> <dd><input type="password" name="passe" id="f_passe" value="{form_field name=passe}" pattern="{$password_pattern}" autocomplete="off" /></dd> <dt><label for="f_repasse">Encore le mot de passe</label> (vérification)</dt> <dd><input type="password" name="passe_confirmed" id="f_repasse" value="{form_field name=passe_confirmed}" pattern="{$password_pattern}" autocomplete="off" /></dd> </dl> </fieldset> {if $session->canAccess('membres', Membres::DROIT_ADMIN)} <fieldset> <legend>Général</legend> <dl> |
︙ | ︙ |
Modified src/templates/admin/membres/cotisations.tpl from [a057efaebd] to [000e2dab08].
︙ | ︙ | |||
22 23 24 25 26 27 28 | {if !empty($cotisations_membre)} {foreach from=$cotisations_membre item="co"} <dd>{$co.intitule} — {if $co.a_jour} <span class="confirm">À jour</span>{if $co.expiration} — Expire le {$co.expiration|format_sqlite_date_to_french}{/if} {else} <span class="error">En retard</span> | < > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | {if !empty($cotisations_membre)} {foreach from=$cotisations_membre item="co"} <dd>{$co.intitule} — {if $co.a_jour} <span class="confirm">À jour</span>{if $co.expiration} — Expire le {$co.expiration|format_sqlite_date_to_french}{/if} {else} <span class="error">En retard</span> {/if} — <a href="{$admin_url}membres/cotisations/rappels.php?id={$membre.id}">Suivi des rappels</a> </dd> {/foreach} {/if} {if $session->canAccess('membres', Membres::DROIT_ECRITURE)} <dt><form method="get" action="{$admin_url}membres/cotisations/ajout.php"><input type="submit" value="Enregistrer une cotisation →" /><input type="hidden" name="id" value="{$membre.id}" /></form></dt> {/if} </dl> |
︙ | ︙ |
Modified src/templates/admin/membres/modifier.tpl from [9ca196cbb1] to [557472298c].
︙ | ︙ | |||
35 36 37 38 39 40 41 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> | | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> <dd><input type="password" name="passe" id="f_passe" value="{form_field name=passe}" pattern="{$password_pattern}" autocomplete="off" /></dd> <dt><label for="f_repasse">Encore le mot de passe</label> (vérification){if $champs.passe.mandatory} <b title="(Champ obligatoire)">obligatoire</b>{/if}</dt> <dd><input type="password" name="passe_confirmed" id="f_repasse" value="{form_field name=passe_confirmed}" pattern="{$password_pattern}" autocomplete="off" /></dd> </dl> </fieldset> {if $membre.secret_otp || $membre.clef_pgp} <fieldset> <legend>Options de sécurité</legend> <dl> |
︙ | ︙ |
Modified src/templates/admin/membres/recherche_sql.tpl from [af64d8399c] to [eb6cda8094].
︙ | ︙ | |||
29 30 31 32 33 34 35 | {if !empty($result)} <p class="alert">{$result|count} résultats retournés.</p> <table class="list search"> <thead> | | | | | | 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 | {if !empty($result)} <p class="alert">{$result|count} résultats retournés.</p> <table class="list search"> <thead> {if isset($result[0]->id)} <td class="check"><input type="checkbox" value="Tout cocher / décocher" onclick="g.checkUncheck();" /></td> {/if} {foreach from=$result[0] key="col" item="ignore"} <td>{$col}</td> {/foreach} {if isset($result[0]->id)} <td></td> {/if} </thead> <tbody> {foreach from=$result item="row"} <tr> {if $session->canAccess('membres', Membres::DROIT_ADMIN) && isset($result[0]->id)} <td class="check">{if !empty($row.id)}<input type="checkbox" name="selected[]" value="{$row.id}" />{/if}</td> {/if} {foreach from=$row item="col"} <td>{$col}</td> {/foreach} {if isset($row.id)} <td class="actions"> {if !empty($row.id)} <a class="icn" href="{$admin_url}membres/fiche.php?id={$row.id}" title="Fiche membre">👤</a> <a class="icn" href="{$admin_url}membres/modifier.php?id={$row.id}" title="Modifier ce membre">✎</a> {/if} </td> {/if} |
︙ | ︙ |
Modified src/templates/admin/mes_infos_securite.tpl from [ce884953c2] to [6446fe3bc3].
︙ | ︙ | |||
31 32 33 34 35 36 37 | <dl> <dt>Votre clé secrète est :</dt> <dd><code>{$otp.secret_display}</code></dd> <dd class="help">Recopiez la clé secrète ou scannez le QR code pour configurer votre application TOTP (par exemple <a href="https://freeotp.github.io/">FreeOTP</a>), puis utilisez celle-ci pour générer un code d'accès et confirmer l'activation.</dd> <dd class="help">Pour configurer une autre application, vous pouvez utiliser ces paramètres : <tt>{$otp.url}</tt></dd> <dt><label for="f_code">Code TOTP</label></dt> <dd class="help">Entrez ici le code donné par l'application d'authentification double facteur.</dd> | | | | 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 | <dl> <dt>Votre clé secrète est :</dt> <dd><code>{$otp.secret_display}</code></dd> <dd class="help">Recopiez la clé secrète ou scannez le QR code pour configurer votre application TOTP (par exemple <a href="https://freeotp.github.io/">FreeOTP</a>), puis utilisez celle-ci pour générer un code d'accès et confirmer l'activation.</dd> <dd class="help">Pour configurer une autre application, vous pouvez utiliser ces paramètres : <tt>{$otp.url}</tt></dd> <dt><label for="f_code">Code TOTP</label></dt> <dd class="help">Entrez ici le code donné par l'application d'authentification double facteur.</dd> <dd><input type="text" name="code" id="f_code" value="{form_field name=code}" autocomplete="off" /></dd> </dl> </fieldset> {/if} <fieldset> <legend>Confirmer les changements</legend> <dl> <dt><label for="f_passe_confirm">Mot de passe actuel</label></dt> <dd class="help">Entrez votre mot de passe actuel pour confirmer les changements demandés.</dd> <dd><input type="password" name="passe_check" autocomplete="current-password" /></dd> </dl> </fieldset> <p class="submit"> {csrf_field key="edit_me_security"} <input type="hidden" name="passe" value="{form_field name="passe"}" /> <input type="hidden" name="passe_confirmed" value="{form_field name="passe_confirmed"}" /> |
︙ | ︙ | |||
77 78 79 80 81 82 83 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> | | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres. </dd> <dd class="help"> Pas d'idée ? Voici une suggestion choisie au hasard : <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" /> </dd> <dd><input type="password" name="passe" id="f_passe" value="{form_field name=passe}" pattern="{$password_pattern}" autocomplete="new-password" /></dd> <dt><label for="f_repasse">Encore le mot de passe</label> (vérification)</dt> <dd><input type="password" name="passe_confirmed" id="f_passe_confirmed" value="{form_field name=passe_confirmed}" pattern="{$password_pattern}" autocomplete="new-password" /></dd> </dl> {/if} </fieldset> <fieldset> <legend>Authentification à double facteur (2FA)</legend> <p class="help">Pour renforcer la sécurité de votre connexion en cas de vol de votre mot de passe, vous pouvez activer |
︙ | ︙ |
Modified src/www/_route.php from [74f34e99f0] to [259d94156a].
︙ | ︙ | |||
19 20 21 22 23 24 25 | if (($pos = strpos($uri, '?')) !== false) { $uri = substr($uri, 0, $pos); } if (file_exists(__DIR__ . $uri)) { | | > > > > > > | 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 | if (($pos = strpos($uri, '?')) !== false) { $uri = substr($uri, 0, $pos); } if (file_exists(__DIR__ . $uri)) { if (PHP_SAPI != 'cli-server') { die('Erreur de configuration du serveur web: cette URL ne devrait pas être traitée par Garradin'); } return false; } elseif (preg_match('!/p/(.+?)/(.*)!', $uri, $match)) { $_GET['_p'] = $match[1]; $_GET['_u'] = $match[2]; require __DIR__ . '/plugin.php'; } elseif (preg_match('!/admin/plugin/(.+?)/(.*)!', $uri, $match)) { $_GET['_p'] = $match[1]; $_GET['_u'] = $match[2]; require __DIR__ . '/admin/plugin.php'; } |
︙ | ︙ |
Modified src/www/admin/compta/import.php from [d14bd1f9b9] to [2c5e1fbc3f].
︙ | ︙ | |||
19 20 21 22 23 24 25 | exit; } if (f('import')) { $form->check('compta_import', [ 'upload' => 'file|required', | | < < < < | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | exit; } if (f('import')) { $form->check('compta_import', [ 'upload' => 'file|required', 'type' => 'required|in:garradin', ]); if (!$form->hasErrors()) { try { if (f('type') == 'garradin') { $import->fromCSV($_FILES['upload']['tmp_name']); } else { throw new UserException('Import inconnu.'); } |
︙ | ︙ |
Modified src/www/admin/membres/modifier.php from [87a063a4f2] to [5c3655690e].
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $membre = $membres->get($id); if (!$membre) { throw new UserException("Ce membre n'existe pas."); } $cats = new Membres\Categories; $champs = $config->get('champs_membres'); // Protection contre la modification des admins par des membres moins puissants $membre_cat = $cats->get($membre->id_categorie); | > > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | $membre = $membres->get($id); if (!$membre) { throw new UserException("Ce membre n'existe pas."); } // Ne pas modifier le membre courant, on risque de se tirer une balle dans le pied if ($membre->id == $user->id) { throw new UserException("Vous ne pouvez pas modifier votre propre profil, la modification doit être faite par un autre membre."); } $cats = new Membres\Categories; $champs = $config->get('champs_membres'); // Protection contre la modification des admins par des membres moins puissants $membre_cat = $cats->get($membre->id_categorie); |
︙ | ︙ |
Modified src/www/admin/mes_infos.php from [132bd75c18] to [dd87b3ed86].
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | foreach ($champs->getAll() as $key=>$c) { if (!empty($c->editable)) { $data[$key] = f($key); } } $session->editUser($data); Utils::redirect(ADMIN_URL); } catch (UserException $e) { | > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | foreach ($champs->getAll() as $key=>$c) { if (!empty($c->editable)) { $data[$key] = f($key); } } if (isset($data[$config->get('champ_identifiant')]) && !trim($data[$config->get('champ_identifiant')]) && $session->canAccess('config', Membres::DROIT_ADMIN)) { throw new UserException("Le champ identifiant ne peut être vide pour un administrateur, sinon vous ne pourriez plus vous connecter."); } $session->editUser($data); Utils::redirect(ADMIN_URL); } catch (UserException $e) { |
︙ | ︙ |
Modified src/www/admin/static/scripts/datepickr.css from [62519de7c9] to [6ff0b57283].
︙ | ︙ | |||
76 77 78 79 80 81 82 | width: 100%; } .calendar th { text-align: center; } | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | width: 100%; } .calendar th { text-align: center; } .calendar table td { text-align: right; padding: 1px; width: 14.3%; } .calendar td a { display: block; |
︙ | ︙ |
Modified src/www/admin/upgrade.php from [ac88cc9359] to [6934806420].
︙ | ︙ | |||
259 260 261 262 263 264 265 266 267 268 269 270 271 272 | if (version_compare($v, '0.9.5', '<')) { $db->beginSchemaUpdate(); // Créer les tables manquantes $db->import(ROOT . '/include/data/0.9.5_schema.sql'); $db->commitSchemaUpdate(); } if (version_compare($v, '1.0.0', '<')) { $db->beginSchemaUpdate(); $db->import(ROOT . '/include/data/1.0.0_migration.sql'); $db->commitSchemaUpdate(); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | if (version_compare($v, '0.9.5', '<')) { $db->beginSchemaUpdate(); // Créer les tables manquantes $db->import(ROOT . '/include/data/0.9.5_schema.sql'); $db->commitSchemaUpdate(); } if (version_compare($v, '0.9.7', '<')) { $db->begin(); // Conversion des champs date $champs = (array) $config->get('champs_membres')->getAll(); $formats = ['d/m/Y', 'd/m/Y H:i:s', 'd/m/Y H:i', 'd/m/y', 'd-m-Y']; foreach ($champs as $key => $champ) { if ($champ->type == 'date') { $target_format = 'Y-m-d'; } elseif ($champ->type == 'datetime') { $target_format = 'Y-m-d H:i:s'; } else { continue; } $sql = sprintf('SELECT id, %s AS date FROM membres WHERE %01$s IS NOT NULL AND date(%01$s) IS NULL;', $db->quoteIdentifier($key)); foreach ($db->iterate($sql) as $row) { foreach ($formats as $format) { $date = \DateTime::createFromFormat($format, $row->date); if ($date) { break; } } if ($date) { $date = $date->format($target_format); } else { $date = null; } $db->update('membres', [$key => $date], 'id = ' . (int)$row->id); } } $db->commit(); } if (version_compare($v, '1.0.0', '<')) { $db->beginSchemaUpdate(); $db->import(ROOT . '/include/data/1.0.0_migration.sql'); $db->commitSchemaUpdate(); } |
︙ | ︙ |
Added src/www/plugin.php version [3b761f779d].
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php namespace Garradin; require_once __DIR__ . '/_inc.php'; $page = !empty($_GET['_u']) ? $_GET['_u'] : 'index.php'; $plugin = new Plugin(!empty($_GET['_p']) ? $_GET['_p'] : null); define('Garradin\PLUGIN_ROOT', $plugin->path()); define('Garradin\PLUGIN_URL', WWW_URL . 'p/' . $plugin->id() . '/'); define('Garradin\PLUGIN_QSP', '?'); $plugin->call('public/' . $page); |