Overview
Comment:Dédoublement du champ ID en deux champs : ID (interne) et numéro de membre (champ membre modifiable)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 3dfb698790ba0063bba7346aa1a6a3cbd04c7435
User & Date: bohwaz on 2017-08-09 06:59:58
Other Links: branch diff | manifest | tags
Context
2017-08-09
07:19
Utilisation du numéro plutôt que l'ID pour import/export de membre check-in: 6877253ad2 user: bohwaz tags: dev
06:59
Dédoublement du champ ID en deux champs : ID (interne) et numéro de membre (champ membre modifiable) check-in: 3dfb698790 user: bohwaz tags: dev
05:02
Modernisation installation, renomme repasse en passe_confirmed check-in: d71d56990e 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
;		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







>
>
>
>
>
>
>
>






|







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
;		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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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








|






|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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

    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
        {







>

|
<



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







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

    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
        {
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
    }

    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







<
<
|
<










|

|




|



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







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
    }

    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
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 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')







>
>
>
>
>








|







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 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
	{
		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)
            {







>
>
>


>
>
>
>
>
>
>
>
>
>

|
>
|
<







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
	{
		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)
            {
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
            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);







<







155
156
157
158
159
160
161

162
163
164
165
166
167
168
            return true;
        else
            return false;
    }

	public function getAll()
	{

		return $this->champs;
	}

    public function getList($with_id = false)
    {
        $champs = clone $this->champs;
        unset($champs->passe);
421
422
423
424
425
426
427





428
429
430
431
432
433
434
            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))
        {







>
>
>
>
>







433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
            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))
        {
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
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

        $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 . ',';

            if (!empty($cfg->title))
            {
                $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);







|
|
|
|
|
|







|
<
<

















|

>
>
>
>
>





|







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
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551

        $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 . ',';

            if (!empty($cfg->title))
            {
                $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);
540
541
542
543
544
545
546





547
548
549
550
551
552
553
            // 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'))
            {







>
>
>
>
>







560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
            // 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
314
315
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);
		}

		if (strtolower(trim($check)) != 'ok')
		{
			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
314
315
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);
		}

		if (strtolower(trim($check)) != 'ok')
		{
			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
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{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">
                <strong>Vous n'êtes pas administrateur dans cette sauvegarde.</strong> Garradin a donné les droits d'administration à toutes les catégories afin d'empêcher de ne plus pouvoir se connecter.
                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>




|
|
|
|
|
|
>
|















|







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
{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">
                <strong>Vous n'êtes pas administrateur dans cette sauvegarde.</strong> Garradin a donné les droits d'administration à toutes les catégories afin d'empêcher de ne plus pouvoir se connecter.
                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>
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
    <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>







|










|







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
    <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>
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
            <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 class="help">
        Une sauvegarde des données courantes sera effectuée avant le remplacement,
        en cas de besoin d'annuler cette restauration.
    </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"}







|










|


















|









107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
            <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 class="help">
        Une sauvegarde des données courantes sera effectuée avant le remplacement,
        en cas de besoin d'annuler cette restauration.
    </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
{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.




<
|



|



<
<
<
<
|
|
>







1
2
3
4

5
6
7
8
9
10
11
12




13
14
15
16
17
18
19
20
21
22
{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.
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
            {/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}">







>











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











|







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
            {/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}">
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
                    {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">







|







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
                    {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">
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
            <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">







|







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
            <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">
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
        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'))
        {







|














|







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
        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
{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">




<
<
|
<
<







1
2
3
4


5


6
7
8
9
10
11
12
{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">
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

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







|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

        <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());