Overview
Comment:Fusion des changements et correctifs effectués dans le trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: c0a510c18ccc47ffa348224a80092970a1f02f51
User & Date: bohwaz on 2019-09-23 21:30:20
Other Links: branch diff | manifest | tags
Context
2019-12-17
16:02
Merge avec trunk check-in: 12a00549c1 user: bohwaz tags: dev
2019-09-23
21:30
Fusion des changements et correctifs effectués dans le trunk check-in: c0a510c18c user: bohwaz tags: dev
20:47
Mise à jour version check-in: 1d2b0786d4 user: bohwaz tags: trunk, stable, 0.9.3
2019-03-08
17:03
Ajout entité compte bancaire check-in: 80bb6a9ff8 user: bohwaz tags: dev
Changes

Modified src/Makefile from [054872df85] to [7b30622c74].

16
17
18
19
20
21
22
23
24
25
26



27
28
29


30
31
32
33
34
35

36
37
38
39
	php -S localhost:8082 -t www www/_route.php

test:
	find . -name '*.php' -print0 | xargs -0 -n1 php -l > /dev/null

release: test
	$(eval VERSION=$(shell cat VERSION))
	fossil ls | grep '^src/' | sed 's/src/garradin-${VERSION}/' > /tmp/garradin-${VERSION}-list.txt
	#echo "garradin-${VERSION}/include/lib/KD2" >> /tmp/garradin-${VERSION}-list.txt
	cat include/lib/dependencies.list | sed -r "s/^/garradin-${VERSION}\/include\/lib\//" >> /tmp/garradin-${VERSION}-list.txt
	rm -f /tmp/garradin-${VERSION}



	ln -s ${PWD} /tmp/garradin-${VERSION}
	tar cjvfh garradin-${VERSION}.tar.bz2 -C /tmp -T /tmp/garradin-${VERSION}-list.txt
	rm -f /tmp/garradin-${VERSION}*



publish: release
	$(eval VERSION=$(shell cat VERSION))
	fossil uv sync
	fossil uv ls | fgrep -v 'garradin-0.8.5' | grep '^garradin-.*\.tar\.bz2' | xargs fossil uv rm
	fossil uv add garradin-${VERSION}.tar.bz2

	fossil uv sync

check-dependencies:
	grep -hEo '^use \\?KD2\\\w+|\\KD2\\\w+' -R include/lib/Garradin www | sed -r 's/^use \\?KD2\\|^\\KD2\\//' | sort | uniq







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

|


|

>




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
	php -S localhost:8082 -t www www/_route.php

test:
	find . -name '*.php' -print0 | xargs -0 -n1 php -l > /dev/null

release: test
	$(eval VERSION=$(shell cat VERSION))
	rm -rf /tmp/garradin-build
	mkdir -p /tmp/garradin-build
	fossil zip ${VERSION} /tmp/garradin-build/src.zip --name garradin
	unzip -d /tmp/garradin-build /tmp/garradin-build/src.zip
	cd include/lib; rsync --files-from=dependencies.list -r ./ /tmp/garradin-build/garradin/src/include/lib/
	mv /tmp/garradin-build/garradin/src /tmp/garradin-build/garradin-${VERSION}
	@#cd /tmp/garradin-build/; zip -r -9 garradin-${VERSION}.zip garradin-${VERSION};
	@#mv -f /tmp/garradin-build/garradin-${VERSION}.zip ./
	tar cjvfh garradin-${VERSION}.tar.bz2 -C /tmp/garradin-build garradin-${VERSION}

deb:
	cd ../debian; ./makedeb.sh

publish: release deb
	$(eval VERSION=$(shell cat VERSION))
	fossil uv sync
#	fossil uv ls | fgrep -v 'garradin-0.8.5' | grep -E '^garradin-.*\.(tar\.bz2|deb)' | xargs fossil uv rm
	fossil uv add garradin-${VERSION}.tar.bz2
	cd ../debian && fossil uv add garradin-${VERSION}-*.deb
	fossil uv sync

check-dependencies:
	grep -hEo '^use \\?KD2\\\w+|\\KD2\\\w+' -R include/lib/Garradin www | sed -r 's/^use \\?KD2\\|^\\KD2\\//' | sort | uniq

Modified src/VERSION from [ef2e10f968] to [a416ca4e18].

1
0.9.2
|
1
0.9.3

Modified src/config.dist.php from [dcde08c471] to [1e67c15c59].

281
282
283
284
285
286
287





























 * soit à l'affichage de la page d'accueil (si nécessaire).
 *
 * Voir paramètre USE_CRON aussi
 *
 * Défaut : true
 */
const ENABLE_AUTOMATIC_BACKUPS = true;




































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
 * soit à l'affichage de la page d'accueil (si nécessaire).
 *
 * Voir paramètre USE_CRON aussi
 *
 * Défaut : true
 */
const ENABLE_AUTOMATIC_BACKUPS = true;


/**
 * Couleur primaire de l'interface admin par défaut
 * (peut être personnalisée dans la configuration)
 *
 * Défaut : #9c4f15
 */
//const ADMIN_COLOR1 = '#20787a';

/**
 * Couleur secondaire de l'interface admin
 * Défaut : #d98628
 */
//const ADMIN_COLOR2 = '#85b9ba';

/**
 * Image de fond par défaut de l'interface admin
 *
 * Cette URL doit être absolue (http://...) ou relative à l'admin (/admin/static...)
 *
 * Attention si l'image est sur un domaine différent vous devrez activer l'entête CORS:
 * Access-Control-Allow-Origin "*"
 *
 * sinon la personnalisation des couleurs ne fonctionnera pas
 *
 * Défaut : [ADMIN_URL]static/gdin_bg.png
 */
//const ADMIN_BACKGROUND_IMAGE = 'http://mon-asso.fr/fond_garradin.png';

Modified src/include/data/0.8.0.sql from [d1406fb553] to [34e1625504].

62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
INSERT INTO compta_categories SELECT * FROM compta_categories_old;
INSERT INTO compta_comptes_bancaires SELECT * FROM compta_comptes_bancaires_old;
INSERT INTO compta_exercices SELECT * FROM compta_exercices_old;
INSERT INTO compta_journal SELECT *, NULL FROM compta_journal_old;
INSERT INTO compta_rapprochement SELECT * FROM compta_rapprochement_old;
INSERT INTO fichiers SELECT * FROM fichiers_old;
INSERT INTO membres_operations SELECT * FROM membres_operations_old;
INSERT INTO membres_categories SELECT * FROM membres_categories_old;


-- Suppression des anciennes tables
DROP TABLE cotisations_membres_old;
DROP TABLE rappels_old;
DROP TABLE rappels_envoyes_old;
DROP TABLE wiki_pages_old;
DROP TABLE wiki_revisions_old;







|
>







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
INSERT INTO compta_categories SELECT * FROM compta_categories_old;
INSERT INTO compta_comptes_bancaires SELECT * FROM compta_comptes_bancaires_old;
INSERT INTO compta_exercices SELECT * FROM compta_exercices_old;
INSERT INTO compta_journal SELECT *, NULL FROM compta_journal_old;
INSERT INTO compta_rapprochement SELECT * FROM compta_rapprochement_old;
INSERT INTO fichiers SELECT * FROM fichiers_old;
INSERT INTO membres_operations SELECT * FROM membres_operations_old;
INSERT INTO membres_categories SELECT id, nom, droit_wiki, droit_membres, droit_compta,
	droit_inscription, droit_connexion, droit_config, cacher, id_cotisation_obligatoire FROM membres_categories_old;

-- Suppression des anciennes tables
DROP TABLE cotisations_membres_old;
DROP TABLE rappels_old;
DROP TABLE rappels_envoyes_old;
DROP TABLE wiki_pages_old;
DROP TABLE wiki_revisions_old;

Modified src/include/data/champs_membres.ini from [e4dcbd25bd] to [f5ad8e1d8e].

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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

[passe]
; ce champ est obligatoirement présent et de type 'password'
; le titre ne peut être modifié
type = password







|


|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
title = "Nom & prénom"
mandatory = true
install = true
editable = true
list_row = 2

[email]
; ce champ est facultatif et de type 'email'
type = email
title = "Adresse E-Mail"
mandatory = false
install = true
editable = true

[passe]
; ce champ est obligatoirement présent et de type 'password'
; le titre ne peut être modifié
type = password

Modified src/include/init.php from [0f77762847] to [f08b2c5f34].

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
    'SMTP_USER'             => null,
    'SMTP_PASSWORD'         => null,
    'SMTP_PORT'             => 587,
    'SMTP_SECURITY'         => 'STARTTLS',
    'ADMIN_URL'             => WWW_URL . 'admin/',
    'NTP_SERVER'            => 'fr.pool.ntp.org',
    'ENABLE_AUTOMATIC_BACKUPS' => true,


];

foreach ($default_config as $const => $value)
{
    $const = sprintf('Garradin\\%s', $const);

    if (!defined($const))
    {
        define($const, $value);
    }
}





const WEBSITE = 'https://garradin.eu/';
const PLUGINS_URL = 'https://garradin.eu/plugins/list.json';
const DEFAULT_REPORT_URL = 'http://henga.test/report/?p=ABCD';


// PHP devrait être assez intelligent pour chopper la TZ système mais nan
// il sait pas faire (sauf sur Debian qui a le bon patch pour ça), donc pour 
// éviter le message d'erreur à la con on définit une timezone par défaut
// Pour utiliser une autre timezone, il suffit de définir date.timezone dans
// un .htaccess ou dans config.local.php
if (!ini_get('date.timezone'))







>
>











>
>
>
>



|
>







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
    'SMTP_USER'             => null,
    'SMTP_PASSWORD'         => null,
    'SMTP_PORT'             => 587,
    'SMTP_SECURITY'         => 'STARTTLS',
    'ADMIN_URL'             => WWW_URL . 'admin/',
    'NTP_SERVER'            => 'fr.pool.ntp.org',
    'ENABLE_AUTOMATIC_BACKUPS' => true,
    'ADMIN_COLOR1'          => '#9c4f15',
    'ADMIN_COLOR2'          => '#d98628',
];

foreach ($default_config as $const => $value)
{
    $const = sprintf('Garradin\\%s', $const);

    if (!defined($const))
    {
        define($const, $value);
    }
}

if (!defined('Garradin\\ADMIN_BACKGROUND_IMAGE')) {
    define('Garradin\\ADMIN_BACKGROUND_IMAGE', ADMIN_URL . 'static/gdin_bg.png');
}

const WEBSITE = 'https://garradin.eu/';
const PLUGINS_URL = 'https://garradin.eu/plugins/list.json';
#const DEFAULT_REPORT_URL = 'http://henga.test/report/?p=ABCD';
const DEFAULT_REPORT_URL = null;

// PHP devrait être assez intelligent pour chopper la TZ système mais nan
// il sait pas faire (sauf sur Debian qui a le bon patch pour ça), donc pour 
// éviter le message d'erreur à la con on définit une timezone par défaut
// Pour utiliser une autre timezone, il suffit de définir date.timezone dans
// un .htaccess ou dans config.local.php
if (!ini_get('date.timezone'))

Modified src/include/lib/Garradin/Compta/Categories.php from [1997e6a481] to [aacf281b4b].

106
107
108
109
110
111
112
113
114
115

116







117
118
119
120
121
122
123
            FROM compta_categories AS cat INNER JOIN compta_comptes AS cc
                ON cc.id = cat.compte
            WHERE %s ORDER BY cat.intitule;', $where);

        return $db->getGrouped($query);
    }

    public function listMoyensPaiement()
    {
        $db = DB::getInstance();

        return $db->getGrouped('SELECT code, nom FROM compta_moyens_paiement ORDER BY nom COLLATE NOCASE;');







    }

    public function getMoyenPaiement($code)
    {
        $db = DB::getInstance();
        return $db->firstColumn('SELECT nom FROM compta_moyens_paiement WHERE code = ?;', $code);
    }







|


>
|
>
>
>
>
>
>
>







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
            FROM compta_categories AS cat INNER JOIN compta_comptes AS cc
                ON cc.id = cat.compte
            WHERE %s ORDER BY cat.intitule;', $where);

        return $db->getGrouped($query);
    }

    public function listMoyensPaiement($assoc = false)
    {
        $db = DB::getInstance();

        $query = 'SELECT code, nom FROM compta_moyens_paiement ORDER BY nom COLLATE NOCASE;';

        if ($assoc) {
            return $db->getAssoc($query);
        }
        else {
            return $db->getGrouped($query);
        }
    }

    public function getMoyenPaiement($code)
    {
        $db = DB::getInstance();
        return $db->firstColumn('SELECT nom FROM compta_moyens_paiement WHERE code = ?;', $code);
    }

Modified src/include/lib/Garradin/Compta/Comptes.php from [8786b38bca] to [9bf36bdeef].

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
    const CARTE_A_ENCAISSER = '5115';

    const PASSIF = 0x01;
    const ACTIF = 0x02;
    const PRODUIT = 0x04;
    const CHARGE = 0x08;











    public function importPlan()
    {








        $plan = json_decode(file_get_contents(\Garradin\ROOT . '/include/data/plan_comptable.json'));






        $db = DB::getInstance();
        $db->begin();
        $ids = [];

        foreach ($plan as $id=>$compte)
        {
            $ids[] = $id;

            if ($db->test('compta_comptes', $db->where('id', $id)))
            {
                $db->update('compta_comptes', [
                    'parent'    =>  $compte->parent,
                    'libelle'   =>  $compte->nom,
                    'position'  =>  $compte->position,
                    'plan_comptable' => 1,

                ], $db->where('id', $id));
            }
            else
            {
                $db->insert('compta_comptes', [
                    'id'        =>  $id,
                    'parent'    =>  $compte->parent,
                    'libelle'   =>  $compte->nom,
                    'position'  =>  $compte->position,
                    'plan_comptable' => 1,

                ]);
            }
        }

        // Supprime les comptes qui étaient dans l'ancien plan comptable
        // mais pas dans le nouveau










        $db->delete('compta_comptes', $db->where('id', 'NOT IN', $ids) . ' AND ' . $db->where('plan_comptable', 1));


        $db->commit();





























        return true;
    }

    public function add($data)
    {
        $this->_checkFields($data, true);








>
>
>
>
>
>
>
>
>
>
|

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















|
>









|
>




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


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







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
    const CARTE_A_ENCAISSER = '5115';

    const PASSIF = 0x01;
    const ACTIF = 0x02;
    const PRODUIT = 0x04;
    const CHARGE = 0x08;

    /**
     * Importe un plan comptable
     * @param  string $source_file Chemin du fichier à importer.
     * @param  boolean $delete_all True active la suppression des tous les anciens comptes (peu importe plan_comptable)
     * @return boolean/array Retourne un array des comptes non-supprimés avec leur raison, s'il y en a. Sinon true.
     *
     * Accepte 0 ou 1 argument : soit un chemin, soit true.
     * Sans arguments : importe le plan par défaut et ne supprime que les comptes
     * plus présent appartenants au plan d'origine (WHERE plan_comptable = 1)
     */
    public function importPlan($source_file = null, $delete_all = false)
    {
        $reset = false;

        if(null == $source_file)
        {
            $reset = true;
            $source_file = \Garradin\ROOT . '/include/data/plan_comptable.json';
        }

        $plan = json_decode(file_get_contents($source_file));

        if(is_null($plan))
        {
            throw new UserException('Le fichier n\'est pas du JSON ou n\'a pas pu être décodé.');
        }

        $db = DB::getInstance();
        $db->begin();
        $ids = [];

        foreach ($plan as $id=>$compte)
        {
            $ids[] = $id;

            if ($db->test('compta_comptes', $db->where('id', $id)))
            {
                $db->update('compta_comptes', [
                    'parent'    =>  $compte->parent,
                    'libelle'   =>  $compte->nom,
                    'position'  =>  $compte->position,
                    'plan_comptable' => $reset || !empty($compte->plan_comptable) ? 1 : 0,
                    'desactive' => !empty($compte->desactive) ? 1 : 0,
                ], $db->where('id', $id));
            }
            else
            {
                $db->insert('compta_comptes', [
                    'id'        =>  $id,
                    'parent'    =>  $compte->parent,
                    'libelle'   =>  $compte->nom,
                    'position'  =>  $compte->position,
                    'plan_comptable' => $reset || !empty($compte->plan_comptable) ? 1 : 0,
                    'desactive' => !empty($compte->desactive) ? 1 : 0,
                ]);
            }
        }

        // Effacer les comptes du plan comptable s'ils ne sont pas utilisés ailleurs
        // et qu'ils ne sont pas dans le nouveau plan comptable qu'on vient d'importer
        $sql = 'DELETE FROM compta_comptes WHERE id NOT IN (
            SELECT id FROM compta_comptes_bancaires
            UNION SELECT compte_credit FROM compta_journal
            UNION SELECT compte_debit FROM compta_journal
            UNION SELECT id FROM compta_categories)
            AND '. $db->where('id', 'NOT IN', $ids);

        // Si on ne fait qu'importer une mise à jour du plan comptable,
        // ne supprimer que les comptes qui n'ont pas été créés par l'usager
        if (!$delete_all) {
            $sql .= ' AND ' . $db->where('plan_comptable', 1);
        }

        $db->commit();

        return true;
    }

    public function exportPlan()
    {
        $name = 'plan_comptable';

        header('Content-type: application/json');
        header(sprintf('Content-Disposition: attachment; filename="%s.json"', $name));

        $liste = $this->listTree(0, true);

        $export = [];

        foreach ($liste as $k => $v)
        {
            $export[$v->id] = [
                'code'           => $v->id,
                'nom'            => $v->libelle,
                'parent'         => $v->parent,
                'position'       => $v->position,
                'plan_comptable' => $v->plan_comptable,
                'desactive'      => $v->desactive,
            ];
        }

        file_put_contents('php://output', json_encode($export, JSON_PRETTY_PRINT));

        return true;
    }

    public function add($data)
    {
        $this->_checkFields($data, true);

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
            $parent = false;
            $id = $new_id;

            // Vérification que c'est bien le bon parent !
            // Sinon risque par exemple d'avoir parent = 5 et id = 512A !
            while (!$parent && strlen($id))
            {
            	// On enlève un caractère à la fin jusqu'à trouver un compte parent
            	$id = substr($id, 0, -1);
            	$parent = $db->firstColumn('SELECT id FROM compta_comptes WHERE id = ?;', $id);
            }

            if (!$parent || $parent != $data['parent'])
            {
            	throw new UserException('Le compte parent sélectionné est incorrect, par exemple pour créer un compte 512A il faut sélectionner 512 comme compte parent.');
            }
        }

        // Vérification que le compte n'existe pas déjà
        if ($db->test('compta_comptes', 'id = ?', $new_id))
        {
        	throw new UserException('Ce numéro de compte existe déjà dans le plan comptable : ' . $new_id);
        }

        if (isset($data['position']))
        {
            $position = (int) $data['position'];
        }
        else







|
|
|




|






|







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
            $parent = false;
            $id = $new_id;

            // Vérification que c'est bien le bon parent !
            // Sinon risque par exemple d'avoir parent = 5 et id = 512A !
            while (!$parent && strlen($id))
            {
                // On enlève un caractère à la fin jusqu'à trouver un compte parent
                $id = substr($id, 0, -1);
                $parent = $db->firstColumn('SELECT id FROM compta_comptes WHERE id = ?;', $id);
            }

            if (!$parent || $parent != $data['parent'])
            {
                throw new UserException('Le compte parent sélectionné est incorrect, par exemple pour créer un compte 512A il faut sélectionner 512 comme compte parent.');
            }
        }

        // Vérification que le compte n'existe pas déjà
        if ($db->test('compta_comptes', 'id = ?', $new_id))
        {
            throw new UserException('Ce numéro de compte existe déjà dans le plan comptable : ' . $new_id);
        }

        if (isset($data['position']))
        {
            $position = (int) $data['position'];
        }
        else

Modified src/include/lib/Garradin/Compta/Import.php from [d8cc440c1c] to [2b6a446835].

85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
		$db = DB::getInstance();
		$db->begin();
		$cats = new Categories;
		$journal = new Journal;

		$columns = array_flip($this->header);
		$liste_cats = $db->getAssoc('SELECT intitule, id FROM compta_categories;');

		$liste_moyens = $cats->listMoyensPaiement();

		$col = function($column) use (&$row, &$columns)
		{
			if (!isset($columns[$column]))
				return null;

			if (!isset($row[$columns[$column]]))







>
|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
		$db = DB::getInstance();
		$db->begin();
		$cats = new Categories;
		$journal = new Journal;

		$columns = array_flip($this->header);
		$liste_cats = $db->getAssoc('SELECT intitule, id FROM compta_categories;');
		// Liste des moyens sous la forme nom -> code
		$liste_moyens = array_flip($cats->listMoyensPaiement(true));

		$col = function($column) use (&$row, &$columns)
		{
			if (!isset($columns[$column]))
				return null;

			if (!isset($row[$columns[$column]]))
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

			if ($line === 1)
			{
				if (trim($row[0]) != 'Numéro mouvement')
				{
					throw new UserException('Erreur sur la ligne ' . $line . ' : l\'entête des colonnes est absent ou incorrect.');
				}
				
				continue;
			}
	
			if (count($row) != count($columns))
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : le nombre de colonnes est incorrect.');
			}

			if (trim($row[0]) !== '' && !is_numeric($row[0]))







|


|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

			if ($line === 1)
			{
				if (trim($row[0]) != 'Numéro mouvement')
				{
					throw new UserException('Erreur sur la ligne ' . $line . ' : l\'entête des colonnes est absent ou incorrect.');
				}

				continue;
			}

			if (count($row) != count($columns))
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : le nombre de colonnes est incorrect.');
			}

			if (trim($row[0]) !== '' && !is_numeric($row[0]))
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

174







175
176
177
178
179
180
181
			{
				continue;
			}

			$debit = $col('Compte de débit - numéro');
			$credit = $col('Compte de crédit - numéro');

			if (trim($debit) == '' && trim($credit) != '')
			{
				$debit = null;
			}
			elseif (trim($debit) != '' && trim($credit) == '')
			{
				$credit = null;
			}

			$cat = $col('Catégorie');
			$moyen = strtoupper(substr($col('Moyen de paiement'), 0, 2));


			if (!$moyen || !array_key_exists($moyen, $liste_moyens))







			{
				$moyen = false;
				$cat = false;
			}

			if ($cat && !array_key_exists($cat, $liste_cats))
			{







<
<
<
<
<
<
<
<
<



>
|
>
>
>
>
>
>
>







156
157
158
159
160
161
162









163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
			{
				continue;
			}

			$debit = $col('Compte de débit - numéro');
			$credit = $col('Compte de crédit - numéro');










			$cat = $col('Catégorie');
			$moyen = strtoupper(substr($col('Moyen de paiement'), 0, 2));

			// Association du moyen de paiement par nom
			if ($moyen && array_key_exists($moyen, $liste_moyens))
			{
				$moyen = $liste_moyens[$moyen];
			}

			// Vérification de l'existence du moyen de paiement
			// s'il n'est pas valide, on ne peut pas avoir de catégorie non plus
			if (!$moyen || !in_array($moyen, $liste_moyens, true))
			{
				$moyen = false;
				$cat = false;
			}

			if ($cat && !array_key_exists($cat, $liste_cats))
			{
300
301
302
303
304
305
306





307
308
309
310
311
312
313
			if (empty($row))
			{
				continue;
			}

			if (empty($columns))
			{





				$columns = $row;
				$columns = array_flip($columns);
				continue;
			}

			$date = $col('Date');
			$date = \DateTime::createFromFormat('d/m/Y', $date);







>
>
>
>
>







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
			if (empty($row))
			{
				continue;
			}

			if (empty($columns))
			{
				if (empty($row[0]))
				{
					throw new UserException(sprintf('Erreur sur la ligne %d : la ligne est vide ?', $line));
				}

				$columns = $row;
				$columns = array_flip($columns);
				continue;
			}

			$date = $col('Date');
			$date = \DateTime::createFromFormat('d/m/Y', $date);

Modified src/include/lib/Garradin/Compta/Journal.php from [2a385b6769] to [88e9a2c624].

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    {
        if (is_null($id))
            return true;

        return DB::getInstance()->test('compta_exercices', 'cloture = 0 AND id = ?', (int)$id);
    }

    public function getSolde($id_compte, $inclure_sous_comptes = false)
    {
        $db = DB::getInstance();
        $exercice = $this->_getCurrentExercice();
        $compte = $inclure_sous_comptes
            ? 'LIKE \'' . $db->escapeString(trim($id_compte)) . '%\''
            : '= \'' . $db->escapeString(trim($id_compte)) . '\'';

        $debit = 'COALESCE((SELECT SUM(montant) FROM compta_journal WHERE compte_debit '.$compte.' AND id_exercice = '.(int)$exercice.'), 0)';
        $credit = 'COALESCE((SELECT SUM(montant) FROM compta_journal WHERE compte_credit '.$compte.' AND id_exercice = '.(int)$exercice.'), 0)';








|


|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    {
        if (is_null($id))
            return true;

        return DB::getInstance()->test('compta_exercices', 'cloture = 0 AND id = ?', (int)$id);
    }

    public function getSolde($id_compte, $inclure_sous_comptes = false, $exercice = null)
    {
        $db = DB::getInstance();
        $exercice = (int) $exercice ?: $this->_getCurrentExercice();
        $compte = $inclure_sous_comptes
            ? 'LIKE \'' . $db->escapeString(trim($id_compte)) . '%\''
            : '= \'' . $db->escapeString(trim($id_compte)) . '\'';

        $debit = 'COALESCE((SELECT SUM(montant) FROM compta_journal WHERE compte_debit '.$compte.' AND id_exercice = '.(int)$exercice.'), 0)';
        $credit = 'COALESCE((SELECT SUM(montant) FROM compta_journal WHERE compte_credit '.$compte.' AND id_exercice = '.(int)$exercice.'), 0)';

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
        {
            $query = $credit . ' - ' . $debit;
        }

        return $db->firstColumn('SELECT ' . $query . ';');
    }

    public function getJournalCompte($compte, $inclure_sous_comptes = false)
    {
        $db = DB::getInstance();

        $position = $db->firstColumn('SELECT position FROM compta_comptes WHERE id = ?;', $compte);

        $exercice = $this->_getCurrentExercice();
        $compte = $inclure_sous_comptes
            ? 'LIKE \'' . $db->escapeString(trim($compte)) . '%\''
            : '= \'' . $db->escapeString(trim($compte)) . '\'';

        // L'actif et les charges augmentent au débit, le passif et les produits au crédit
        if (($position & Comptes::ACTIF) || ($position & Comptes::CHARGE))
        {







|





|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
        {
            $query = $credit . ' - ' . $debit;
        }

        return $db->firstColumn('SELECT ' . $query . ';');
    }

    public function getJournalCompte($compte, $inclure_sous_comptes = false, $exercice = null)
    {
        $db = DB::getInstance();

        $position = $db->firstColumn('SELECT position FROM compta_comptes WHERE id = ?;', $compte);

        $exercice = (int) $exercice ?: $this->_getCurrentExercice();
        $compte = $inclure_sous_comptes
            ? 'LIKE \'' . $db->escapeString(trim($compte)) . '%\''
            : '= \'' . $db->escapeString(trim($compte)) . '\'';

        // L'actif et les charges augmentent au débit, le passif et les produits au crédit
        if (($position & Comptes::ACTIF) || ($position & Comptes::CHARGE))
        {
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
            }
            else
            {
                $data[$champ] = trim($data[$champ]);
            }
        }

        if (!array_key_exists('compte_debit', $data) || 
            (!is_null($data['compte_debit']) && 
                !$db->test('compta_comptes', $db->where('id', $data['compte_debit']))))
        {
            throw new UserException('Compte débité inconnu.');
        }

        if (!array_key_exists('compte_credit', $data) || 
            (!is_null($data['compte_credit']) && 
                !$db->test('compta_comptes', $db->where('id', $data['compte_credit']))))
        {
            throw new UserException('Compte crédité inconnu.');
        }

        $data['compte_credit'] = is_null($data['compte_credit']) ? null : strtoupper(trim($data['compte_credit']));
        $data['compte_debit'] = is_null($data['compte_debit']) ? null : strtoupper(trim($data['compte_debit']));

        if ($data['compte_credit'] == $data['compte_debit'])
        {
            throw new UserException('Compte crédité identique au compte débité.');
        }

        if (isset($data['id_categorie']))







<
<
|




<
<
|




|
|







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
            }
            else
            {
                $data[$champ] = trim($data[$champ]);
            }
        }



        if (empty($data['compte_debit']) || !$db->test('compta_comptes', $db->where('id', $data['compte_debit'])))
        {
            throw new UserException('Compte débité inconnu.');
        }



        if (empty($data['compte_credit']) || !$db->test('compta_comptes', $db->where('id', $data['compte_credit'])))
        {
            throw new UserException('Compte crédité inconnu.');
        }

        $data['compte_credit'] = strtoupper(trim($data['compte_credit']));
        $data['compte_debit'] = strtoupper(trim($data['compte_debit']));

        if ($data['compte_credit'] == $data['compte_debit'])
        {
            throw new UserException('Compte crédité identique au compte débité.');
        }

        if (isset($data['id_categorie']))

Modified src/include/lib/Garradin/Form.php from [e13d9531e0] to [5a5ce58fea].

122
123
124
125
126
127
128


129
130
131
132
133
134
135
136
137
138
				return sprintf('La vérification du champ %s n\'est pas identique au champ lui-même.', $element);
			case 'date_format':
				return sprintf('Format de date invalide dans le champ %s.', $element);
			case 'numeric':
				return sprintf('Le champ %s doit être un nombre.', $element);
			case 'money':
				return sprintf('Le champ %s n\'est pas un nombre valide.', $element);


			default:
				return sprintf('Erreur "%s" dans le champ "%s"', $rule, $element);
		}
	}

	public function __invoke($key)
	{
		return \KD2\Form::get($key);
	}
}







>
>










122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
				return sprintf('La vérification du champ %s n\'est pas identique au champ lui-même.', $element);
			case 'date_format':
				return sprintf('Format de date invalide dans le champ %s.', $element);
			case 'numeric':
				return sprintf('Le champ %s doit être un nombre.', $element);
			case 'money':
				return sprintf('Le champ %s n\'est pas un nombre valide.', $element);
			case 'in':
				return sprintf('Valeur invalide dans le champ \'%s\'.', $element);
			default:
				return sprintf('Erreur "%s" dans le champ "%s"', $rule, $element);
		}
	}

	public function __invoke($key)
	{
		return \KD2\Form::get($key);
	}
}

Modified src/include/lib/Garradin/Membres.php from [89209054f9] to [84b4e80ec3].

325
326
327
328
329
330
331
332
333









334
335
336
337
338
339
340
341
        return $fields;
    }

    public function sendMessage(array $recipients, $subject, $message, $send_copy)
    {
        $config = Config::getInstance();

        foreach ($recipients as $recipient)
        {









            if (!SMTP::checkEmailIsValid($recipient->email, true))
            {
                throw new UserException(sprintf('Adresse email invalide : "%s". Aucun message n\'a été envoyé.', $recipient->email));
            }
        }

        foreach ($recipients as $recipient)
        {







|

>
>
>
>
>
>
>
>
>
|







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
        return $fields;
    }

    public function sendMessage(array $recipients, $subject, $message, $send_copy)
    {
        $config = Config::getInstance();

        foreach ($recipients as $key => $recipient)
        {
            // Ignorer les destinataires avec une adresse email vide
            if (empty($recipient->email))
            {
                unset($recipients[$key]);
                continue;
            }

            // Refuser d'envoyer un mail à une adresse invalide, sans vérifier le MX
            // sinon ça serait trop lent
            if (!SMTP::checkEmailIsValid($recipient->email, false))
            {
                throw new UserException(sprintf('Adresse email invalide : "%s". Aucun message n\'a été envoyé.', $recipient->email));
            }
        }

        foreach ($recipients as $recipient)
        {

Modified src/include/lib/Garradin/Membres/Champs.php from [a027cb81e3] to [9b116040de].

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

            $rules = [];

            if (!empty($config->mandatory) && !($name == 'passe' && $mode != 'create'))
            {
                $rules[] = 'required';
            }
            
            if ($config->type == 'email')
            {
                $rules[] = 'email';
            }
            elseif ($config->type == 'url')
            {
                $rules[] = 'url';
            }
            elseif ($config->type == 'date')
            {
                $rules[] = 'date_format:Y-m-d';
            }
            elseif ($config->type == 'date')
            {
                $rules[] = 'date_format:Y-m-d H\:i';
            }
            elseif ($config->type == 'number' || $config->type == 'multiple')
            {
                $rules[] = 'numeric';
            }
            elseif ($config->type == 'select')
            {
                $rules[] = 'in:' . range(0, count($config->options) - 1);
            }
            elseif ($config->type == 'checkbox')
            {
                $rules[] = 'boolean';
            }

            if ($name == 'passe')
            {







|




















<
<
<
<







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252




253
254
255
256
257
258
259

            $rules = [];

            if (!empty($config->mandatory) && !($name == 'passe' && $mode != 'create'))
            {
                $rules[] = 'required';
            }

            if ($config->type == 'email')
            {
                $rules[] = 'email';
            }
            elseif ($config->type == 'url')
            {
                $rules[] = 'url';
            }
            elseif ($config->type == 'date')
            {
                $rules[] = 'date_format:Y-m-d';
            }
            elseif ($config->type == 'date')
            {
                $rules[] = 'date_format:Y-m-d H\:i';
            }
            elseif ($config->type == 'number' || $config->type == 'multiple')
            {
                $rules[] = 'numeric';
            }




            elseif ($config->type == 'checkbox')
            {
                $rules[] = 'boolean';
            }

            if ($name == 'passe')
            {

Modified src/include/lib/Garradin/Membres/Cotisations.php from [86b67dc8e7] to [cf23856e38].

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
		if ($co->id_categorie_compta)
		{
			$membre = (new Membres)->getNom($data['id_membre']);

			try {
				$data_compta = array_merge($data_compta, [
					'id_categorie' => $co->id_categorie_compta,
					'libelle'      => 'Cotisation - ' . $membre,
					'date'         => $data['date'],
					'id_auteur'    => $data['id_auteur'],
					'id_membre'    => $data['id_membre'],
				]);

				$id_operation = $this->addOperationCompta($id, $data_compta);
			}







|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
		if ($co->id_categorie_compta)
		{
			$membre = (new Membres)->getNom($data['id_membre']);

			try {
				$data_compta = array_merge($data_compta, [
					'id_categorie' => $co->id_categorie_compta,
					'libelle'      => sprintf('%s - %s', $co->intitule, $membre),
					'date'         => $data['date'],
					'id_auteur'    => $data['id_auteur'],
					'id_membre'    => $data['id_membre'],
				]);

				$id_operation = $this->addOperationCompta($id, $data_compta);
			}
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
	}

	/**
	 * Liste des membres qui sont inscrits à une cotisation
	 * @param  integer $id Numéro de la cotisation
	 * @return array     Liste des membres
	 */
	public function listMembersForCotisation($id, $page = 1, $order = null, $desc = true)
	{
		$begin = ($page - 1) * self::ITEMS_PER_PAGE;

		$db = DB::getInstance();
		$champ_id = Config::getInstance()->get('champ_identite');

		if (empty($order))







|







253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
	}

	/**
	 * Liste des membres qui sont inscrits à une cotisation
	 * @param  integer $id Numéro de la cotisation
	 * @return array     Liste des membres
	 */
	public function listMembersForCotisation($id, $include_category, $page = 1, $order = null, $desc = true)
	{
		$begin = ($page - 1) * self::ITEMS_PER_PAGE;

		$db = DB::getInstance();
		$champ_id = Config::getInstance()->get('champ_identite');

		if (empty($order))
279
280
281
282
283
284
285




















286
287
288
289
290

291
292
293
294
295
296
297
				break;
			default:
				$order = 'cm.id_membre';
				break;
		}

		$desc = $desc ? 'DESC' : 'ASC';





















		return $db->get('SELECT cm.id_membre, cm.date, cm.id, m.numero,
			m.'.$champ_id.' AS nom, c.montant,
			CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date()
			WHEN c.fin IS NOT NULL THEN (date() <= c.fin AND date() >= c.debut) ELSE 1 END AS a_jour

			FROM cotisations_membres AS cm
				INNER JOIN cotisations AS c ON c.id = cm.id_cotisation
				INNER JOIN membres AS m ON m.id = cm.id_membre
			WHERE
				cm.id_cotisation = ?
				AND m.id_categorie NOT IN (SELECT mc.id FROM membres_categories AS mc WHERE mc.cacher = 1)
			GROUP BY cm.id_membre ORDER BY '.$order.' '.$desc.' LIMIT ?,?;',







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




|
>







279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
				break;
			default:
				$order = 'cm.id_membre';
				break;
		}

		$desc = $desc ? 'DESC' : 'ASC';

		// Renvoyer la liste avec tous les membres des catégories dont la cotisation obligatoire est celle-ci
		if ($include_category)
		{
			$cats_obligatoires = $db->getAssoc('SELECT id, id FROM membres_categories WHERE id_cotisation_obligatoire = ? AND cacher = 0;', $id);

			return $db->get('SELECT m.id AS id_membre, cm.date, cm.id, m.numero,
				m.'.$champ_id.' AS nom, c.montant,
				CASE WHEN cm.id IS NULL THEN 0
				WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date()
				WHEN c.fin IS NOT NULL THEN (cm.date <= c.fin AND cm.date >= c.debut)
				ELSE 1 END AS a_jour
				FROM membres AS m
					LEFT JOIN cotisations_membres AS cm ON cm.id_membre = m.id AND cm.id_cotisation = ?
					LEFT JOIN cotisations AS c ON c.id = cm.id_cotisation
				WHERE
					'.$db->where('m.id_categorie', $cats_obligatoires) . '
				GROUP BY m.id ORDER BY '.$order.' '.$desc.' LIMIT ?,?;',
				$id, $begin, self::ITEMS_PER_PAGE);
		}

		return $db->get('SELECT cm.id_membre, cm.date, cm.id, m.numero,
			m.'.$champ_id.' AS nom, c.montant,
			CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date()
			WHEN c.fin IS NOT NULL THEN (cm.date <= c.fin AND cm.date >= c.debut)
			ELSE 1 END AS a_jour
			FROM cotisations_membres AS cm
				INNER JOIN cotisations AS c ON c.id = cm.id_cotisation
				INNER JOIN membres AS m ON m.id = cm.id_membre
			WHERE
				cm.id_cotisation = ?
				AND m.id_categorie NOT IN (SELECT mc.id FROM membres_categories AS mc WHERE mc.cacher = 1)
			GROUP BY cm.id_membre ORDER BY '.$order.' '.$desc.' LIMIT ?,?;',
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
	 * @return array     Liste des cotisations en cours de validité
	 */
	public function listSubscriptionsForMember($id)
	{
		$db = DB::getInstance();
		return $db->get('SELECT c.*,
			CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date()
			WHEN c.fin IS NOT NULL THEN (cm.id IS NOT NULL AND date() <= c.fin AND date() >= c.debut)
			WHEN cm.id IS NOT NULL THEN 1 ELSE 0 END AS a_jour,
			CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\')
			WHEN c.fin IS NOT NULL THEN c.fin ELSE 1 END AS expiration,
			(julianday(date()) - julianday(CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\')
			WHEN c.fin IS NOT NULL THEN c.fin END)) AS nb_jours
			FROM cotisations_membres AS cm
				INNER JOIN cotisations AS c ON c.id = cm.id_cotisation
			WHERE cm.id_membre = ?
				AND ((c.fin IS NOT NULL AND date() <= c.fin AND date() >= c.debut) OR c.fin IS NULL)
			GROUP BY cm.id_cotisation
			ORDER BY cm.date DESC;', (int)$id);
	}

	/**
	 * Ce membre est-il à jour sur cette cotisation ?
	 * @param  integer  $id             Numéro de membre







|








|







344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
	 * @return array     Liste des cotisations en cours de validité
	 */
	public function listSubscriptionsForMember($id)
	{
		$db = DB::getInstance();
		return $db->get('SELECT c.*,
			CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date()
			WHEN c.fin IS NOT NULL THEN (cm.id IS NOT NULL AND cm.date <= c.fin AND cm.date >= c.debut)
			WHEN cm.id IS NOT NULL THEN 1 ELSE 0 END AS a_jour,
			CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\')
			WHEN c.fin IS NOT NULL THEN c.fin ELSE 1 END AS expiration,
			(julianday(date()) - julianday(CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\')
			WHEN c.fin IS NOT NULL THEN c.fin END)) AS nb_jours
			FROM cotisations_membres AS cm
				INNER JOIN cotisations AS c ON c.id = cm.id_cotisation
			WHERE cm.id_membre = ?
				AND ((c.fin IS NOT NULL AND cm.date <= c.fin AND cm.date >= c.debut) OR c.fin IS NULL)
			GROUP BY cm.id_cotisation
			ORDER BY cm.date DESC;', (int)$id);
	}

	/**
	 * Ce membre est-il à jour sur cette cotisation ?
	 * @param  integer  $id             Numéro de membre

Modified src/include/lib/Garradin/Membres/Import.php from [db87ef00f2] to [7b54b35f93].

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
			if (empty($row))
			{
				continue;
			}

			if ($line == 1)
			{
				if (is_numeric($row[0]))
				{
					$db->rollback();
					throw new UserException('Erreur sur la ligne 1 : devrait contenir l\'en-tête des colonnes.');
				}

				$columns = array_flip($row);
				continue;
			}

			if (count($row) != count($columns))
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : le nombre de colonnes est incorrect.');
			}

			$data = [];

			foreach ($columns as $name=>$id)
			{
				$name = trim($name);
				
				// Champs qui n'existent pas dans le schéma actuel
				if (!in_array($name, $champs))
					continue;

				if (trim($row[$id]) !== '')
					$data[$name] = $row[$id];
			}







|




















|







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
			if (empty($row))
			{
				continue;
			}

			if ($line == 1)
			{
				if (empty($row[0]) || !is_string($row[0]) || is_numeric($row[0]))
				{
					$db->rollback();
					throw new UserException('Erreur sur la ligne 1 : devrait contenir l\'en-tête des colonnes.');
				}

				$columns = array_flip($row);
				continue;
			}

			if (count($row) != count($columns))
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : le nombre de colonnes est incorrect.');
			}

			$data = [];

			foreach ($columns as $name=>$id)
			{
				$name = trim($name);

				// Champs qui n'existent pas dans le schéma actuel
				if (!in_array($name, $champs))
					continue;

				if (trim($row[$id]) !== '')
					$data[$name] = $row[$id];
			}

Modified src/include/lib/Garradin/Recherche.php from [0325ea49c7] to [35a5a3057c].

135
136
137
138
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
			WHERE (id_membre IS NULL OR id_membre = ?) AND cible = ?
			ORDER BY intitule;', (int)$id_membre, $cible);
	}

	/**
	 * Lancer une recherche enregistrée
	 */
	public function search($id, $force_select = null)
	{
		$search = $this->get($id);

		if (!$search)
		{
			return false;
		}

		if ($search->type == self::TYPE_JSON)
		{
			$search->contenu = $this->buildQuery($search->cible, $search->query, $search->order, $search->desc, $search->limit);
		}

		return $this->searchSQL($search->cible, $search->contenu, $force_select);
	}

	/**
	 * Renvoie la liste des colonnes d'une cible
	 */
	public function getColumns($target)
	{







|










|


|







135
136
137
138
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
			WHERE (id_membre IS NULL OR id_membre = ?) AND cible = ?
			ORDER BY intitule;', (int)$id_membre, $cible);
	}

	/**
	 * Lancer une recherche enregistrée
	 */
	public function search($id, array $force_select = null, $no_limit = false)
	{
		$search = $this->get($id);

		if (!$search)
		{
			return false;
		}

		if ($search->type == self::TYPE_JSON)
		{
			$search->contenu = $this->buildQuery($search->cible, $search->query, $search->order, $search->desc, $no_limit ? 10000 : $search->limit);
		}

		return $this->searchSQL($search->cible, $search->contenu, $force_select, $no_limit);
	}

	/**
	 * Renvoie la liste des colonnes d'une cible
	 */
	public function getColumns($target)
	{
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414







415
416
417
418
419
420
421

		return $sql_query;
	}

	/**
	 * Lancer une recherche SQL
	 */
	public function searchSQL($target, $query, $force_select = null)
	{
		if (!in_array($target, self::TARGETS, true))
		{
			throw new \InvalidArgumentException('Cible inconnue : ' . $target);
		}

		if ($force_select)
		{
			$query = preg_replace('/^\s*SELECT.*FROM\s+/Ui', 'SELECT ' . $force_select . ' FROM ', $query);
		}

		if (!preg_match('/LIMIT\s+\d+/i', $query))
		{
			$query = preg_replace('/;?\s*$/', '', $query);
			$query .= ' LIMIT 100';
		}

		try {
			return DB::getInstance()->userSelectGet($query);
		}
		catch (\Exception $e) {
			throw new UserException('Erreur dans la requête : ' . $e->getMessage());







		}
	}

	public function schema($target)
	{
		$db = DB::getInstance();








|






|

|


|









|
>
>
>
>
>
>
>







385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428

		return $sql_query;
	}

	/**
	 * Lancer une recherche SQL
	 */
	public function searchSQL($target, $query, array $force_select = null, $no_limit = false)
	{
		if (!in_array($target, self::TARGETS, true))
		{
			throw new \InvalidArgumentException('Cible inconnue : ' . $target);
		}

		if (null !== $force_select)
		{
			$query = preg_replace('/^\s*SELECT.*FROM\s+/Ui', 'SELECT ' . implode(', ', $force_select) . ' FROM ', $query);
		}

		if (!$no_limit && !preg_match('/LIMIT\s+\d+/i', $query))
		{
			$query = preg_replace('/;?\s*$/', '', $query);
			$query .= ' LIMIT 100';
		}

		try {
			return DB::getInstance()->userSelectGet($query);
		}
		catch (\Exception $e) {
			$message = 'Erreur dans la requête : ' . $e->getMessage();

			if (null !== $force_select)
			{
				$message .= "\nVérifiez que votre requête sélectionne bien les colonnes suivantes : " . implode(', ', $force_select);
			}

			throw new UserException($message);
		}
	}

	public function schema($target)
	{
		$db = DB::getInstance();

Modified src/include/lib/Garradin/Sauvegarde.php from [7513189b78] to [41b6845214].

391
392
393
394
395
396
397
398
399
400
401
402
403
404





405
406
407
408
409
410
411
			$db = DB::getInstance();
			$db->update('membres_categories', [
				'droit_membres' => Membres::DROIT_ADMIN,
				'droit_connexion' => Membres::DROIT_ACCES
			]);
		}

		// Force l'installation de plugin système si non existant dans la sauvegarde existante
		Plugin::checkAndInstallSystemPlugins();

		if ($version != garradin_version())
		{
			$return |= self::NEED_UPGRADE;
		}






		return $return;
	}

	/**
	 * Taille de la base de données actuelle
	 * @return integer Taille en octets du fichier SQLite







<
<
<




>
>
>
>
>







391
392
393
394
395
396
397



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
			$db = DB::getInstance();
			$db->update('membres_categories', [
				'droit_membres' => Membres::DROIT_ADMIN,
				'droit_connexion' => Membres::DROIT_ACCES
			]);
		}




		if ($version != garradin_version())
		{
			$return |= self::NEED_UPGRADE;
		}
		else {
			// Force l'installation de plugin système si non existant dans la sauvegarde existante
			// si une mise à jour est nécessaire, normalement ça sera fait après la mise à jour
			Plugin::checkAndInstallSystemPlugins();
		}

		return $return;
	}

	/**
	 * Taille de la base de données actuelle
	 * @return integer Taille en octets du fichier SQLite

Modified src/include/lib/Garradin/Template.php from [682f3b4c7a] to [3def9bc1cf].

216
217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240



241
242
243
244
245
246
247

248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269


270
271
272
273
274
275
276
		return $n;
	}

	protected function customColors()
	{
		$config = Config::getInstance();

		if (!$config->get('couleur1') || !$config->get('couleur2') || !$config->get('image_fond'))
		{

			return '';
		}

		$couleur1 = implode(', ', sscanf($config->get('couleur1'), '#%02x%02x%02x'));
		$couleur2 = implode(', ', sscanf($config->get('couleur2'), '#%02x%02x%02x'));

		try {
			$f = new Fichiers($config->get('image_fond'));

		}
		catch (\InvalidArgumentException $e)
		{
			// Fichier qui n'existe pas
			return '';
		}

		$image_fond = $f->getURL();




		$out = '
		<style type="text/css">
		:root {
			--gMainColor: %s;
			--gSecondColor: %s;
		}

		.header .menu, body {
			background-image: url("%s");

		}
		</style>';

		return sprintf($out, $couleur1, $couleur2, $image_fond);
	}

	protected function displayChampMembre($v, $config)
	{
		switch ($config->type)
		{
			case 'checkbox':
				return $v ? 'Oui' : 'Non';
			case 'email':
				return '<a href="mailto:' . rawurlencode($v) . '">' . htmlspecialchars($v) . '</a>';
			case 'tel':
				return '<a href="tel:' . rawurlencode($v) . '">' . htmlspecialchars($v) . '</a>';
			case 'url':
				return '<a href="' . htmlspecialchars($v) . '">' . htmlspecialchars($v) . '</a>';
			case 'country':
				return Utils::getCountryName($v);


			case 'multiple':
				$out = [];

				foreach ($config->options as $b => $name)
				{
					if ($v & (0x01 << $b))
						$out[] = $name;







|
<
>
|
|
|
<
<
|
|
|
>
|
|
|
|
<
|
|
|
>
>
>







>
|
|
>




















>
>







216
217
218
219
220
221
222
223

224
225
226
227


228
229
230
231
232
233
234
235

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
		return $n;
	}

	protected function customColors()
	{
		$config = Config::getInstance();

		$couleur1 = $config->get('couleur1') ?: ADMIN_COLOR1;

		$couleur2 = $config->get('couleur2') ?: ADMIN_COLOR2;
		$image_fond = ADMIN_BACKGROUND_IMAGE;

		if ($config->get('image_fond'))


		{
			try {
				$f = new Fichiers($config->get('image_fond'));
				$image_fond = $f->getURL();
			}
			catch (\InvalidArgumentException $e)
			{
				// Fichier qui n'existe pas/plus

			}
		}

		// Transformation Hexa vers décimal
		$couleur1 = implode(', ', sscanf($couleur1, '#%02x%02x%02x'));
		$couleur2 = implode(', ', sscanf($couleur2, '#%02x%02x%02x'));

		$out = '
		<style type="text/css">
		:root {
			--gMainColor: %s;
			--gSecondColor: %s;
		}
		@media screen, handheld {
			.header .menu, body {
				background-image: url("%s");
			}
		}
		</style>';

		return sprintf($out, $couleur1, $couleur2, $image_fond);
	}

	protected function displayChampMembre($v, $config)
	{
		switch ($config->type)
		{
			case 'checkbox':
				return $v ? 'Oui' : 'Non';
			case 'email':
				return '<a href="mailto:' . rawurlencode($v) . '">' . htmlspecialchars($v) . '</a>';
			case 'tel':
				return '<a href="tel:' . rawurlencode($v) . '">' . htmlspecialchars($v) . '</a>';
			case 'url':
				return '<a href="' . htmlspecialchars($v) . '">' . htmlspecialchars($v) . '</a>';
			case 'country':
				return Utils::getCountryName($v);
			case 'date':
				return Utils::sqliteDateToFrench($v);
			case 'multiple':
				$out = [];

				foreach ($config->options as $b => $name)
				{
					if ($v & (0x01 << $b))
						$out[] = $name;

Modified src/include/lib/Garradin/Utils.php from [ac43981659] to [c134309b5d].

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

        $str = preg_replace('#&[^;]+;#', '', $str); // supprime les autres caractères
        $str = preg_replace('![^[:ascii:]]+!', '', $str);

        return $str;
    }

    static public function htmlLinksOnUrls($str)
    {
        return preg_replace_callback('!(?<=\s|^)((?:(ftp|https?|file|ed2k|ircs?)://|(magnet|mailto|data|tel|fax|geo|sips?|xmpp):)([^\s<]+))!',
            function ($match) {
                $proto = $match[2] ?: $match[3];
                $text = ($proto == 'http' || $proto == 'mailto') ? $match[4] : $match[1];
                return '<a class="'.$proto.'" href="'.htmlspecialchars($match[1], ENT_QUOTES, 'UTF-8').'">'.htmlspecialchars($text, ENT_QUOTES, 'UTF-8').'</a>';
            }, $str);
    }

    /**
     * Transforme un texte SkrivML en HTML
     * @param  string $str Texte SkrivML
     * @return string      Texte HTML
     */
    static public function SkrivToHTML($str)
    {







<
<
<
<
<
<
<
<
<
<







297
298
299
300
301
302
303










304
305
306
307
308
309
310

        $str = preg_replace('#&[^;]+;#', '', $str); // supprime les autres caractères
        $str = preg_replace('![^[:ascii:]]+!', '', $str);

        return $str;
    }











    /**
     * Transforme un texte SkrivML en HTML
     * @param  string $str Texte SkrivML
     * @return string      Texte HTML
     */
    static public function SkrivToHTML($str)
    {

Added src/templates/admin/compta/comptes/classe.tpl version [45939485c2].































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
{include file="admin/_head.tpl" title=$classe_compte.libelle current="compta/categories"}

<ul class="actions">
    <li><a href="{$admin_url}compta/comptes/">Liste des classes</a></li>
    <li><a href="{$admin_url}compta/comptes/ajouter.php?classe={$classe}">Ajouter un compte dans cette classe</a></li>
</ul>

<p class="help">
    Les comptes avec la mention <em>*</em> font partie du plan comptable standard
    et ne peuvent être modifiés ou supprimés.
</p>

{if !empty($liste)}
    <table class="list accountList">
    {foreach from=$liste item="compte"}
        <tr class="niveau_{$compte.id|strlen}">
            <th>{$compte.id}</th>
            <td class="libelle">{$compte.libelle}</td>
            <td>
                {if !empty($compte.desactive)}
                    <em>Désactivé</em>
                {else}
                    {$compte.position|get_position}
                {/if}
            </td>
            <td class="actions">
                {if empty($compte.desactive)}
                    {if !$compte.plan_comptable}
                        <a class="icn" href="{$admin_url}compta/comptes/modifier.php?id={$compte.id}" title="Modifier">✎</a>
                        <a class="icn" href="{$admin_url}compta/comptes/supprimer.php?id={$compte.id}" title="Supprimer">✘</a>
                    {else}
                        <em>*</em>
                    {/if}
                {/if}
            </td>
        </tr>
    {/foreach}
    </table>

{else}
    <p class="alert">
        Aucun compte trouvé.
    </p>
{/if}


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

Added src/templates/admin/compta/comptes/import.tpl version [99c4dfd495].

























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
{include file="admin/_head.tpl" title="Plan comptable" current="compta/categories"}

<ul class="actions">
	<li><a href="{$admin_url}compta/comptes/">Plan comptable</a></li>
	<li class="current"><a href="?import">Import / remise à zéro</a></li>
	<li><a href="?export=plan">Exporter le plan en format JSON</a></li>
</ul>

{form_errors}

{if $confirm}
<p class="confirm">
	{if $confirm == 'import'}L'import s'est correctement déroulé.
	{elseif $confirm == 'reset'}Le plan comptable a bien été remis à zéro.{/if}
</p>
{/if}

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

	<fieldset>
		<legend>Importer un plan comptable</legend>
		<p class="help">
			Toute modification actuelle du plan comptable sera perdue.<br />
			Les comptes associés à des écritures ou des comptes bancaires ne seront pas supprimés.
		</p>
		<dl>
			<dt><label for="f_file">Fichier à importer</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
			<dd><input type="file" name="upload" id="f_file" required="required" /></dd>
			<dt><label for="f_type">Format de fichier</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
			<dd>
				<input type="radio" name="format" id="f_format_json" value="json" {*form_field name="format" checked="json"*} checked="checked" />
				<label for="f_format_json">Plan comptable au format JSON de plan comptable Garradin</label>
			</dd>
		</dl>

		<p class="submit">
			{csrf_field key="plan_import"}
			<input type="submit" name="import" value="Importer &rarr;" />
		</p>
	</fieldset>

</form>

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

	<fieldset>
		<legend>Remise à zéro du plan comptable</legend>
		<p class="help">
			Permet de rétablir le plan comptable par défaut de Garradin.<br />
			Vos modifications personnelles seront perdues, assurez-vous d'en avoir une copie avant en cas de problèmes (bouton «&nbsp;Exporter le plan&nbsp;»).
		</p>

		<p class="submit">
			{csrf_field key="plan_reset"}
			<input type="submit" name="reset" value="Rétablir le plan comptable &rarr;" />
		</p>

	</fieldset>

</form>

Modified src/templates/admin/compta/comptes/index.tpl from [452d349397] to [4936874101].

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
{if empty($classe)}
    {include file="admin/_head.tpl" title="Comptes" current="compta/categories"}
    <ul class="accountList">
    {foreach from=$classes item="_classe"}
        <li><h4><a href="{$admin_url}compta/comptes/?classe={$_classe.id}">{$_classe.libelle}</a></h4></li>
    {/foreach}
    </ul>
{else}
    {include file="admin/_head.tpl" title=$classe_compte.libelle current="compta/categories"}

    <ul class="actions">
        <li><a href="{$admin_url}compta/comptes/">Liste des classes</a></li>
        <li><a href="{$admin_url}compta/comptes/ajouter.php?classe={$classe}">Ajouter un compte dans cette classe</a></li>

    </ul>

    <p class="help">
        Les comptes avec la mention <em>*</em> font partie du plan comptable standard
        et ne peuvent être modifiés ou supprimés.
    </p>

    {if !empty($liste)}
        <table class="list accountList">
        {foreach from=$liste item="compte"}
            <tr class="niveau_{$compte.id|strlen}">
                <th>{$compte.id}</th>
                <td class="libelle">{$compte.libelle}</td>
                <td>
                    {if !empty($compte.desactive)}
                        <em>Désactivé</em>
                    {else}
                        {$compte.position|get_position}
                    {/if}
                </td>
                <td class="actions">
                    {if empty($compte.desactive)}
                        {if !$compte.plan_comptable}
                            <a class="icn" href="{$admin_url}compta/comptes/modifier.php?id={$compte.id}" title="Modifier">✎</a>
                            <a class="icn" href="{$admin_url}compta/comptes/supprimer.php?id={$compte.id}" title="Supprimer">✘</a>
                        {else}
                            <em>*</em>
                        {/if}
                    {/if}
                </td>
            </tr>
        {/foreach}
        </table>

    {else}
        <p class="alert">
            Aucun compte trouvé.
        </p>
    {/if}
{/if}

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

|
|
|
>
|

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



1







2
3
4
5
6
7
8
9






10













11







12





13


14
15

{include file="admin/_head.tpl" title="Plan comptable" current="compta/categories"}








<ul class="actions">
	<li class="current"><a href="{$admin_url}compta/comptes/">Plan comptable</a></li>
	<li><a href="?import">Import / remise à zéro</a></li>
	<li><a href="?export=plan">Exporter le plan en format JSON</a></li>
</ul>

<ul class="accountList">






{foreach from=$classes item="_classe"}













	<li><h4><a href="{$admin_url}compta/comptes/?classe={$_classe.id}">{$_classe.libelle}</a></h4></li>







{/foreach}





</ul>



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

Modified src/templates/admin/compta/comptes/journal.tpl from [76d3a0a881] to [03e8b58484].

1
2
3
4
5
6


7
8
9
10
11

















12
13
14
15
16
17
18
{if $suivi}
    {include file="admin/_head.tpl" title="Journal : %s - %s"|args:$compte.id:$compte.libelle current="compta/banques" body_id="rapport"}

    <ul class="actions">
        <li><a href="{$admin_url}compta/banques/">Comptes bancaires</a></li>
        <li><a href="{$admin_url}compta/comptes/journal.php?id={$id_caisse}">Journal de caisse</a></li>


    </ul>
{else}
    {include file="admin/_head.tpl" title="Journal : %s - %s"|args:$compte.id:$compte.libelle current="compta/gestion" body_id="rapport"}
{/if}



















<table class="list">
    <colgroup>
        <col width="3%" />
        <col width="3%" />
        <col width="12%" />
        <col width="10%" />
|




|
>
>





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







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
{if null !== $suivi}
    {include file="admin/_head.tpl" title="Journal : %s - %s"|args:$compte.id:$compte.libelle current="compta/banques" body_id="rapport"}

    <ul class="actions">
        <li><a href="{$admin_url}compta/banques/">Comptes bancaires</a></li>
        <li{if $compte.id == Compta\Comptes::CAISSE} class="current"{/if}><a href="{$admin_url}compta/comptes/journal.php?id={$id_caisse}&amp;suivi">Journal de caisse</a></li>
        <li{if $compte.id == Compta\Comptes::CHEQUE_A_ENCAISSER} class="current"{/if}><a href="{$admin_url}compta/comptes/journal.php?id={$id_cheque_a_encaisser}&amp;suivi">Chèques à encaisser</a></li>
        <li{if $compte.id == Compta\Comptes::CARTE_A_ENCAISSER} class="current"{/if}><a href="{$admin_url}compta/comptes/journal.php?id={$id_carte_a_encaisser}&amp;suivi">Paiements par carte à encaisser</a></li>
    </ul>
{else}
    {include file="admin/_head.tpl" title="Journal : %s - %s"|args:$compte.id:$compte.libelle current="compta/gestion" body_id="rapport"}
{/if}

{if count($exercices)}
    <form action="{$self_url_no_qs}" method="get" class="shortFormRight">
        <fieldset>
            <legend><label for="f_exercice">Afficher le journal de l'exercice suivant :</label></legend>
            <p>
                <select name="exercice" id="f_exercice" onchange="this.form.submit();">
                {foreach from=$exercices item="exercice"}
                    <option value="{$exercice.id}"{if $exercice_selectionne == $exercice.id} selected="selected"{/if}>{$exercice.libelle}</option>
                {/foreach}
                </select>
                <input type="hidden" name="id" value="{$compte.id}" />
                {if null !== $suivi}<input type="hidden" name="suivi" value=""/>{/if}
                <noscript><input type="submit" value="Afficher"/></noscript>
            </p>
        </fieldset>
    </form>
{/if}

<table class="list">
    <colgroup>
        <col width="3%" />
        <col width="3%" />
        <col width="12%" />
        <col width="10%" />

Modified src/templates/admin/config/index.tpl from [6078c55f63] to [f4c6ed1cb3].

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
        <legend>Personnalisation de l'interface</legend>
        <dl>
            <dt><label for="f_couleur1">Couleur principale</label></dt>
            <dd><input type="color" pattern="#[a-f0-9]{ldelim}6{rdelim}" title="Couleur au format hexadécimal" placeholder="{$couleurs_defaut[0]}" name="couleur1" value="{form_field name=couleur1 default=$couleur1}" id="f_couleur1" /></dd>
            <dt><label for="f_couleur2">Couleur secondaire</label></dt>
            <dd><input type="color" pattern="#[a-f0-9]{ldelim}6{rdelim}" title="Couleur au format hexadécimal" placeholder="{$couleurs_defaut[1]}" name="couleur2" value="{form_field name=couleur2 default=$couleur2}" id="f_couleur2" /></dd>
        </dl>
        <input type="hidden" name="image_fond" id="f_image_fond" value="{form_field name=image_fond}" />
    </fieldset>

    <p class="submit">
        {csrf_field key="config"}
        <input type="submit" name="save" value="Enregistrer &rarr;" />
    </p>

</form>

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







|










108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
        <legend>Personnalisation de l'interface</legend>
        <dl>
            <dt><label for="f_couleur1">Couleur principale</label></dt>
            <dd><input type="color" pattern="#[a-f0-9]{ldelim}6{rdelim}" title="Couleur au format hexadécimal" placeholder="{$couleurs_defaut[0]}" name="couleur1" value="{form_field name=couleur1 default=$couleur1}" id="f_couleur1" /></dd>
            <dt><label for="f_couleur2">Couleur secondaire</label></dt>
            <dd><input type="color" pattern="#[a-f0-9]{ldelim}6{rdelim}" title="Couleur au format hexadécimal" placeholder="{$couleurs_defaut[1]}" name="couleur2" value="{form_field name=couleur2 default=$couleur2}" id="f_couleur2" /></dd>
        </dl>
        <input type="hidden" name="image_fond" id="f_image_fond" data-source="{$background_image_source}" value="{form_field name=image_fond}" />
    </fieldset>

    <p class="submit">
        {csrf_field key="config"}
        <input type="submit" name="save" value="Enregistrer &rarr;" />
    </p>

</form>

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

Modified src/templates/admin/membres/cotisations/voir.tpl from [3ab24d7fdd] to [79533ebb65].

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
        {elseif $cotisation.debut}
            du {$cotisation.debut|format_sqlite_date_to_french} au {$cotisation.fin|format_sqlite_date_to_french}
        {else}
            ponctuelle
        {/if}
        — {$cotisation.montant|escape|html_money} {$config.monnaie}
    </dd>






    <dt>Nombre de membres ayant cotisé</dt>
    <dd>
        {$cotisation.nb_membres}
        <small class="help">(incluant les membres des catégories cachées)</small>
    </dd>
</dl>

{if !empty($liste)}
    <table class="list">
        <thead class="userOrder">
            <tr>
                <td class="{if $order == "id"} cur {if $desc}desc{else}asc{/if}{/if}"><a href="?id={$cotisation.id}&amp;o=id&amp;a" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=id&amp;d" class="icn dn">&darr;</a></td>
                <th class="{if $order == "identite"} cur {if $desc}desc{else}asc{/if}{/if}">Membre <a href="?id={$cotisation.id}&amp;o=identite&amp;a" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=identite&amp;d" class="icn dn">&darr;</a></th>
                <td class="{if $order == "a_jour"} cur {if $desc}desc{else}asc{/if}{/if}">Statut <a href="?id={$cotisation.id}&amp;o=a_jour&amp;a" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=a_jour&amp;d" class="icn dn">&darr;</a></td>
                <td class="{if $order == "date"} cur {if $desc}desc{else}asc{/if}{/if}">Date de cotisation <a href="?id={$cotisation.id}&amp;o=date&amp;a" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=date&amp;d" class="icn dn">&darr;</a></td>
                <td></td>
            </tr>
        </thead>
        <tbody>
            {foreach from=$liste item="co"}
                <tr>

                    <td class="num"><a href="{$admin_url}membres/fiche.php?id={$co.id_membre}">{$co.numero}</a></td>
                    <th>{$co.nom}</th>
                    <td>{if $co.a_jour}<b class="confirm">À jour</b>{else}<b class="error">En retard</b>{/if}</td>
                    <td>{$co.date|format_sqlite_date_to_french}</td>
                    <td class="actions">
                        {if $session->canAccess('membres', Membres::DROIT_ECRITURE)}
                        <a class="icn" href="{$admin_url}membres/cotisations/ajout.php?id={$co.id_membre}&amp;cotisation={$cotisation.id}" title="Saisir une cotisation">➕</a>
                        {/if}
                        <a class="icn" href="{$admin_url}membres/cotisations.php?id={$co.id_membre}" title="Voir toutes les cotisations de ce membre">𝍢</a>







>
>
>
>
>
>
|










|
|
|
|






>
|
<







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
        {elseif $cotisation.debut}
            du {$cotisation.debut|format_sqlite_date_to_french} au {$cotisation.fin|format_sqlite_date_to_french}
        {else}
            ponctuelle
        {/if}
        — {$cotisation.montant|escape|html_money} {$config.monnaie}
    </dd>
    <dd>
        {if !$cats}
            <a href="?id={$cotisation.id}&amp;cats=1">Afficher les membres des catégories pour lesquelles cette cotisation est obligatoire</a>
        {else}
            <a href="?id={$cotisation.id}">Afficher seulement les membres à jour (ou qui ont déjà payé, mais ne sont plus à jour)</a>
        {/if}
    </dd>    <dt>Nombre de membres ayant cotisé</dt>
    <dd>
        {$cotisation.nb_membres}
        <small class="help">(incluant les membres des catégories cachées)</small>
    </dd>
</dl>

{if !empty($liste)}
    <table class="list">
        <thead class="userOrder">
            <tr>
                <td class="{if $order == "id"} cur {if $desc}desc{else}asc{/if}{/if}"><a href="?id={$cotisation.id}&amp;o=id&amp;a&amp;cats={$cats}" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=id&amp;d&amp;cats={$cats}" class="icn dn">&darr;</a></td>
                <th class="{if $order == "identite"} cur {if $desc}desc{else}asc{/if}{/if}">Membre <a href="?id={$cotisation.id}&amp;o=identite&amp;a&amp;cats={$cats}" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=identite&amp;d&amp;cats={$cats}" class="icn dn">&darr;</a></th>
                <td class="{if $order == "a_jour"} cur {if $desc}desc{else}asc{/if}{/if}">Statut <a href="?id={$cotisation.id}&amp;o=a_jour&amp;a&amp;cats={$cats}" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=a_jour&amp;d&amp;cats={$cats}" class="icn dn">&darr;</a></td>
                <td class="{if $order == "date"} cur {if $desc}desc{else}asc{/if}{/if}">Date de cotisation <a href="?id={$cotisation.id}&amp;o=date&amp;a" class="icn up">&uarr;</a><a href="?id={$cotisation.id}&amp;o=date&amp;d&amp;cats={$cats}" class="icn dn">&darr;</a></td>
                <td></td>
            </tr>
        </thead>
        <tbody>
            {foreach from=$liste item="co"}
                <tr>
                    <td class="num">{$co.numero}</td>
                    <th><a href="{$admin_url}membres/fiche.php?id={$co.id_membre}" class="icn">{$co.nom}</a></th>

                    <td>{if $co.a_jour}<b class="confirm">À jour</b>{else}<b class="error">En retard</b>{/if}</td>
                    <td>{$co.date|format_sqlite_date_to_french}</td>
                    <td class="actions">
                        {if $session->canAccess('membres', Membres::DROIT_ECRITURE)}
                        <a class="icn" href="{$admin_url}membres/cotisations/ajout.php?id={$co.id_membre}&amp;cotisation={$cotisation.id}" title="Saisir une cotisation">➕</a>
                        {/if}
                        <a class="icn" href="{$admin_url}membres/cotisations.php?id={$co.id_membre}" title="Voir toutes les cotisations de ce membre">𝍢</a>

Modified src/templates/admin/membres/fiche.tpl from [afe9db5157] to [747af1b2b4].

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
		<dd>{if empty($membre.date_connexion)}Jamais{else}{$membre.date_connexion|date_fr:'d/m/Y à H:i'}{/if}</dd>
		<dt>Mot de passe</dt>
		<dd>
			{if empty($membre.passe)}
				Pas de mot de passe configuré
			{else}
				<b class="icn">☑</b> Oui
				{if !empty($membre.otp_secret)}
					(<b class="icn">🔒</b> avec second facteur)
				{else}
					(<b class="icn">🔓</b> sans second facteur)
				{/if}
		{/if}
		</dd>
	</dl>







|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
		<dd>{if empty($membre.date_connexion)}Jamais{else}{$membre.date_connexion|date_fr:'d/m/Y à H:i'}{/if}</dd>
		<dt>Mot de passe</dt>
		<dd>
			{if empty($membre.passe)}
				Pas de mot de passe configuré
			{else}
				<b class="icn">☑</b> Oui
				{if !empty($membre.secret_otp)}
					(<b class="icn">🔒</b> avec second facteur)
				{else}
					(<b class="icn">🔓</b> sans second facteur)
				{/if}
		{/if}
		</dd>
	</dl>

Modified src/templates/admin/membres/modifier.tpl from [02f4db1009] to [8fb5353cd6].

40
41
42
43
44
45
46














47
48
49
50
51
52
53
                <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" />
            </dd>
            <dd><input type="password" name="passe" id="f_passe" value="{form_field name=passe}" pattern="{$password_pattern}" /></dd>
            <dt><label for="f_repasse">Encore le mot de passe</label> (vérification){if $champs.passe.mandatory} <b title="(Champ obligatoire)">obligatoire</b>{/if}</dt>
            <dd><input type="password" name="passe_confirmed" id="f_repasse" value="{form_field name=passe_confirmed}" pattern="{$password_pattern}" /></dd>
        </dl>
    </fieldset>















    {if $session->canAccess('membres', Membres::DROIT_ADMIN) && $user.id != $membre.id}
    <fieldset>
        <legend>Général</legend>
        <dl>
            <dt><label for="f_cat">Catégorie du membre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd>







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







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
                <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" />
            </dd>
            <dd><input type="password" name="passe" id="f_passe" value="{form_field name=passe}" pattern="{$password_pattern}" /></dd>
            <dt><label for="f_repasse">Encore le mot de passe</label> (vérification){if $champs.passe.mandatory} <b title="(Champ obligatoire)">obligatoire</b>{/if}</dt>
            <dd><input type="password" name="passe_confirmed" id="f_repasse" value="{form_field name=passe_confirmed}" pattern="{$password_pattern}" /></dd>
        </dl>
    </fieldset>

    {if $membre.secret_otp || $membre.clef_pgp}
    <fieldset>
        <legend>Options de sécurité</legend>
        <dl>
        {if $membre.secret_otp}
            <dt><label><input type="checkbox" name="clear_otp" value="1" /> Désactiver l'authentification à double facteur TOTP</label></dt>
        {/if}
        {if $membre.clef_pgp}
            <dt><label><input type="checkbox" name="clear_pgp" value="1" /> Supprimer la clé PGP associée au membre</label></dt>
        {/if}
        </dl>
    </fieldset>
    {/if}

    {if $session->canAccess('membres', Membres::DROIT_ADMIN) && $user.id != $membre.id}
    <fieldset>
        <legend>Général</legend>
        <dl>
            <dt><label for="f_cat">Catégorie du membre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd>

Modified src/templates/error.tpl from [52bccc1e68] to [5457db8bcb].

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>{if empty($title)}Erreur{else}{$title}{/if}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style type="text/css">
    {literal}
    * { margin: 0; padding: 0; }

    html { width: 100%; height: 100%; }
|


|







1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
    <meta charset="utf-8" />
    <title>{if empty($title)}Erreur{else}{$title}{/if}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style type="text/css">
    {literal}
    * { margin: 0; padding: 0; }

    html { width: 100%; height: 100%; }
34
35
36
37
38
39
40
41
42
43
44
45
<h1>{if empty($title)}Erreur{else}{$title}{/if}</h1>

<p class="error">
    {$error|escape|nl2br}
</p>

<p>
    <a href="{$www_url}" onclick="return history.back();">&larr; Retour</a>
</p>

</body>
</html>







|




34
35
36
37
38
39
40
41
42
43
44
45
<h1>{if empty($title)}Erreur{else}{$title}{/if}</h1>

<p class="error">
    {$error|escape|nl2br}
</p>

<p>
    <a href="{$admin_url}" onclick="history.back(); return false;">&larr; Retour</a>
</p>

</body>
</html>

Deleted src/templates/index.tpl version [e30875923e].

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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>{$config.nom_asso}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

<body>

<div class="header">
    <h1>{$config.nom_asso}</h1>
</div>

<div class="page">
    <p>
        {$config.adresse_asso}
    </p>
</div>

<p>
    <a href="admin/">Administration</a>
</p>

</body>
</html>
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































Modified src/www/admin/compta/comptes/index.php from [8422c33bbc] to [7140f6c1ad].

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

require_once __DIR__ . '/../_inc.php';

$session->requireAccess('compta', Membres::DROIT_ADMIN);






























$classe = (int) qg('classe');

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

if (!$classe)
{
    $tpl->assign('classes', $comptes->listTree(0, false));
}
else
{
    $positions = $comptes->getPositions();

    $tpl->assign('classe_compte', $comptes->get($classe));
    $tpl->assign('liste', $comptes->listTree($classe));
}

function tpl_get_position($pos)
{
    global $positions;
    return $positions[$pos];
}

$tpl->register_modifier('get_position', 'Garradin\tpl_get_position');











$tpl->display('admin/compta/comptes/index.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
<?php
namespace Garradin;

require_once __DIR__ . '/../_inc.php';

$session->requireAccess('compta', Membres::DROIT_ADMIN);

if (qg('export') == 'plan')
{
	$comptes->exportPlan();
	exit;
}

$tpl->assign('confirm', qg('confirm'));

if (f('import') && $form->check('plan_import', ['upload' => 'file|required', 'format' => 'required|in:json']))
{
	try {
		$comptes->importPlan($_FILES['upload']['tmp_name'], true);
		Utils::redirect(ADMIN_URL . 'compta/comptes/?import&confirm=import');
	}
	catch (UserException $e) {
		$form->addError($e->getMessage());
	}
}
elseif (f('reset') && $form->check('plan_reset'))
{
	try {
		$comptes->importPlan(null, true);
		Utils::redirect(ADMIN_URL . 'compta/comptes/?import&confirm=reset');
	}
	catch (UserException $e) {
		$form->addError($e->getMessage());
	}
}

$classe = (int) qg('classe');

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

if (!$classe)
{
	$tpl->assign('classes', $comptes->listTree(0, false));
}
else
{
	$positions = $comptes->getPositions();

	$tpl->assign('classe_compte', $comptes->get($classe));
	$tpl->assign('liste', $comptes->listTree($classe));
}

function tpl_get_position($pos)
{
	global $positions;
	return $positions[$pos];
}

$tpl->register_modifier('get_position', 'Garradin\tpl_get_position');

$template = 'index';

if ($classe) {
	$template = 'classe';
}
elseif (qg('import') !== null) {
	$template = 'import';
	$tpl->assign('confirm', qg('confirm'));
}

$tpl->display(sprintf('admin/compta/comptes/%s.tpl', $template));

Modified src/www/admin/compta/comptes/journal.php from [82a2dbba77] to [768483fe88].

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
if (!$compte)
{
    throw new UserException("Le compte demandé n'existe pas.");
}

$journal = new Compta\Journal;





$solde = $journal->getSolde($compte->id);

if (($compte->position & Compta\Comptes::ACTIF) || ($compte->position & Compta\Comptes::CHARGE))
{
    $tpl->assign('credit', '-');
    $tpl->assign('debit', '+');
}
else
{
    $tpl->assign('credit', '+');
    $tpl->assign('debit', '-');
}




$tpl->assign('compte', $compte);
$tpl->assign('solde', $solde);
$tpl->assign('journal', $journal->getJournalCompte($compte->id));
$tpl->assign('suivi', qg('suivi'));

$tpl->display('admin/compta/comptes/journal.tpl');







>
>
>
>
|












>
>
>


|



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
if (!$compte)
{
    throw new UserException("Le compte demandé n'existe pas.");
}

$journal = new Compta\Journal;

// Récupération de l'exercice courant et sélectionné
$exercices = new Compta\Exercices;
$exercice = (int) qg('exercice') ?: $exercices->getCurrent()->id;

$solde = $journal->getSolde($compte->id, false, $exercice);

if (($compte->position & Compta\Comptes::ACTIF) || ($compte->position & Compta\Comptes::CHARGE))
{
    $tpl->assign('credit', '-');
    $tpl->assign('debit', '+');
}
else
{
    $tpl->assign('credit', '+');
    $tpl->assign('debit', '-');
}

$tpl->assign('exercices', $exercices->getList());
$tpl->assign('exercice_selectionne', $exercice);

$tpl->assign('compte', $compte);
$tpl->assign('solde', $solde);
$tpl->assign('journal', $journal->getJournalCompte($compte->id, false, $exercice));
$tpl->assign('suivi', qg('suivi'));

$tpl->display('admin/compta/comptes/journal.tpl');

Modified src/www/admin/config/index.php from [402547d004] to [149f2778eb].

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

require_once __DIR__ . '/_inc.php';

$couleur1 = '#9c4f15';
$couleur2 = '#d98628';

if (f('save') && $form->check('config'))
{
    try {
        $config->set('nom_asso', f('nom_asso'));
        $config->set('email_asso', f('email_asso'));
        $config->set('adresse_asso', f('adresse_asso'));
        $config->set('site_asso', f('site_asso'));
        $config->set('accueil_wiki', f('accueil_wiki'));
        $config->set('accueil_connexion', f('accueil_connexion'));
        $config->set('categorie_membres', f('categorie_membres'));
        
        $config->set('champ_identite', f('champ_identite'));
        $config->set('champ_identifiant', f('champ_identifiant'));

        $config->set('pays', f('pays'));
        $config->set('monnaie', f('monnaie'));

        // N'enregistrer les couleurs que si ce ne sont pas les couleurs par défaut
        if (f('couleur1') != $couleur1 || f('couleur2') != $couleur2)
        {
            $config->set('couleur1', f('couleur1'));
            $config->set('couleur2', f('couleur2'));

            if (f('image_fond'))
            {
                $config->set('image_fond', f('image_fond'));





<
<
<


















|







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

require_once __DIR__ . '/_inc.php';




if (f('save') && $form->check('config'))
{
    try {
        $config->set('nom_asso', f('nom_asso'));
        $config->set('email_asso', f('email_asso'));
        $config->set('adresse_asso', f('adresse_asso'));
        $config->set('site_asso', f('site_asso'));
        $config->set('accueil_wiki', f('accueil_wiki'));
        $config->set('accueil_connexion', f('accueil_connexion'));
        $config->set('categorie_membres', f('categorie_membres'));
        
        $config->set('champ_identite', f('champ_identite'));
        $config->set('champ_identifiant', f('champ_identifiant'));

        $config->set('pays', f('pays'));
        $config->set('monnaie', f('monnaie'));

        // N'enregistrer les couleurs que si ce ne sont pas les couleurs par défaut
        if (f('couleur1') != ADMIN_COLOR1 || f('couleur2') != ADMIN_COLOR2)
        {
            $config->set('couleur1', f('couleur1'));
            $config->set('couleur2', f('couleur2'));

            if (f('image_fond'))
            {
                $config->set('image_fond', f('image_fond'));
66
67
68
69
70
71
72
73
74

75
76
77
78
$tpl->assign('pays', Utils::getCountryList());

$cats = new Membres\Categories;
$tpl->assign('membres_cats', $cats->listSimple());

$tpl->assign('champs', $config->get('champs_membres')->getList());

$tpl->assign('couleur1', $config->get('couleur1') ?: $couleur1);
$tpl->assign('couleur2', $config->get('couleur2') ?: $couleur2);

$tpl->assign('couleurs_defaut', [$couleur1, $couleur2]);

$tpl->assign('custom_js', ['color_helper.js']);
$tpl->display('admin/config/index.tpl');







|
|
>
|



63
64
65
66
67
68
69
70
71
72
73
74
75
76
$tpl->assign('pays', Utils::getCountryList());

$cats = new Membres\Categories;
$tpl->assign('membres_cats', $cats->listSimple());

$tpl->assign('champs', $config->get('champs_membres')->getList());

$tpl->assign('couleur1', $config->get('couleur1') ?: ADMIN_COLOR1);
$tpl->assign('couleur2', $config->get('couleur2') ?: ADMIN_COLOR2);
$tpl->assign('background_image_source', ADMIN_BACKGROUND_IMAGE);
$tpl->assign('couleurs_defaut', [ADMIN_COLOR1, ADMIN_COLOR2]);

$tpl->assign('custom_js', ['color_helper.js']);
$tpl->display('admin/config/index.tpl');

Modified src/www/admin/membres/cotisations/voir.php from [db337019a9] to [8935c9e002].

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

if (!$co)
{
    throw new UserException("Cette cotisation n'existe pas.");
}

$page = (int) qg('p') ?: 1;



$tpl->assign('page', $page);
$tpl->assign('bypage', Membres\Cotisations::ITEMS_PER_PAGE);
$tpl->assign('total', $m_cotisations->countMembersForCotisation($co->id));
$tpl->assign('pagination_url', Utils::getSelfUrl([
	'id' => $co->id,
	'o' => qg('o'),
	(qg('a') !== null ? 'a' : 'd') => '',
	'p'  => '[ID]',

]));

$tpl->assign('cotisation', $co);
$tpl->assign('order', qg('o') ?: 'date');
$tpl->assign('desc', !null !== qg('a'));
$tpl->assign('liste', $m_cotisations->listMembersForCotisation(
	$co->id, $page, qg('o'), null !== qg('a') ? false : true));

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







>

>








>




|

|


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

if (!$co)
{
    throw new UserException("Cette cotisation n'existe pas.");
}

$page = (int) qg('p') ?: 1;
$categories = (int) qg('cats');

$tpl->assign('cats', $categories);
$tpl->assign('page', $page);
$tpl->assign('bypage', Membres\Cotisations::ITEMS_PER_PAGE);
$tpl->assign('total', $m_cotisations->countMembersForCotisation($co->id));
$tpl->assign('pagination_url', Utils::getSelfUrl([
	'id' => $co->id,
	'o' => qg('o'),
	(qg('a') !== null ? 'a' : 'd') => '',
	'p'  => '[ID]',
	'cats' => $categories,
]));

$tpl->assign('cotisation', $co);
$tpl->assign('order', qg('o') ?: 'date');
$tpl->assign('desc', null === qg('a'));
$tpl->assign('liste', $m_cotisations->listMembersForCotisation(
	$co->id, $categories, $page, qg('o'), null !== qg('a') ? false : true));

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

Modified src/www/admin/membres/message_collectif.php from [4372383f21] to [e6c89479ee].

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
    {
        if ($match[1] == 'categorie')
        {
            $recipients = $membres->listAllByCategory($match[2], true);
        }
        else
        {

            $recipients = $recherche->search($match[2], 'id, email');
        }





        if (!count($recipients) || !isset($recipients[0]->email))
        {
            $form->addError('Aucun membre dans la liste.');
        }
    }
    else
    {
        throw new UserException('Destinataires invalides : ' . f('recipients'));
    }

    if (!$form->hasErrors())
    {
        try {
            $membres->sendMessage($recipients, f('sujet'),
                f('message'), (bool) f('copie'));







>
|
|
>
>
|
>
>
|






|







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
    {
        if ($match[1] == 'categorie')
        {
            $recipients = $membres->listAllByCategory($match[2], true);
        }
        else
        {
            try {
                $recipients = $recherche->search($match[2], ['id', 'email'], true);
            }
            catch (UserException $e) {
                $form->addError($e->getMessage());
            }
        }

        if (isset($recipients) && (!count($recipients) || !isset($recipients[0]->email)))
        {
            $form->addError('Aucun membre dans la liste.');
        }
    }
    else
    {
        $form->addErrror('Destinataires invalides : ' . f('recipients'));
    }

    if (!$form->hasErrors())
    {
        try {
            $membres->sendMessage($recipients, f('sujet'),
                f('message'), (bool) f('copie'));

Modified src/www/admin/membres/modifier.php from [1cc5572967] to [87a063a4f2].

46
47
48
49
50
51
52








53
54
55
56
57
58
59
            }

            if ($session->canAccess('membres', Membres::DROIT_ADMIN) && $user->id != $membre->id)
            {
                $data['id_categorie'] = f('id_categorie');
                $data['id'] = f('id');
            }









            $membres->edit($id, $data);

            if (isset($data['id']) && $data['id'] != $id)
            {
                $id = (int)$data['id'];
            }







>
>
>
>
>
>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
            }

            if ($session->canAccess('membres', Membres::DROIT_ADMIN) && $user->id != $membre->id)
            {
                $data['id_categorie'] = f('id_categorie');
                $data['id'] = f('id');
            }

            if (f('clear_otp')) {
                $data['secret_otp'] = null;
            }

            if (f('clear_pgp')) {
                $data['clef_pgp'] = null;
            }

            $membres->edit($id, $data);

            if (isset($data['id']) && $data['id'] != $id)
            {
                $id = (int)$data['id'];
            }

Modified src/www/admin/membres/recherche.php from [d259486866] to [ed8d36c24a].

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    {
        throw new UserException('Recherche inconnue ou invalide');
    }

    $query = $r->query;
    $order = $r->order;
    $desc = $r->desc;
    $limit = $r->limit;

    $tpl->assign('recherche', $r);
}

if (f('q') !== null)
{
    $query = json_decode(f('q'), true);







|







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    {
        throw new UserException('Recherche inconnue ou invalide');
    }

    $query = $r->query;
    $order = $r->order;
    $desc = $r->desc;
    $limit = (int) f('limit') ?: $r->limit;

    $tpl->assign('recherche', $r);
}

if (f('q') !== null)
{
    $query = json_decode(f('q'), true);

Modified src/www/admin/static/print.css from [e54ede2290] to [3d3e25b3f9].

64
65
66
67
68
69
70



#rapport .parent {
    background: #ccc;
}

.noprint {
    display: none;
}










>
>
>
64
65
66
67
68
69
70
71
72
73
#rapport .parent {
    background: #ccc;
}

.noprint {
    display: none;
}
td.actions * {
    display: none;
}

Modified src/www/admin/static/scripts/color_helper.js from [1f3037b2de] to [e3dcda260f].

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

		applyLogoColors();
	}

	function applyLogoColors()
	{
		var color = colorToRGB(document.getElementById('f_couleur2').value);
		
		var img = new Image;
		img.src = g.static_url + 'gdin_bg.png';

		img.onload = function() {
			var canvas = document.createElement('canvas');
			var ctx = canvas.getContext('2d');
			canvas.width = img.width;
			canvas.height = img.height;
			ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);







|

|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

		applyLogoColors();
	}

	function applyLogoColors()
	{
		var color = colorToRGB(document.getElementById('f_couleur2').value);

		var img = new Image;
		img.crossOrigin = "Anonymous";

		img.onload = function() {
			var canvas = document.createElement('canvas');
			var ctx = canvas.getContext('2d');
			canvas.width = img.width;
			canvas.height = img.height;
			ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
63
64
65
66
67
68
69


70
71
72
73
74
75
76

			document.getElementById('f_image_fond').value = i.substr(i.indexOf(',')+1);

			delete canvas2;
			delete canvas;
			delete ctx;
		};


	}

	garradin.onload(function () {
		var couleurs = {'couleur1': 'gMainColor', 'couleur2': 'gSecondColor'};

		for (var couleur in couleurs)
		{







>
>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

			document.getElementById('f_image_fond').value = i.substr(i.indexOf(',')+1);

			delete canvas2;
			delete canvas;
			delete ctx;
		};

		img.src = document.getElementById('f_image_fond').getAttribute('data-source');
	}

	garradin.onload(function () {
		var couleurs = {'couleur1': 'gMainColor', 'couleur2': 'gSecondColor'};

		for (var couleur in couleurs)
		{