Overview
SHA1:3dfb698790ba0063bba7346aa1a6a3cbd04c7435
Date: 2017-08-09 06:59:58
User: bohwaz
Comment:Dédoublement du champ ID en deux champs : ID (interne) et numéro de membre (champ membre modifiable)
Timelines: family | ancestors | descendants | both | dev
Downloads: Tarball | ZIP archive
Other Links: files | file ages | folders | manifest
Tags And Properties
Context
2017-08-09
07:19
[6877253ad2] Utilisation du numéro plutôt que l'ID pour import/export de membre (user: bohwaz, tags: dev)
06:59
[3dfb698790] Dédoublement du champ ID en deux champs : ID (interne) et numéro de membre (champ membre modifiable) (user: bohwaz, tags: dev)
05:02
[d71d56990e] Modernisation installation, renomme repasse en passe_confirmed (user: bohwaz, tags: dev)
Changes

Modified src/include/data/champs_membres.ini from [c3b46c317e] to [1bb1e7c845].

40
41
42
43
44
45
46








47
48
49
50
51
52
53
54
55
56
57
58
59
60
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
;		Si présent et un chiffre supérieur à 0, alors le champ apparaîtra dans la liste des membres
;		dans l'ordre défini par le chiffre (si nom est à 2 et email à 1, alors email sera
;		la première colonne et nom la seconde)
;	install:
;		true = sera ajouté aux fiches membres à l'installation
;		false = sera seulement présent dans les champs supplémentaires possibles (défaut)









[nom]
type = text
title = "Nom & prénom"
mandatory = true
install = true
editable = true
list_row = 1

[email]
; ce champ est obligatoirement présent et de type 'email'
type = email
title = "Adresse E-Mail"
mandatory = true
install = true
................................................................................
editable = true

[code_postal]
type = text
title = "Code postal"
install = true
editable = true
list_row = 2

[ville]
type = text
title = "Ville"
install = true
editable = true
list_row = 3

[pays]
type = country
title = "Pays"
install = true
editable = true








>
>
>
>
>
>
>
>






|







 







|






|







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
..
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
;		Si présent et un chiffre supérieur à 0, alors le champ apparaîtra dans la liste des membres
;		dans l'ordre défini par le chiffre (si nom est à 2 et email à 1, alors email sera
;		la première colonne et nom la seconde)
;	install:
;		true = sera ajouté aux fiches membres à l'installation
;		false = sera seulement présent dans les champs supplémentaires possibles (défaut)

[numero]
type = number
title = "Numéro de membre"
mandatory = true
install = true
editable = false
list_row = 1

[nom]
type = text
title = "Nom & prénom"
mandatory = true
install = true
editable = true
list_row = 2

[email]
; ce champ est obligatoirement présent et de type 'email'
type = email
title = "Adresse E-Mail"
mandatory = true
install = true
................................................................................
editable = true

[code_postal]
type = text
title = "Code postal"
install = true
editable = true
list_row = 3

[ville]
type = text
title = "Ville"
install = true
editable = true
list_row = 4

[pays]
type = country
title = "Pays"
install = true
editable = true

Modified src/include/lib/Garradin/Membres.php from [29f4b66141] to [5324be994b].

139
140
141
142
143
144
145
146
147
148
149
150
151













152
153
154
155
156
157
158
...
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
215
216
217
218
219
220
221
222
223
224
225
226
227
...
284
285
286
287
288
289
290





291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306

    public function add($data = [])
    {
        $this->_checkFields($data);
        $db = DB::getInstance();
        $config = Config::getInstance();
        $id = $config->get('champ_identifiant');

        if (!empty($data[$id])
            && $db->firstColumn('SELECT 1 FROM membres WHERE '.$id.' = ? LIMIT 1;', $data[$id]))
        {
            throw new UserException('La valeur du champ '.$id.' est déjà utilisée par un autre membre, hors ce champ doit être unique à chaque membre.');
        }














        if (isset($data['passe']) && trim($data['passe']) != '')
        {
            $data['passe'] = self::hashPassword($data['passe']);
        }
        else
        {
................................................................................
    }

    public function edit($id, $data = [], $check_editable = true)
    {
        $db = DB::getInstance();
        $config = Config::getInstance();

        if (isset($data['id']) && ($data['id'] == $id || empty($data['id'])))
        {
            unset($data['id']);
        }

        $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.' = ? 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, hors ce champ doit être unique à chaque membre.');
        }

        if (!empty($data['id']))
        {
            if (!preg_match('/^\d+$/', $data['id']))
            {
                throw new UserException('Le numéro de membre ne doit contenir que des chiffres.');
            }

            if ($db->firstColumn('SELECT 1 FROM membres WHERE id = ?;', (int)$data['id']))
            {
                throw new UserException('Ce numéro est déjà attribué à un autre membre.');
            }

            // Si on ne vérifie pas toutes les tables qui sont liées ici à un ID de membre
            // la requête de modification provoquera une erreur de contrainte de foreign key
            // ce qui est normal. Donc : il n'est pas possible de changer l'ID d'un membre qui
            // a participé au wiki, à la compta, etc.
            if ($db->firstColumn('SELECT 1 FROM wiki_revisions WHERE id_auteur = ?;', (int)$id)
                || $db->firstColumn('SELECT 1 FROM compta_journal WHERE id_auteur = ?;', (int)$id)
                || $db->firstColumn('SELECT 1 FROM compta_rapprochement WHERE id_auteur = ?;', (int)$id)
                || $db->firstColumn('SELECT 1 FROM membres_operations WHERE id_membre = ?;', (int)$id)
                || $db->firstColumn('SELECT 1 FROM cotisations_membres WHERE id_membre = ?;', (int)$id)
                || $db->firstColumn('SELECT 1 FROM rappels_envoyes WHERE id_membre = ?;', (int)$id)
                || $db->firstColumn('SELECT 1 FROM fichiers_membres WHERE id = ?;', (int)$id))
            # FIXME || $db->firstColumn('SELECT 1 FROM wiki_suivi WHERE id_membre = ?;', false, (int)$id))
            {
                throw new UserException('Le numéro n\'est pas modifiable pour ce membre car des contenus sont liés à ce numéro de membre (wiki, compta, etc.).');
            }
        }

        if (!empty($data['passe']) && trim($data['passe']))
        {
            $data['passe'] = self::hashPassword($data['passe']);
        }
        else
................................................................................
    public function getNom($id)
    {
        $db = DB::getInstance();
        $config = Config::getInstance();

        return $db->firstColumn('SELECT '.$config->get('champ_identite').' FROM membres WHERE id = ? LIMIT 1;', (int)$id);
    }






    public function search($field, $query)
    {
        $db = DB::getInstance();
        $config = Config::getInstance();

        $champs = $config->get('champs_membres');

        if ($field != 'id' && !$champs->get($field))
        {
            throw new \UnexpectedValueException($field . ' is not a valid field');
        }

        $champ = $champs->get($field);

        if ($champ['type'] == 'multiple')







|
|
|



>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
|
<










|

|




|



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>








|







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
...
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
















215
216
217
218
219
220
221
...
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

    public function add($data = [])
    {
        $this->_checkFields($data);
        $db = DB::getInstance();
        $config = Config::getInstance();
        $id = $config->get('champ_identifiant');
        $champs = $config->get('champs_membres');

        if (!empty($data[$id]) && $db->test('membres', $db->where($id, $data[$id])))
        {
            throw new UserException('La valeur du champ '.$id.' est déjà utilisée par un autre membre, hors ce champ doit être unique à chaque membre.');
        }

        // Numéro de membre
        if ($champs->get('numero'))
        {
            if (empty($data['numero']))
            {
                $data['numero'] = $db->firstColumn('SELECT MAX(numero) + 1 FROM membres;');
            }
            elseif ($db->test('membres', $db->where('numero', $data['numero'])))
            {
                throw new UserException('Ce numéro de membre est déjà attribué à un autre membre.');
            }
        }

        if (isset($data['passe']) && trim($data['passe']) != '')
        {
            $data['passe'] = self::hashPassword($data['passe']);
        }
        else
        {
................................................................................
    }

    public function edit($id, $data = [], $check_editable = true)
    {
        $db = DB::getInstance();
        $config = Config::getInstance();



        unset($data['id']);


        $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.' = ? 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, hors ce champ doit être unique à chaque membre.');
        }

        if (!empty($data['numero']))
        {
            if (!preg_match('/^\d+$/', $data['numero']))
            {
                throw new UserException('Le numéro de membre ne doit contenir que des chiffres.');
            }

            if ($db->test('membres', 'numero = ? AND id != ?', (int)$data['numero'], $id))
            {
                throw new UserException('Ce numéro est déjà attribué à un autre membre.');
            }
















        }

        if (!empty($data['passe']) && trim($data['passe']))
        {
            $data['passe'] = self::hashPassword($data['passe']);
        }
        else
................................................................................
    public function getNom($id)
    {
        $db = DB::getInstance();
        $config = Config::getInstance();

        return $db->firstColumn('SELECT '.$config->get('champ_identite').' FROM membres WHERE id = ? LIMIT 1;', (int)$id);
    }

    public function getIDWithNumero($numero)
    {
        return DB::getInstance()->firstColumn('SELECT id FROM membres WHERE numero = ?;', (int) $numero);
    }

    public function search($field, $query)
    {
        $db = DB::getInstance();
        $config = Config::getInstance();

        $champs = $config->get('champs_membres');

        if (!$champs->get($field))
        {
            throw new \UnexpectedValueException($field . ' is not a valid field');
        }

        $champ = $champs->get($field);

        if ($champ['type'] == 'multiple')

Modified src/include/lib/Garradin/Membres/Champs.php from [72f212aeb0] to [6b0d0f9ef0].

87
88
89
90
91
92
93



94
95










96
97

98
99
100
101
102
103
104
105
106
...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
421
422
423
424
425
426
427





428
429
430
431
432
433
434
...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
...
510
511
512
513
514
515
516
517
518





519
520
521
522
523
524
525
526
527
528
529
530
531
...
540
541
542
543
544
545
546





547
548
549
550
551
552
553
	{
		if ($champs instanceOf Champs)
		{
			$this->champs = $champs->getAll();
		}
        elseif (is_array($champs))
        {



            foreach ($champs as $key=>&$config)
            {










                $this->_checkField($key, $config);
            }


            $this->champs = $champs;
        }
		else
		{
			$champs = parse_ini_string((string)$champs, true);

            foreach ($champs as $key=>&$config)
            {
................................................................................
            return true;
        else
            return false;
    }

	public function getAll()
	{
        $this->champs->passe->title = 'Mot de passe';
		return $this->champs;
	}

    public function getList($with_id = false)
    {
        $champs = clone $this->champs;
        unset($champs->passe);
................................................................................
            throw new UserException('Le champ E-Mail ne peut être supprimé des fiches membres.');
        }

        if (!array_key_exists('passe', $champs))
        {
            throw new UserException('Le champ Mot de passe ne peut être supprimé des fiches membres.');
        }






        $config = Config::getInstance();

        $identite = $config->get('champ_identite');

        if ($identite != 'id' && !array_key_exists($identite, $champs))
        {
................................................................................

        $create_keys = [
            'FOREIGN KEY (id_categorie) REFERENCES membres_categories (id)'
        ];

    	// Champs à recopier
    	$copy = [
    		'id',
    		'id_categorie',
            'date_connexion',
            'date_inscription',
            'secret_otp',
            'clef_pgp',
    	];

        $anciens_champs = $config->get('champs_membres');
    	$anciens_champs = is_null($anciens_champs) ? $this->champs : $anciens_champs->getAll();

    	foreach ($this->champs as $key=>$cfg)
    	{
    		if ($cfg->type == 'number')
    			$type = 'FLOAT';
    		elseif ($cfg->type == 'multiple' || $cfg->type == 'checkbox')
    			$type = 'INTEGER';
    		elseif ($cfg->type == 'file')
    			$type = 'BLOB';
    		else
    			$type = 'TEXT';

    		$line = $key . ' ' . $type . ',';
................................................................................
                $line .= ' -- ' . str_replace(["\n", "\r"], '', $cfg->title);
            }

            $create[] = $line;

    		if (property_exists($anciens_champs, $key))
    		{
    			$copy[] = $key;
    		}





    	}

    	$create = array_merge($create, $create_keys);

    	$create = 'CREATE TABLE membres_tmp (' . "\n\t" . implode("\n\t", $create) . "\n);";
    	$copy = 'INSERT INTO membres_tmp (' . implode(', ', $copy) . ') SELECT ' . implode(', ', $copy) . ' FROM membres;';

    	$db->exec('PRAGMA foreign_keys = OFF;');
    	$db->begin();
    	$db->exec($create);
    	
    	if ($enable_copy) {
    		$db->exec($copy);
................................................................................
            // Mettre les champs identifiant vides à NULL pour pouvoir créer un index unique
            $db->exec('UPDATE membres SET '.$config->get('champ_identifiant').' = NULL 
                WHERE '.$config->get('champ_identifiant').' = "";');

            // Création de l'index unique
            $db->exec('CREATE UNIQUE INDEX membres_identifiant ON membres ('.$config->get('champ_identifiant').');');
        }






        // Création des index pour les champs affichés dans la liste des membres
        $listed_fields = array_keys((array) $this->getListedFields());
        foreach ($listed_fields as $field)
        {
            if ($field === $config->get('champ_identifiant'))
            {







>
>
>


>
>
>
>
>
>
>
>
>
>

|
>
|
<







 







<







 







>
>
>
>
>







 







|
|
|
|
|
|







<
<
|







 







|

>
>
>
>
>





|







 







>
>
>
>
>







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
...
155
156
157
158
159
160
161

162
163
164
165
166
167
168
...
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
...
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
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
...
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
	{
		if ($champs instanceOf Champs)
		{
			$this->champs = $champs->getAll();
		}
        elseif (is_array($champs))
        {
            $presets = self::importPresets();
            $this->champs = new \stdClass;

            foreach ($champs as $key=>&$config)
            {
                if (is_array($config))
                {
                    $config = (object) $config;
                }

                if (isset($presets[$key]))
                {
                    $config->type = $presets[$key]['type'];
                }

                $this->_checkField($key, $config);

                $this->champs->$key = $config;
            }

        }
		else
		{
			$champs = parse_ini_string((string)$champs, true);

            foreach ($champs as $key=>&$config)
            {
................................................................................
            return true;
        else
            return false;
    }

	public function getAll()
	{

		return $this->champs;
	}

    public function getList($with_id = false)
    {
        $champs = clone $this->champs;
        unset($champs->passe);
................................................................................
            throw new UserException('Le champ E-Mail ne peut être supprimé des fiches membres.');
        }

        if (!array_key_exists('passe', $champs))
        {
            throw new UserException('Le champ Mot de passe ne peut être supprimé des fiches membres.');
        }

        if (!array_key_exists('numero', $champs))
        {
            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' && !array_key_exists($identite, $champs))
        {
................................................................................

        $create_keys = [
            'FOREIGN KEY (id_categorie) REFERENCES membres_categories (id)'
        ];

    	// Champs à recopier
    	$copy = [
    		'id' => 'id',
    		'id_categorie' => 'id_categorie',
            'date_connexion' => 'date_connexion',
            'date_inscription' => 'date_inscription',
            'secret_otp' => 'secret_otp',
            'clef_pgp' => 'clef_pgp',
    	];

        $anciens_champs = $config->get('champs_membres');
    	$anciens_champs = is_null($anciens_champs) ? $this->champs : $anciens_champs->getAll();

    	foreach ($this->champs as $key=>$cfg)
    	{


    		if ($cfg->type == 'number' || $cfg->type == 'multiple' || $cfg->type == 'checkbox')
    			$type = 'INTEGER';
    		elseif ($cfg->type == 'file')
    			$type = 'BLOB';
    		else
    			$type = 'TEXT';

    		$line = $key . ' ' . $type . ',';
................................................................................
                $line .= ' -- ' . str_replace(["\n", "\r"], '', $cfg->title);
            }

            $create[] = $line;

    		if (property_exists($anciens_champs, $key))
    		{
    			$copy[$key] = $key;
    		}
            elseif ($key == 'numero')
            {
                // Copie des numéros de membre à partir du champ ID
                $copy[$key] = 'id';
            }
    	}

    	$create = array_merge($create, $create_keys);

    	$create = 'CREATE TABLE membres_tmp (' . "\n\t" . implode("\n\t", $create) . "\n);";
    	$copy = 'INSERT INTO membres_tmp (' . implode(', ', array_keys($copy)) . ') SELECT ' . implode(', ', $copy) . ' FROM membres;';

    	$db->exec('PRAGMA foreign_keys = OFF;');
    	$db->begin();
    	$db->exec($create);
    	
    	if ($enable_copy) {
    		$db->exec($copy);
................................................................................
            // Mettre les champs identifiant vides à NULL pour pouvoir créer un index unique
            $db->exec('UPDATE membres SET '.$config->get('champ_identifiant').' = NULL 
                WHERE '.$config->get('champ_identifiant').' = "";');

            // Création de l'index unique
            $db->exec('CREATE UNIQUE INDEX membres_identifiant ON membres ('.$config->get('champ_identifiant').');');
        }

        if (isset($this->champs->numero))
        {
            $db->exec('CREATE UNIQUE INDEX membres_numero ON membres (numero);');
        }

        // Création des index pour les champs affichés dans la liste des membres
        $listed_fields = array_keys((array) $this->getListedFields());
        foreach ($listed_fields as $field)
        {
            if ($field === $config->get('champ_identifiant'))
            {

Modified src/include/lib/Garradin/Sauvegarde.php from [31ac9b4e77] to [5f30e3fbad].

299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
		{
			throw new UserException('Le fichier fourni n\'est pas une base de données valide. ' .
				'Message d\'erreur de SQLite : ' . $e->getMessage(), self::NOT_A_DB);
		}

		try {
			// Regardons ensuite si la base de données n'est pas corrompue
			$check = $db->firstColumn('PRAGMA integrity_check;', false);
		}
		catch (\Exception $e)
		{
			// Ici SQLite peut rejeter un message type "file is encrypted or is not a db"
			throw new UserException('Le fichier fourni n\'est pas une base de données valide. ' .
				'Message d\'erreur de SQLite : ' . $e->getMessage(), self::NOT_A_DB);
		}
................................................................................
		{
			throw new UserException('Le fichier fourni est corrompu. SQLite a trouvé ' . $check . ' erreurs.');
		}

		// On ne peut pas faire de vérifications très poussées sur la structure de la base de données,
		// celle-ci pouvant changer d'une version à l'autre et on peut vouloir importer une base
		// un peu vieille, mais on vérifie quand même que ça ressemble un minimum à une base garradin
		$table = $db->firstColumn('SELECT 1 FROM sqlite_master WHERE type=\'table\' AND tbl_name=\'config\';', false);

		if (!$table)
		{
			throw new UserException('Le fichier fourni ne semble pas contenir de données liées à Garradin.');
		}

		// On récupère la version
		$version = $db->firstColumn('SELECT valeur FROM config WHERE cle=\'version\';');

		// Vérification de l'AppID pour les versions récentes
		if (version_compare($version, '0.8.0', '>='))
		{
			$appid = $db->firstColumn('PRAGMA application_id;', false);

			if ($appid !== DB::APPID)
			{
				throw new UserException('Ce fichier n\'est pas une sauvegarde Garradin (application_id ne correspond pas).', self::NO_APP_ID);
			}
		}

		if ($user_id)
		{
			// Empêchons l'admin de se tirer une balle dans le pied
			$is_still_admin = $db->test('membres_categories', 
				'id = (SELECT id_categorie FROM membres WHERE id = ' . (int) $user_id . ')
				AND droit_config >= ' . Membres::DROIT_ADMIN . '
				AND droit_connexion >= ' . Membres::DROIT_ACCES);

			if (!$is_still_admin)
			{
				$return |= self::NOT_AN_ADMIN;
			}







|







 







|







|




|










|
|







299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
		{
			throw new UserException('Le fichier fourni n\'est pas une base de données valide. ' .
				'Message d\'erreur de SQLite : ' . $e->getMessage(), self::NOT_A_DB);
		}

		try {
			// Regardons ensuite si la base de données n'est pas corrompue
			$check = $db->querySingle('PRAGMA integrity_check;', false);
		}
		catch (\Exception $e)
		{
			// Ici SQLite peut rejeter un message type "file is encrypted or is not a db"
			throw new UserException('Le fichier fourni n\'est pas une base de données valide. ' .
				'Message d\'erreur de SQLite : ' . $e->getMessage(), self::NOT_A_DB);
		}
................................................................................
		{
			throw new UserException('Le fichier fourni est corrompu. SQLite a trouvé ' . $check . ' erreurs.');
		}

		// On ne peut pas faire de vérifications très poussées sur la structure de la base de données,
		// celle-ci pouvant changer d'une version à l'autre et on peut vouloir importer une base
		// un peu vieille, mais on vérifie quand même que ça ressemble un minimum à une base garradin
		$table = $db->querySingle('SELECT 1 FROM sqlite_master WHERE type=\'table\' AND tbl_name=\'config\';');

		if (!$table)
		{
			throw new UserException('Le fichier fourni ne semble pas contenir de données liées à Garradin.');
		}

		// On récupère la version
		$version = $db->querySingle('SELECT valeur FROM config WHERE cle=\'version\';');

		// Vérification de l'AppID pour les versions récentes
		if (version_compare($version, '0.8.0', '>='))
		{
			$appid = $db->querySingle('PRAGMA application_id;', false);

			if ($appid !== DB::APPID)
			{
				throw new UserException('Ce fichier n\'est pas une sauvegarde Garradin (application_id ne correspond pas).', self::NO_APP_ID);
			}
		}

		if ($user_id)
		{
			// Empêchons l'admin de se tirer une balle dans le pied
			$is_still_admin = $db->querySingle('SELECT 1 FROM membres_categories
				WHERE id = (SELECT id_categorie FROM membres WHERE id = ' . (int) $user_id . ')
				AND droit_config >= ' . Membres::DROIT_ADMIN . '
				AND droit_connexion >= ' . Membres::DROIT_ACCES);

			if (!$is_still_admin)
			{
				$return |= self::NOT_AN_ADMIN;
			}

Modified src/include/lib/Garradin/Template.php from [44be2cd115] to [6ee6d1d6a7].

482
483
484
485
486
487
488





489
490
491
492
493
494
495
        $params['pattern'] = '\d{4}-\d{2}-\d{2}';
    }

    $field = '';
    $value = tpl_form_field($params);
    $attributes = 'name="' . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '" ';
    $attributes .= 'id="f_' . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '" ';






    if (!empty($params['disabled']))
    {
        $attributes .= 'disabled="disabled" ';
    }

    if (!empty($config->mandatory))







>
>
>
>
>







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
        $params['pattern'] = '\d{4}-\d{2}-\d{2}';
    }

    $field = '';
    $value = tpl_form_field($params);
    $attributes = 'name="' . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '" ';
    $attributes .= 'id="f_' . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '" ';

    if ($params['name'] == 'numero' && $config->type == 'number' && !$value)
    {
        $value = DB::getInstance()->firstColumn('SELECT MAX(numero) + 1 FROM membres;');
    }

    if (!empty($params['disabled']))
    {
        $attributes .= 'disabled="disabled" ';
    }

    if (!empty($config->mandatory))

Modified src/templates/admin/config/donnees.tpl from [815a847b92] to [396a0ea87b].

1
2
3
4
5
6

7
8
9
10

11
12
13
14
15
16
17
18
..
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
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
...
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
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
{include file="admin/_head.tpl" title="Données — Sauvegarde et restauration" current="config"}

{include file="admin/config/_menu.tpl" current="donnees"}

{if $error}
    <p class="error">{$error}</p>

    {if $code == Garradin\Sauvegarde::INTEGRITY_FAIL && Garradin\ALLOW_MODIFIED_IMPORT}
        <p class="alert">Pour passer outre, renvoyez le fichier en cochant la case «&nbsp;Ignorer les erreurs&nbsp;».
        Attention, si vous avez effectué des modifications dans la base de données, cela peut créer des bugs&nbsp;!</p>
    {/if}

{elseif $ok}
    <p class="confirm">
        {if $ok == 'config'}La configuration a bien été enregistrée.
        {elseif $ok == 'create'}Une nouvelle sauvegarde a été créée.
        {elseif $ok == 'restore'}La restauration a bien été effectuée. Si vous désirez revenir en arrière, vous pouvez utiliser la sauvegarde automatique nommée <em>date-du-jour.avant_restauration.sqlite</em>, sinon vous pouvez l'effacer.
            {if $ok_code & Garradin\Sauvegarde::NOT_AN_ADMIN}
            </p>
            <p class="alert">
................................................................................
                Merci de corriger les droits des catégories maintenant.
            {/if}
        {elseif $ok == 'remove'}La sauvegarde a été supprimée.
        {/if}
    </p>
{/if}

<form method="post" action="{$self_url}">

<p class="help">
    Info : la base de données fait actuellement {$db_size|format_bytes} (dont {$files_size|format_bytes} pour les documents et images).
</p>

<fieldset>
    <legend>Sauvegarde automatique</legend>
................................................................................
    <p>
        {csrf_field key="backup_config"}
        <input type="submit" name="config" value="Enregistrer &rarr;" />
    </p>
</fieldset>

</form>
<form method="post" action="{$self_url}">

<fieldset>
    <legend>Sauvegarde manuelle</legend>
    <p>
        {csrf_field key="backup_create"}
        <input type="submit" name="create" value="Créer une nouvelle sauvegarde des données &rarr;" />
    </p>
</fieldset>

</form>
<form method="post" action="{$self_url}">

<fieldset>
    <legend>Copies de sauvegarde disponibles</legend>
    {if empty($liste)}
        <p class="help">Aucune copie de sauvegarde disponible.</p>
    {else}
        <dl>
................................................................................
            <input type="submit" name="restore" value="Restaurer cette sauvegarde" />
            <input type="submit" name="remove" value="Supprimer cette sauvegarde" />
        </p>
    {/if}
</fieldset>

</form>
<form method="post" action="{$self_url}">

<fieldset>
    <legend>Téléchargement</legend>
    <p>
        {csrf_field key="backup_download"}
        <input type="submit" name="download" value="Télécharger une copie des données sur mon ordinateur" />
    </p>
</fieldset>

</form>
<form method="post" action="{$self_url}" enctype="multipart/form-data">

<fieldset>
    <legend><label for="f_file">Restaurer depuis un fichier</label></legend>
    <p class="alert">
        Attention, l'intégralité des données courantes seront effacées et remplacées par celles
        contenues dans le fichier fourni.
    </p>
................................................................................
    <p>
        {csrf_field key="backup_restore"}
        <input type="hidden" name="MAX_FILE_SIZE" value="{$max_file_size}" />
        <input type="file" name="file" id="f_file" required="required" />
        (maximum {$max_file_size|format_bytes})
        <input type="submit" name="restore_file" value="Restaurer depuis le fichier sélectionné &rarr;" />
    </p>
    {if $error && ($code == Garradin\Sauvegarde::INTEGRITY_FAIL && Garradin\ALLOW_MODIFIED_IMPORT)}
    <p>
        <label><input type="checkbox" name="force_import" value="1" /> Ignorer les erreurs, je sais ce que je fait</label>
    </p>
    {/if}
</fieldset>

</form>

{include file="admin/_foot.tpl"}




|
<
>
|
|
|
|
>
|







 







|







 







|










|







 







|










|







 







|









1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
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
...
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
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
{include file="admin/_head.tpl" title="Données — Sauvegarde et restauration" current="config"}

{include file="admin/config/_menu.tpl" current="donnees"}

{form_errors}


{if $code == Garradin\Sauvegarde::INTEGRITY_FAIL && Garradin\ALLOW_MODIFIED_IMPORT}
    <p class="alert">Pour passer outre, renvoyez le fichier en cochant la case «&nbsp;Ignorer les erreurs&nbsp;».
    Attention, si vous avez effectué des modifications dans la base de données, cela peut créer des bugs&nbsp;!</p>
{/if}

{if $ok}
    <p class="confirm">
        {if $ok == 'config'}La configuration a bien été enregistrée.
        {elseif $ok == 'create'}Une nouvelle sauvegarde a été créée.
        {elseif $ok == 'restore'}La restauration a bien été effectuée. Si vous désirez revenir en arrière, vous pouvez utiliser la sauvegarde automatique nommée <em>date-du-jour.avant_restauration.sqlite</em>, sinon vous pouvez l'effacer.
            {if $ok_code & Garradin\Sauvegarde::NOT_AN_ADMIN}
            </p>
            <p class="alert">
................................................................................
                Merci de corriger les droits des catégories maintenant.
            {/if}
        {elseif $ok == 'remove'}La sauvegarde a été supprimée.
        {/if}
    </p>
{/if}

<form method="post" action="{$self_url_no_qs}">

<p class="help">
    Info : la base de données fait actuellement {$db_size|format_bytes} (dont {$files_size|format_bytes} pour les documents et images).
</p>

<fieldset>
    <legend>Sauvegarde automatique</legend>
................................................................................
    <p>
        {csrf_field key="backup_config"}
        <input type="submit" name="config" value="Enregistrer &rarr;" />
    </p>
</fieldset>

</form>
<form method="post" action="{$self_url_no_qs}">

<fieldset>
    <legend>Sauvegarde manuelle</legend>
    <p>
        {csrf_field key="backup_create"}
        <input type="submit" name="create" value="Créer une nouvelle sauvegarde des données &rarr;" />
    </p>
</fieldset>

</form>
<form method="post" action="{$self_url_no_qs}">

<fieldset>
    <legend>Copies de sauvegarde disponibles</legend>
    {if empty($liste)}
        <p class="help">Aucune copie de sauvegarde disponible.</p>
    {else}
        <dl>
................................................................................
            <input type="submit" name="restore" value="Restaurer cette sauvegarde" />
            <input type="submit" name="remove" value="Supprimer cette sauvegarde" />
        </p>
    {/if}
</fieldset>

</form>
<form method="post" action="{$self_url_no_qs}">

<fieldset>
    <legend>Téléchargement</legend>
    <p>
        {csrf_field key="backup_download"}
        <input type="submit" name="download" value="Télécharger une copie des données sur mon ordinateur" />
    </p>
</fieldset>

</form>
<form method="post" action="{$self_url_no_qs}" enctype="multipart/form-data">

<fieldset>
    <legend><label for="f_file">Restaurer depuis un fichier</label></legend>
    <p class="alert">
        Attention, l'intégralité des données courantes seront effacées et remplacées par celles
        contenues dans le fichier fourni.
    </p>
................................................................................
    <p>
        {csrf_field key="backup_restore"}
        <input type="hidden" name="MAX_FILE_SIZE" value="{$max_file_size}" />
        <input type="file" name="file" id="f_file" required="required" />
        (maximum {$max_file_size|format_bytes})
        <input type="submit" name="restore_file" value="Restaurer depuis le fichier sélectionné &rarr;" />
    </p>
    {if $code && ($code == Garradin\Sauvegarde::INTEGRITY_FAIL && Garradin\ALLOW_MODIFIED_IMPORT)}
    <p>
        <label><input type="checkbox" name="force_import" value="1" /> Ignorer les erreurs, je sais ce que je fait</label>
    </p>
    {/if}
</fieldset>

</form>

{include file="admin/_foot.tpl"}

Modified src/templates/admin/config/membres.tpl from [fae223707c] to [731fd23cc8].

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
..
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
...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
...
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
{include file="admin/_head.tpl" current="config" js=1}

{include file="admin/config/_menu.tpl" current="membres"}

{if $error}
    {if $error == 'OK'}
    <p class="confirm">
        La configuration a bien été enregistrée.
    </p>
    {elseif $error == 'ADD_OK'}
    <p class="confirm">
        Le champ a été ajouté à la fin de la liste.
    </p>
    {else}
    <p class="error">
        {$error}
    </p>
    {/if}
{/if}



{if $review}
    <p class="help">
        Voici ce à quoi ressemblera la nouvelle fiche de membre, vérifiez vos modifications avant d'enregistrer les changements.
    </p>
    <p class="alert">
        Attention&nbsp;! Si vous avez supprimé un champ, les données liées à celui-ci seront supprimées de toutes les fiches de tous les membres.
................................................................................
            {/if}
        </dl>
    </fieldset>

    <form method="post" action="{$admin_url}config/membres.php">
        <p class="submit">
            {csrf_field key="config_membres"}

            <input type="submit" name="back" value="&larr; Retour à l'édition" class="minor" />
            <input type="submit" name="reset" value="Annuler les changements" class="minor" />
            <input type="submit" name="save" value="Enregistrer &rarr;" />
        </p>
    </form>
{else}
    <p class="help">
        Cette page vous permet de personnaliser les fiches d'information des membres de l'association.<br />
        <strong>Attention :</strong> Les champs supprimés de la fiche seront effacés de toutes les fiches de tous les membres, et les données qu'ils contenaient seront perdues.
    </p>

    <fieldset>
        <legend>Champs non-personnalisables</legend>
        <dl>
            <dt>Numéro unique</dt>
            <dd>Ce numéro identifie de manière unique chacun des membres. 
                Il est incrémenté à chaque nouveau membre ajouté.</dd>
            <dt>Catégorie</dt>
            <dd>Identifie la catégorie du membre.</dd>
            <dt>Date de dernière connexion</dt>
            <dd>Mémorise la date de dernière connexion à l'administration de Garradin.</dd>
            <dt>Date d'inscription</dt>
            <dd>Enregistre la date de création de la fiche du membre.</dd>
        </dl>
    </fieldset>

    {if !empty($presets)}
    <form method="post" action="{$self_url}">
    <fieldset>
        <legend>Ajouter un champ pré-défini</legend>
        <p>
            <select name="preset" required="required">
                <option></option>
                {foreach from=$presets key="name" item="preset"}
                <option value="{$name}">{$name} &mdash; {$preset.title}</option>
                {/foreach}
            </select>
            <input type="hidden" name="{$csrf_name}" value="{$csrf_value}" />
            <input type="submit" name="add" value="Ajouter ce champ à la fiche membre" />
        </p>
    </fieldset>
    </form>
    {/if}

<form method="post" action="{$self_url}">
................................................................................
                    {foreach from=$types key="type" item="nom"}
                    <option value="{$type}" {form_field name=new_type selected=$type}>{$nom}</option>
                    {/foreach}
                </select>
            </dd>
        </dl>
        <p>
            <input type="hidden" name="{$csrf_name}" value="{$csrf_value}" />
            <input type="submit" name="add" value="Ajouter ce champ à la fiche membre" />
        </p>
    </fieldset>
</form>

<form method="post" action="{$self_url}">
    <div id="orderFields">
................................................................................
            <dd class="help">Si coché, ce champ ne pourra rester vide.</dd>
            <dt><label><input type="checkbox" name="champs[passe][private]" value="1" {form_field data=$champs.passe name=private checked="1"} /> Champ privé</label></dt>
            <dd class="help">Si coché, ce champ ne sera visible et modifiable que par les personnes pouvant gérer les membres, mais pas les membres eux-même.</dd>
        </dl>
    </fieldset>

    <p class="submit">
        <input type="hidden" name="{$csrf_name}" value="{$csrf_value}" />
        <input type="submit" name="reset" value="Annuler les changements" class="minor" />
        <input type="submit" name="review" value="Enregistrer &rarr;" />
        (un récapitulatif sera présenté et une confirmation sera demandée)
    </p>
</form>

<script type="text/javascript">
................................................................................
        edit.title = 'Modifier ce champ';
        edit.onclick = function (e) {
            this.parentNode.parentNode.querySelector('dl').classList.toggle('hidden');
            return false;
        };
        actions.appendChild(edit);

        if (field.id != champ_identifiant && field.id != 'f_passe' && field.id != champ_identite)
        {
            var rem = document.createElement('a');
            rem.className = 'icn remove';
            rem.innerHTML = '✘';
            rem.title = 'Enlever ce champ de la fiche';
            rem.onclick = function (e) {
                if (!window.confirm('Êtes-vous sûr de supprimer ce champ des fiches de membre ?'))
................................................................................
                {
                    return false;
                }

                var field = this.parentNode.parentNode;
                this.parentNode.parentNode.querySelector('dl').classList.add('hidden');
                field.classList.toggle('removed');
                window.setTimeout(function () { field.parentNode.removeChild(field); }, 1000);
                return false;
            };
            actions.appendChild(rem);
        }

        if (field.querySelector('.options'))
        {




|
<



|



<
<
<
<
|
<
>
>







 







>











<
<
<
<
<
<
<
<
<
<
<
<
<
<
<











|







 







|







 







|







 







|







 







|







1
2
3
4
5

6
7
8
9
10
11
12




13

14
15
16
17
18
19
20
21
22
..
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
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
...
254
255
256
257
258
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
{include file="admin/_head.tpl" current="config" js=1}

{include file="admin/config/_menu.tpl" current="membres"}

{if isset($status) && $status == 'OK'}

    <p class="confirm">
        La configuration a bien été enregistrée.
    </p>
{elseif isset($status) && $status == 'ADDED'}
    <p class="confirm">
        Le champ a été ajouté à la fin de la liste.
    </p>




{/if}


{form_errors}

{if $review}
    <p class="help">
        Voici ce à quoi ressemblera la nouvelle fiche de membre, vérifiez vos modifications avant d'enregistrer les changements.
    </p>
    <p class="alert">
        Attention&nbsp;! Si vous avez supprimé un champ, les données liées à celui-ci seront supprimées de toutes les fiches de tous les membres.
................................................................................
            {/if}
        </dl>
    </fieldset>

    <form method="post" action="{$admin_url}config/membres.php">
        <p class="submit">
            {csrf_field key="config_membres"}
            <input type="hidden" name="champs" value="{$champs|escape:json|escape}" />
            <input type="submit" name="back" value="&larr; Retour à l'édition" class="minor" />
            <input type="submit" name="reset" value="Annuler les changements" class="minor" />
            <input type="submit" name="save" value="Enregistrer &rarr;" />
        </p>
    </form>
{else}
    <p class="help">
        Cette page vous permet de personnaliser les fiches d'information des membres de l'association.<br />
        <strong>Attention :</strong> Les champs supprimés de la fiche seront effacés de toutes les fiches de tous les membres, et les données qu'ils contenaient seront perdues.
    </p>
















    {if !empty($presets)}
    <form method="post" action="{$self_url}">
    <fieldset>
        <legend>Ajouter un champ pré-défini</legend>
        <p>
            <select name="preset" required="required">
                <option></option>
                {foreach from=$presets key="name" item="preset"}
                <option value="{$name}">{$name} &mdash; {$preset.title}</option>
                {/foreach}
            </select>
            {csrf_field key="config_membres"}
            <input type="submit" name="add" value="Ajouter ce champ à la fiche membre" />
        </p>
    </fieldset>
    </form>
    {/if}

<form method="post" action="{$self_url}">
................................................................................
                    {foreach from=$types key="type" item="nom"}
                    <option value="{$type}" {form_field name=new_type selected=$type}>{$nom}</option>
                    {/foreach}
                </select>
            </dd>
        </dl>
        <p>
            {csrf_field key="config_membres"}
            <input type="submit" name="add" value="Ajouter ce champ à la fiche membre" />
        </p>
    </fieldset>
</form>

<form method="post" action="{$self_url}">
    <div id="orderFields">
................................................................................
            <dd class="help">Si coché, ce champ ne pourra rester vide.</dd>
            <dt><label><input type="checkbox" name="champs[passe][private]" value="1" {form_field data=$champs.passe name=private checked="1"} /> Champ privé</label></dt>
            <dd class="help">Si coché, ce champ ne sera visible et modifiable que par les personnes pouvant gérer les membres, mais pas les membres eux-même.</dd>
        </dl>
    </fieldset>

    <p class="submit">
        {csrf_field key="config_membres"}
        <input type="submit" name="reset" value="Annuler les changements" class="minor" />
        <input type="submit" name="review" value="Enregistrer &rarr;" />
        (un récapitulatif sera présenté et une confirmation sera demandée)
    </p>
</form>

<script type="text/javascript">
................................................................................
        edit.title = 'Modifier ce champ';
        edit.onclick = function (e) {
            this.parentNode.parentNode.querySelector('dl').classList.toggle('hidden');
            return false;
        };
        actions.appendChild(edit);

        if (field.id != champ_identifiant && field.id != 'f_passe' && field.id != champ_identite && field.id != 'f_numero')
        {
            var rem = document.createElement('a');
            rem.className = 'icn remove';
            rem.innerHTML = '✘';
            rem.title = 'Enlever ce champ de la fiche';
            rem.onclick = function (e) {
                if (!window.confirm('Êtes-vous sûr de supprimer ce champ des fiches de membre ?'))
................................................................................
                {
                    return false;
                }

                var field = this.parentNode.parentNode;
                this.parentNode.parentNode.querySelector('dl').classList.add('hidden');
                field.classList.toggle('removed');
                window.setTimeout(function () { field.parentNode.removeChild(field); }, 800);
                return false;
            };
            actions.appendChild(rem);
        }

        if (field.querySelector('.options'))
        {

Modified src/templates/admin/config/plugins.tpl from [1d5abf639f] to [f3dbb74805].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
{include file="admin/_head.tpl" title="Extensions" current="config"}

{include file="admin/config/_menu.tpl" current="plugins"}

{if $error}
    <p class="error">
        {$error}
    </p>
{/if}

{if !empty($delete)}
    <form method="post" action="{$self_url}">

        <fieldset>
            <legend>Désinstaller une extension</legend>
            <h3 class="warning">
................................................................................

        <fieldset>
            <legend>Extensions à installer</legend>
            <dl>
                {foreach from=$liste_telecharges item="plugin" key="id"}
                <dt>
                    <label>
                        <input type="radio" name="to_install" value="{$id}" />
                        {$plugin.nom}
                    </label>
                    (version {$plugin.version})
                </dt>
                <dd>[<a href="{$plugin.url}" onclick="return !window.open(this.href);">{$plugin.auteur}</a>] {$plugin.description}</dd>
                {/foreach}
            </dl>




<
<
|
<
<







 







|







1
2
3
4


5


6
7
8
9
10
11
12
..
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
{include file="admin/_head.tpl" title="Extensions" current="config"}

{include file="admin/config/_menu.tpl" current="plugins"}



{form_errors}



{if !empty($delete)}
    <form method="post" action="{$self_url}">

        <fieldset>
            <legend>Désinstaller une extension</legend>
            <h3 class="warning">
................................................................................

        <fieldset>
            <legend>Extensions à installer</legend>
            <dl>
                {foreach from=$liste_telecharges item="plugin" key="id"}
                <dt>
                    <label>
                        <input type="radio" name="plugin" value="{$id}" />
                        {$plugin.nom}
                    </label>
                    (version {$plugin.version})
                </dt>
                <dd>[<a href="{$plugin.url}" onclick="return !window.open(this.href);">{$plugin.auteur}</a>] {$plugin.description}</dd>
                {/foreach}
            </dl>

Modified src/templates/admin/membres/cotisations/ajout.tpl from [d289eec67b] to [aaf9e77610].

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
                    <option value="{$compte.id}"{if $compte.id == $banque} selected="selected"{/if}>{$compte.libelle} - {$compte.banque}</option>
                {/foreach}
                </select>
            </dd>
            <dt><label for="f_date">Date</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="date" name="date" id="f_date" value="{form_field name=date default=$default_date}" required="required" /></dd>
            {if !$membre}
            <dt><label for="f_id_membre">Numéro de membre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="number" name="id_membre" id="f_id_membre" value="{form_field name=id_membre}" step="1" min="1" required="required" /></dd>
            {/if}
        </dl>
    </fieldset>

    <p class="submit">
        {csrf_field key="add_cotisation"}
        {if $membre}<input type="hidden" name="id_membre" value="{$membre.id}" />{/if}







|
|







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
                    <option value="{$compte.id}"{if $compte.id == $banque} selected="selected"{/if}>{$compte.libelle} - {$compte.banque}</option>
                {/foreach}
                </select>
            </dd>
            <dt><label for="f_date">Date</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="date" name="date" id="f_date" value="{form_field name=date default=$default_date}" required="required" /></dd>
            {if !$membre}
            <dt><label for="f_numero_membre">Numéro de membre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="number" name="numero_membre" id="f_numero_membre" value="{form_field name=numero_membre}" step="1" min="1" required="required" /></dd>
            {/if}
        </dl>
    </fieldset>

    <p class="submit">
        {csrf_field key="add_cotisation"}
        {if $membre}<input type="hidden" name="id_membre" value="{$membre.id}" />{/if}

Modified src/templates/admin/membres/fiche.tpl from [ce083161b7] to [6c7c3b67dd].

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    <dd>{$nb_operations} écritures comptables
        — <a href="{$admin_url}compta/operations/membre.php?id={$membre.id}">Voir la liste des écritures ajoutées par ce membre</a>
    </dd>
 {/if}
</dl>

<dl class="describe">
    <dt>Numéro d'adhérent</dt>
    <dd>{$membre.id}</dd>
    <dt>Catégorie</dt>
    <dd>{$categorie.nom} <span class="droits">{format_droits droits=$categorie}</span></dd>
    <dt>Inscription</dt>
    <dd>{$membre.date_inscription|date_fr:'d/m/Y'}</dd>
    <dt>Dernière connexion</dt>
    <dd>{if empty($membre.date_connexion)}Jamais{else}{$membre.date_connexion|date_fr:'d/m/Y à H:i'}{/if}</dd>
    {foreach from=$champs key="c" item="config"}







<
<







52
53
54
55
56
57
58


59
60
61
62
63
64
65
    <dd>{$nb_operations} écritures comptables
        — <a href="{$admin_url}compta/operations/membre.php?id={$membre.id}">Voir la liste des écritures ajoutées par ce membre</a>
    </dd>
 {/if}
</dl>

<dl class="describe">


    <dt>Catégorie</dt>
    <dd>{$categorie.nom} <span class="droits">{format_droits droits=$categorie}</span></dd>
    <dt>Inscription</dt>
    <dd>{$membre.date_inscription|date_fr:'d/m/Y'}</dd>
    <dt>Dernière connexion</dt>
    <dd>{if empty($membre.date_connexion)}Jamais{else}{$membre.date_connexion|date_fr:'d/m/Y à H:i'}{/if}</dd>
    {foreach from=$champs key="c" item="config"}

Modified src/templates/admin/membres/index.tpl from [a7b4f693eb] to [3953d5a24d].

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
    <form method="post" action="action.php" class="memberList">

    {if !empty($liste)}
    <table class="list">
        <thead class="userOrder">
            <tr>
                {if $user.droits.membres == Garradin\Membres::DROIT_ADMIN}<td class="check"><input type="checkbox" title="Tout cocher / décocher" /></td>{/if}
                <td class="{if $order == 'id'} cur {if $desc}desc{else}asc{/if}{/if}" title="Numéro unique"><a href="?o=id&amp;a" class="icn up">&uarr;</a><a href="?o=id&amp;d" class="icn dn">&darr;</a></td>
                {foreach from=$champs key="c" item="champ"}
                    <td class="{if $order == $c} cur {if $desc}desc{else}asc{/if}{/if}">{$champ.title} <a href="?o={$c}&amp;a&amp;cat={$current_cat}" class="icn up">&uarr;</a><a href="?o={$c}&amp;d&amp;cat={$current_cat}" class="icn dn">&darr;</a></td>
                {/foreach}
                <td></td>
            </tr>
        </thead>
        <tbody>
            {foreach from=$liste item="membre"}
                <tr>
                    {if $user.droits.membres == Garradin\Membres::DROIT_ADMIN}<td class="check"><input type="checkbox" name="selected[]" value="{$membre.id}" /></td>{/if}
                    <td class="num"><a href="{$admin_url}membres/fiche.php?id={$membre.id}">{$membre.id}</a></th>
                    {foreach from=$champs key="c" item="cfg"}


                        <td>{$membre->$c|raw|display_champ_membre:$cfg}</td>


                    {/foreach}
                    <td class="actions">
                        {if !empty($membre.email)}<a class="icn" href="{$admin_url}membres/message.php?id={$membre.id}" title="Envoyer un message">✉</a> {/if}
                        <a class="icn" href="{$admin_url}membres/fiche.php?id={$membre.id}" title="Fiche membre">👤</a>
                        <a class="icn" href="{$admin_url}membres/modifier.php?id={$membre.id}" title="Modifier la fiche membre">✎</a>
                    </td>
                </tr>
            {/foreach}
        </tbody>
    </table>







<

|








<

>
>
|
>
>


<







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
    <form method="post" action="action.php" class="memberList">

    {if !empty($liste)}
    <table class="list">
        <thead class="userOrder">
            <tr>
                {if $user.droits.membres == Garradin\Membres::DROIT_ADMIN}<td class="check"><input type="checkbox" title="Tout cocher / décocher" /></td>{/if}

                {foreach from=$champs key="c" item="champ"}
                    <td class="{if $order == $c} cur {if $desc}desc{else}asc{/if}{/if}">{if $c == "numero"}#{else}{$champ.title}{/if} <a href="?o={$c}&amp;a&amp;cat={$current_cat}" class="icn up">&uarr;</a><a href="?o={$c}&amp;d&amp;cat={$current_cat}" class="icn dn">&darr;</a></td>
                {/foreach}
                <td></td>
            </tr>
        </thead>
        <tbody>
            {foreach from=$liste item="membre"}
                <tr>
                    {if $user.droits.membres == Garradin\Membres::DROIT_ADMIN}<td class="check"><input type="checkbox" name="selected[]" value="{$membre.id}" /></td>{/if}

                    {foreach from=$champs key="c" item="cfg"}
                        <td>
                            {if $c == $config.champ_identite}<a href="{$admin_url}membres/fiche.php?id={$membre.id}">{/if}
                            {$membre->$c|raw|display_champ_membre:$cfg}
                            {if $c == $config.champ_identite}</a>{/if}
                        </td>
                    {/foreach}
                    <td class="actions">

                        <a class="icn" href="{$admin_url}membres/fiche.php?id={$membre.id}" title="Fiche membre">👤</a>
                        <a class="icn" href="{$admin_url}membres/modifier.php?id={$membre.id}" title="Modifier la fiche membre">✎</a>
                    </td>
                </tr>
            {/foreach}
        </tbody>
    </table>

Modified src/templates/admin/membres/modifier.tpl from [77d2626485] to [45f1ca0f6f].

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{form_errors}

<form method="post" action="{$self_url}">

    <fieldset>
        <legend>Informations personnelles</legend>
        <dl>
        {if $user.droits.membres == Garradin\Membres::DROIT_ADMIN}
            <dt><label for="f_id">Numéro de membre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="text" name="id" id="f_id" value="{form_field data=$membre name=id}" required="required" pattern="^\d+$" /></dd>
        {/if}
            {foreach from=$champs item="champ" key="nom"}
                {html_champ_membre config=$champ name=$nom data=$membre}
            {/foreach}
        </dl>
    </fieldset>

    <fieldset>







<
<
<
<







12
13
14
15
16
17
18




19
20
21
22
23
24
25
{form_errors}

<form method="post" action="{$self_url}">

    <fieldset>
        <legend>Informations personnelles</legend>
        <dl>




            {foreach from=$champs item="champ" key="nom"}
                {html_champ_membre config=$champ name=$nom data=$membre}
            {/foreach}
        </dl>
    </fieldset>

    <fieldset>

Modified src/www/admin/compta/pie.php from [485262b8ec] to [f0385456c4].

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
	{
		if ($i++ >= $max)
		{
			$others += $row['somme'];
		}
		else
		{
			$cat = $categories[$row['id_categorie']];
			$pie->add(new \KD2\SVGPie_Data($row['somme'], substr($cat['intitule'], 0, 50), $colors[$i-1]));
		}
	}

	if ($others > 0)
	{
		$pie->add(new \KD2\SVGPie_Data($others, 'Autres', '#ccc'));
	}

	Static_Cache::store('pie_' . $graph, $pie->output());
}

header('Content-Type: image/svg+xml');
Static_Cache::display('pie_' . $graph);







|
|













36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
	{
		if ($i++ >= $max)
		{
			$others += $row['somme'];
		}
		else
		{
			$cat = $categories[$row->id_categorie];
			$pie->add(new \KD2\SVGPie_Data($row->somme, substr($cat->intitule, 0, 50), $colors[$i-1]));
		}
	}

	if ($others > 0)
	{
		$pie->add(new \KD2\SVGPie_Data($others, 'Autres', '#ccc'));
	}

	Static_Cache::store('pie_' . $graph, $pie->output());
}

header('Content-Type: image/svg+xml');
Static_Cache::display('pie_' . $graph);

Modified src/www/admin/config/donnees.php from [b1d0abed2c] to [fd934fbfc9].

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
<?php
namespace Garradin;

require_once __DIR__ . '/_inc.php';

$s = new Sauvegarde;
$code = $error = false;

if (Utils::post('config'))
{
    if (!Utils::CSRF_check('backup_config'))
    {

        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';

    }
    else

    {
        try {
            $config->set('frequence_sauvegardes', Utils::post('frequence_sauvegardes'));
            $config->set('nombre_sauvegardes', Utils::post('nombre_sauvegardes'));
            $config->save();

            Utils::redirect('/admin/config/donnees.php?ok=config');
        } catch (UserException $e) {
            $error = $e->getMessage();
        }
    }
}
elseif (Utils::post('create'))
{
    if (!Utils::CSRF_check('backup_create'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    else
    {
        try {
            $s->create();
            Utils::redirect('/admin/config/donnees.php?ok=create');
        } catch (UserException $e) {
            $error = $e->getMessage();
        }
    }
}
elseif (Utils::post('download'))
{
    if (!Utils::CSRF_check('backup_download'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    else
    {
        header('Content-type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $config->get('nom_asso') . ' - Sauvegarde données - ' . date('Y-m-d') . '.sqlite"');
        header('Content-Length: ' . $s->getDBSize());

        $s->dump();
        exit;
    }
}
elseif (Utils::post('restore'))
{
    if (!Utils::CSRF_check('backup_manage'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    else
    {
        try {
            $r = $s->restoreFromLocal(Utils::post('file'));
            Utils::redirect('/admin/config/donnees.php?ok=restore&code=' . (int)$r);
        } catch (UserException $e) {
            $error = $e->getMessage();
        }
    }
}
elseif (Utils::post('remove'))
{
    if (!Utils::CSRF_check('backup_manage'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    else
    {
        try {
            $s->remove(Utils::post('file'));
            Utils::redirect('/admin/config/donnees.php?ok=remove');
        } catch (UserException $e) {
            $error = $e->getMessage();
        }
    }
}
elseif (Utils::post('restore_file'))
{
    if (!Utils::CSRF_check('backup_restore'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    else
    {
        // Ignorer la vérification d'intégrité si autorisé et demandé
        $check = (ALLOW_MODIFIED_IMPORT && Utils::post('force_import')) ? false : true;

        try {
            $r = $s->restoreFromUpload($_FILES['file'], $user->id, $check);
            Utils::redirect('/admin/config/donnees.php?ok=restore&code=' . (int)$r);
        } catch (UserException $e) {
            $error = $e->getMessage();
            $code = $e->getCode();
        }
    }
}

$tpl->assign('error', $error);
$tpl->assign('code', $code);
$tpl->assign('ok_code', qg('code'));
$tpl->assign('ok', qg('ok'));
$tpl->assign('liste', $s->getList());
$tpl->assign('max_file_size', Utils::getMaxUploadSize());

$tpl->assign('db_size', $s->getDBSize());
$tpl->assign('files_size', $s->getDBFilesSize());

$tpl->display('admin/config/donnees.tpl');






|

|

|
<
>
|
>
|
<
>


|
|




|



|

|
|
|
<
<





|



|

|
|
|
<
<









|

|
|
|
<
<


|


|



|

|
|
|
<
<


|


|



|

|
|
|
<
<


|





|





<










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
<?php
namespace Garradin;

require_once __DIR__ . '/_inc.php';

$s = new Sauvegarde;
$code = false;

if (f('config'))
{
    $form->check('backup_config', [

        'frequence_sauvegardes' => 'present|numeric|min:0|max:365',
        'nombre_sauvegardes' => 'present|numeric|min:1|max:90',
    ]);


    if (!$form->hasErrors())
    {
        try {
            $config->set('frequence_sauvegardes', f('frequence_sauvegardes'));
            $config->set('nombre_sauvegardes', f('nombre_sauvegardes'));
            $config->save();

            Utils::redirect('/admin/config/donnees.php?ok=config');
        } catch (UserException $e) {
            $form->addError($e->getMessage());
        }
    }
}
elseif (f('create'))
{
    $form->check('backup_create');

    if (!$form->hasErrors())


    {
        try {
            $s->create();
            Utils::redirect('/admin/config/donnees.php?ok=create');
        } catch (UserException $e) {
            $form->addError($e->getMessage());
        }
    }
}
elseif (f('download'))
{
    $form->check('backup_download');

    if (!$form->hasErrors())


    {
        header('Content-type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $config->get('nom_asso') . ' - Sauvegarde données - ' . date('Y-m-d') . '.sqlite"');
        header('Content-Length: ' . $s->getDBSize());

        $s->dump();
        exit;
    }
}
elseif (f('restore'))
{
    $form->check('backup_manage');

    if (!$form->hasErrors())


    {
        try {
            $r = $s->restoreFromLocal(f('file'));
            Utils::redirect('/admin/config/donnees.php?ok=restore&code=' . (int)$r);
        } catch (UserException $e) {
            $form->addError($e->getMessage());
        }
    }
}
elseif (f('remove'))
{
    $form->check('backup_manage');

    if (!$form->hasErrors())


    {
        try {
            $s->remove(f('file'));
            Utils::redirect('/admin/config/donnees.php?ok=remove');
        } catch (UserException $e) {
            $form->addError($e->getMessage());
        }
    }
}
elseif (f('restore_file'))
{
    $form->check('backup_restore');

    if (!$form->hasErrors())


    {
        // Ignorer la vérification d'intégrité si autorisé et demandé
        $check = (ALLOW_MODIFIED_IMPORT && f('force_import')) ? false : true;

        try {
            $r = $s->restoreFromUpload($_FILES['file'], $user->id, $check);
            Utils::redirect('/admin/config/donnees.php?ok=restore&code=' . (int)$r);
        } catch (UserException $e) {
            $form->addError($e->getMessage());
            $code = $e->getCode();
        }
    }
}


$tpl->assign('code', $code);
$tpl->assign('ok_code', qg('code'));
$tpl->assign('ok', qg('ok'));
$tpl->assign('liste', $s->getList());
$tpl->assign('max_file_size', Utils::getMaxUploadSize());

$tpl->assign('db_size', $s->getDBSize());
$tpl->assign('files_size', $s->getDBFilesSize());

$tpl->display('admin/config/donnees.tpl');

Modified src/www/admin/config/membres.php from [8a6c35e7ca] to [5ecc83e171].

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
<?php
namespace Garradin;

require_once __DIR__ . '/_inc.php';

$error = false;

$membres = new Membres;

// Restauration de ce qui était en session


if ($champs = $session->sessionGet('champs_membres'))
{




    $champs = new Membres\Champs($champs);







}
else
{
    // Il est nécessaire de créer une nouvelle instance ici, sinon
    // l'enregistrement des modifs ne marchera pas car les deux instances seront identiques.
    // Càd si on utilise directement l'instance de $config, elle sera modifiée directement
    // du coup quand on essaiera de comparer si ça a changé ça comparera deux fois la même chose
    // donc ça n'aura pas changé forcément.
    $champs = new Membres\Champs($config->get('champs_membres'));
}

if (null !== qg('ok'))
{
    $error = 'OK';
}

if (!empty($_POST['save']) || !empty($_POST['add']) || !empty($_POST['review']) || !empty($_POST['reset']))
{
    if (!Utils::CSRF_check('config_membres'))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    else

    {
        if (!empty($_POST['reset']))

        {
            $session->sessionStore('champs_membres', null);
            Utils::redirect('/admin/config/membres.php');
        }
        elseif (!empty($_POST['review']))

        {
            try {
                $nouveau_champs = Utils::post('champs');

                foreach ($nouveau_champs as $key=>&$cfg)
                {
                    $cfg['type'] = $champs->get($key, 'type');
                }
                
                $champs->setAll($nouveau_champs);
                $session->sessionStore('champs_membres', (string)$champs);

                Utils::redirect('/admin/config/membres.php?review');
            }
            catch (UserException $e)
            {
                $error = $e->getMessage();
            }
        }
        elseif (!empty($_POST['add']))
        {
            try {
                if (Utils::post('preset'))
                {
                    $presets = Membres\Champs::listUnusedPresets($champs);
                    if (!array_key_exists(Utils::post('preset'), $presets))
                    {
                        throw new UserException('Le champ pré-défini demandé ne fait pas partie des champs disponibles.');
                    }

                    $champs->add(Utils::post('preset'), $presets[Utils::post('preset')]);
                }
                elseif (Utils::post('new'))
                {
                    $presets = Membres\Champs::importPresets();
                    $new = Utils::post('new');

                    if (array_key_exists($new, $presets))
                    {
                        throw new UserException('Le champ personnalisé ne peut avoir le même nom qu\'un champ pré-défini.');
                    }

                    $config = [
                        'type'  =>  Utils::post('new_type'),
                        'title' =>  Utils::post('new_title'),
                        'editable'  =>  true,
                        'mandatory' =>  false,
                    ];

                    if ($config['type'] == 'select' || $config['type'] == 'multiple')
                    {
                        $config['options'] = ['Première option'];
                    }

                    $champs->add($new, $config);
                }

                $session->sessionStore('champs_membres', (string) $champs);

                Utils::redirect('/admin/config/membres.php?added');
            }
            catch (UserException $e)
            {
                $error = $e->getMessage();
            }
        }
        elseif (!empty($_POST['save']))
        {
            try {
                $champs->save();
                $session->sessionStore('champs_membres', null);
                Utils::redirect('/admin/config/membres.php?ok');
            }
            catch (UserException $e)
            {
                $error = $e->getMessage();
            }
        }
    }
}


$tpl->assign('error', $error);


$tpl->assign('review', null !== qg('review') ? true : false);

$types = $champs->getTypes();

$tpl->assign('champs', $champs->getAll());
$tpl->assign('types', $types);
$tpl->assign('presets', Membres\Champs::listUnusedPresets($champs));
$tpl->assign('new', Utils::post('new'));

$tpl->register_modifier('get_type', function ($type) use ($types) {
    return $types[$type];
});

$tpl->assign('csrf_name', Utils::CSRF_field_name('config_membres'));
$tpl->assign('csrf_value', Utils::CSRF_create('config_membres'));

$tpl->assign('title', 'Configuration — ' . (null !== qg('review') ? 'Confirmer les changements' : 'Fiche membres'));

$tpl->display('admin/config/membres.tpl');





<
<


|
>
>
|
|
>
>
>
>
|
>
>
>
>
>
>
>











|

<
<
<
<
<
|
|
<
<
<
>

<
>

<


<
>


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|


|




|

|


|







|
|












|
<
<



|


|



<




|




>
|
|
>
>
|






|





<
<
<



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
<?php
namespace Garradin;

require_once __DIR__ . '/_inc.php';



$membres = new Membres;

// Restauration de ce qui était 
if ($champs = f('champs'))
{
    if (is_string($champs))
    {
        $champs = json_decode($champs, true);
    }

    try {
        $champs = new Membres\Champs($champs);
    }
    catch (UserException $e)
    {
        $champs = new Membres\Champs($config->get('champs_membres'));
        unset($_POST['review']);
        $form->addError($e->getMessage());
    }
}
else
{
    // Il est nécessaire de créer une nouvelle instance ici, sinon
    // l'enregistrement des modifs ne marchera pas car les deux instances seront identiques.
    // Càd si on utilise directement l'instance de $config, elle sera modifiée directement
    // du coup quand on essaiera de comparer si ça a changé ça comparera deux fois la même chose
    // donc ça n'aura pas changé forcément.
    $champs = new Membres\Champs($config->get('champs_membres'));
}

if (f('save') || f('add') || f('review') || f('reset'))
{





    $form->check('config_membres');




    if (!$form->hasErrors())
    {

        if (f('reset'))
        {

            Utils::redirect('/admin/config/membres.php');
        }

        elseif (f('add'))
        {
            try {




















                if (f('preset'))
                {
                    $presets = Membres\Champs::listUnusedPresets($champs);
                    if (!array_key_exists(f('preset'), $presets))
                    {
                        throw new UserException('Le champ pré-défini demandé ne fait pas partie des champs disponibles.');
                    }

                    $champs->add(f('preset'), $presets[f('preset')]);
                }
                elseif (f('new'))
                {
                    $presets = Membres\Champs::importPresets();
                    $new = f('new');

                    if (array_key_exists($new, $presets))
                    {
                        throw new UserException('Le champ personnalisé ne peut avoir le même nom qu\'un champ pré-défini.');
                    }

                    $config = [
                        'type'  =>  f('new_type'),
                        'title' =>  f('new_title'),
                        'editable'  =>  true,
                        'mandatory' =>  false,
                    ];

                    if ($config['type'] == 'select' || $config['type'] == 'multiple')
                    {
                        $config['options'] = ['Première option'];
                    }

                    $champs->add($new, $config);
                }

                $tpl->assign('status', 'ADDED');


            }
            catch (UserException $e)
            {
                $form->addError($e->getMessage());
            }
        }
        elseif (f('save'))
        {
            try {
                $champs->save();

                Utils::redirect('/admin/config/membres.php?ok');
            }
            catch (UserException $e)
            {
                $form->addError($e->getMessage());
            }
        }
    }
}
else
{
    $tpl->assign('status', null !== qg('ok'));
}

$tpl->assign('review', (bool) f('review'));

$types = $champs->getTypes();

$tpl->assign('champs', $champs->getAll());
$tpl->assign('types', $types);
$tpl->assign('presets', Membres\Champs::listUnusedPresets($champs));
$tpl->assign('new', f('new'));

$tpl->register_modifier('get_type', function ($type) use ($types) {
    return $types[$type];
});




$tpl->assign('title', 'Configuration — ' . (null !== qg('review') ? 'Confirmer les changements' : 'Fiche membres'));

$tpl->display('admin/config/membres.tpl');

Modified src/www/admin/config/plugins.php from [c5914916c1] to [bbcf5eb592].

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
<?php

namespace Garradin;

require_once __DIR__ . '/_inc.php';

$error = false;

if (!empty($_POST['install']))
{
    if (!Utils::CSRF_check('install_plugin'))


    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    elseif (trim(Utils::post('to_install')) === '')
    {
        $error = 'Aucun plugin sélectionné.';
    }
    else
    {
        try {
            Plugin::install(Utils::post('to_install'), false);
            
            Utils::redirect('/admin/config/plugins.php');
        }
        catch (UserException $e)
        {
            $error = $e->getMessage();
        }
    }
}

if (Utils::post('delete'))
{
    if (!Utils::CSRF_check('delete_plugin_' . qg('delete')))
    {
        $error = 'Une erreur est survenue, merci de renvoyer le formulaire.';
    }
    else
    {
        try {
            $plugin = new Plugin(qg('delete'));
            $plugin->uninstall();
            
            Utils::redirect('/admin/config/plugins.php');
        }
        catch (UserException $e)
        {
            $error = $e->getMessage();
        }
    }
}

$tpl->assign('error', $error);

if (qg('delete'))
{
    $plugin = new Plugin(qg('delete'));
    $tpl->assign('plugin', $plugin->getInfos());
    $tpl->assign('delete', true);
}
else
{
    $tpl->assign('liste_telecharges', Plugin::listDownloaded());
    $tpl->assign('liste_installes', Plugin::listInstalled());
}

$tpl->display('admin/config/plugins.tpl');






<
<
|

|
>
>
|
|
<
<

<
<
<
<

|
<




|




|
|
|
|
|
|
|









|




<
<













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
<?php

namespace Garradin;

require_once __DIR__ . '/_inc.php';



if (f('install'))
{
    $form->check('install_plugins', [
        'plugin' => 'required',
    ]);

    if (!$form->hasErrors())


    {




        try {
            Plugin::install(f('to_install'), false);

            Utils::redirect('/admin/config/plugins.php');
        }
        catch (UserException $e)
        {
            $form->addError($e->getMessage());
        }
    }
}

if (f('delete'))
{
    $form->check('delete_plugin_' . qg('delete'), [
        'plugin' => 'required',
    ]);

    if (!$form->hasErrors())
    {
        try {
            $plugin = new Plugin(qg('delete'));
            $plugin->uninstall();
            
            Utils::redirect('/admin/config/plugins.php');
        }
        catch (UserException $e)
        {
            $form->addError($e->getMessage());
        }
    }
}



if (qg('delete'))
{
    $plugin = new Plugin(qg('delete'));
    $tpl->assign('plugin', $plugin->getInfos());
    $tpl->assign('delete', true);
}
else
{
    $tpl->assign('liste_telecharges', Plugin::listDownloaded());
    $tpl->assign('liste_installes', Plugin::listInstalled());
}

$tpl->display('admin/config/plugins.tpl');

Modified src/www/admin/install.php from [58ff9c90a2] to [42229607cb].

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
{
    $tpl->assign('disabled', true);
}
else
{
    $tpl->assign('disabled', false);

    if (!empty($_POST['save']))
    {
        $form->check('install', [
            'nom_asso'     => 'required',
            'email_asso'   => 'required|email',
            'nom_membre'   => 'required',
            'email_membre' => 'required|email',
            'passe'        => 'confirmed|required',







|







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
{
    $tpl->assign('disabled', true);
}
else
{
    $tpl->assign('disabled', false);

    if (f('save'))
    {
        $form->check('install', [
            'nom_asso'     => 'required',
            'email_asso'   => 'required|email',
            'nom_membre'   => 'required',
            'email_membre' => 'required|email',
            'passe'        => 'confirmed|required',

Modified src/www/admin/membres/cotisations/ajout.php from [f220d5298d] to [4c34f02825].

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
$m_cotisations = new Membres\Cotisations;

$cats = new Compta\Categories;
$banques = new Compta\Comptes_Bancaires;

$error = false;

if (f('add') && $membre)
{
    $form->check('add_cotisation', [
        'date'          => 'date_format:Y-m-d|required',
        'id_cotisation' => 'numeric|required|in_table:cotisations,id',
        'id_membre'     => 'numeric|required|in_table:membres,id',
    ]);

    if (!$form->hasErrors())
    {
        try {







            $data = [
                'date'           =>  f('date'),
                'id_cotisation'  =>  f('id_cotisation'),
                'id_membre'      =>  f('id_membre'),
                'id_auteur'      =>  $user->id,
                'montant'        =>  f('montant'),
                'moyen_paiement' =>  f('moyen_paiement'),
                'numero_cheque'  =>  f('numero_cheque'),
                'banque'         =>  f('banque'),
            ];

            $m_cotisations->add($data);

            Utils::redirect('/admin/membres/cotisations.php?id=' . (int)f('id_membre'));
        }
        catch (UserException $e)
        {
            $form->addError($e->getMessage());
        }
    }
}







|










>
>
>
>
>
>
>



|









|







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
$m_cotisations = new Membres\Cotisations;

$cats = new Compta\Categories;
$banques = new Compta\Comptes_Bancaires;

$error = false;

if (f('add'))
{
    $form->check('add_cotisation', [
        'date'          => 'date_format:Y-m-d|required',
        'id_cotisation' => 'numeric|required|in_table:cotisations,id',
        'id_membre'     => 'numeric|required|in_table:membres,id',
    ]);

    if (!$form->hasErrors())
    {
        try {
            $id_membre = f('id_membre');

            if (!$id_membre && f('numero_membre'))
            {
                $id_membre = (new Membres)->getIDWithNumero(f('numero_membre'));
            }

            $data = [
                'date'           =>  f('date'),
                'id_cotisation'  =>  f('id_cotisation'),
                'id_membre'      =>  $id_membre,
                'id_auteur'      =>  $user->id,
                'montant'        =>  f('montant'),
                'moyen_paiement' =>  f('moyen_paiement'),
                'numero_cheque'  =>  f('numero_cheque'),
                'banque'         =>  f('banque'),
            ];

            $m_cotisations->add($data);

            Utils::redirect('/admin/membres/cotisations.php?id=' . $id_membre);
        }
        catch (UserException $e)
        {
            $form->addError($e->getMessage());
        }
    }
}

Modified src/www/admin/membres/index.php from [1aa05ee07f] to [983da9689e].

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

	if (null !== qg('d'))
	    $desc = true;

	$fields = $champs->getListedFields();

	// Vérifier que le champ de tri existe bien dans la table
	if ($order != 'id' && !isset($fields->$order))
	{
		// Sinon par défaut c'est le premier champ de la table qui fait le tri
		$order = $champs->getFirstListed();
	}

	$tpl->assign('order', $order);
	$tpl->assign('desc', $desc);







|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

	if (null !== qg('d'))
	    $desc = true;

	$fields = $champs->getListedFields();

	// Vérifier que le champ de tri existe bien dans la table
	if (!isset($fields->$order))
	{
		// Sinon par défaut c'est le premier champ de la table qui fait le tri
		$order = $champs->getFirstListed();
	}

	$tpl->assign('order', $order);
	$tpl->assign('desc', $desc);

Modified src/www/admin/upgrade.php from [2713b28f11] to [1d7fc72053].

264
265
266
267
268
269
270








271
272
273
274
275
276
277

    $db->begin();

    $db->import(ROOT . '/include/data/0.8.0.sql');

    $db->commit();









    // Nettoyage de la base de données
    $db->exec('VACUUM;');
}

Utils::clearCaches();

$config->setVersion(garradin_version());







>
>
>
>
>
>
>
>







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

    $db->begin();

    $db->import(ROOT . '/include/data/0.8.0.sql');

    $db->commit();

    // Ajout champ numéro de membre
    $champs = (array) Config::getInstance()->get('champs_membres')->getAll();
    $presets = Membres\Champs::importPresets();

    // Ajout du numéro au début
    $champs = array_merge(['numero' => $presets['numero']], $champs);
    (new Membres\Champs($champs))->save();

    // Nettoyage de la base de données
    $db->exec('VACUUM;');
}

Utils::clearCaches();

$config->setVersion(garradin_version());