Overview
Comment:Merge avec trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 6e79e54ca7c6bd6e443848f3738fb57a13b198bf
User & Date: bohwaz on 2018-11-26 22:54:03
Other Links: branch diff | manifest | tags
Context
2018-12-06
15:28
Merge de trunk vers dev check-in: 7bfce6c590 user: bohwaz tags: dev
2018-11-26
22:54
Merge avec trunk check-in: 6e79e54ca7 user: bohwaz tags: dev
22:51
Corrige bug "UNIQUE constraint failed: membres.email" lors de l'import de CSV check-in: 5160efccc2 user: bohwaz tags: trunk
22:39
Correction bug "FOREIGN KEY constraint failed DELETE FROM compta_categories WHERE id = ?" à la suppression de catégorie qui a été liée par une cotisation check-in: ab04534d50 user: bohwaz tags: dev
Changes

Modified src/Makefile from [78b3f7812f] to [1650b4239f].

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
.PHONY: dev-server release deps publish check-dependencies
KD2_FILE := https://fossil.kd2.org/kd2fw/uv/KD2-5.6.zip

deps:
	$(eval TMP_KD2=$(shell mktemp -d))
	#cd ${TMP_KD2}

	wget ${KD2_FILE} -O ${TMP_KD2}/kd2.zip

	rm -rf "include/lib/KD2"
	unzip "${TMP_KD2}/kd2.zip" -d include/lib

	rm -rf ${TMP_KD2}

dev-server:
	php -S localhost:8082 -t www www/_route.php




release:
	$(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
|
















>
>
>
|







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
.PHONY: dev-server release deps publish check-dependencies test
KD2_FILE := https://fossil.kd2.org/kd2fw/uv/KD2-5.6.zip

deps:
	$(eval TMP_KD2=$(shell mktemp -d))
	#cd ${TMP_KD2}

	wget ${KD2_FILE} -O ${TMP_KD2}/kd2.zip

	rm -rf "include/lib/KD2"
	unzip "${TMP_KD2}/kd2.zip" -d include/lib

	rm -rf ${TMP_KD2}

dev-server:
	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

Modified src/config.dist.php from [4b1bdaf310] to [fd9bfab8c2].

126
127
128
129
130
131
132
133
134


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
























151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
 */
//const ADMIN_URL = 'https://admin.garradin.chezmoi.tld/';

/**
 * Affichage des erreurs
 * Si "true" alors un message expliquant l'erreur et comment rapporter le bug s'affiche
 * en cas d'erreur. Sinon rien ne sera affiché.
 * 
 * Défaut : true


 */
const SHOW_ERRORS = true;

/**
 * Envoi des erreurs par e-mail
 * 
 * Si renseigné, un email sera envoyé à l'adresse indiquée à chaque fois qu'une erreur
 * d'exécution sera rencontrée.
 * Si "false" alors aucun email ne sera envoyé.
 * Note : les erreurs sont déjà toutes loguées dans error.log à la racine de DATA_ROOT
 *
 * Défaut : false
 */
const MAIL_ERRORS = false;

/**
























 * Utilisation de cron pour les tâches automatiques
 * 
 * Si "true" on s'attend à ce qu'une tâche automatisée appelle
 * le script cron.php à la racine toutes les 24 heures. Sinon Garradin
 * effectuera les actions automatiques quand quelqu'un se connecte à 
 * l'administration ou visite le site.
 * 
 * Défaut : false
 */
const USE_CRON = false;

/**
 * Activation de l'envoi de fichier directement par le serveur web.
 * (X-SendFile)
 * 
 * Permet d'améliorer la rapidité d'envoi des fichiers.
 * Supporte les serveurs web suivants :
 * - Apache avec mod_xsendfile (paquet libapache2-mod-xsendfile)
 * - Lighttpd
 * 
 * N'activer que si vous êtes sûr que le module est installé et activé (sinon 
 * les fichiers ne pourront être vus ou téléchargés).
 * Nginx n'est PAS supporté, car X-Accel-Redirect ne peut gérer que des fichiers
 * qui sont *dans* le document root du vhost, ce qui n'est pas le cas ici.
 *
 * Défaut : false
 */







|
|
>
>

|



|










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

|


|

|







|




|







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
 */
//const ADMIN_URL = 'https://admin.garradin.chezmoi.tld/';

/**
 * Affichage des erreurs
 * Si "true" alors un message expliquant l'erreur et comment rapporter le bug s'affiche
 * en cas d'erreur. Sinon rien ne sera affiché.
 *
 * Défaut : false
 *
 * Il est fortement conseillé de mettre cette valeur à false en production !
 */
const SHOW_ERRORS = false;

/**
 * Envoi des erreurs par e-mail
 *
 * Si renseigné, un email sera envoyé à l'adresse indiquée à chaque fois qu'une erreur
 * d'exécution sera rencontrée.
 * Si "false" alors aucun email ne sera envoyé.
 * Note : les erreurs sont déjà toutes loguées dans error.log à la racine de DATA_ROOT
 *
 * Défaut : false
 */
const MAIL_ERRORS = false;

/**
 * Envoi des erreurs à une API compatible AirBrake/Errbit
 *
 * Si renseigné avec une URL HTTP(S) valide, chaque erreur système sera envoyée
 * automatiquement à cette URL.
 *
 * Si laissé à null, aucun rapport ne sera envoyé.
 *
 * Défaut : null
 */
const ERRORS_REPORT_URL = null;

/**
 * Activation de la page permettant de visualiser et rapporter les erreurs présentes
 * dans le error.log.
 *
 * Conseillé de mettre à false si vous ne voulez pas que les administrateurs de votre
 * instance puissent voir les erreurs système.
 *
 * Défaut : true
 * (Afin d'aider au rapport de bugs des instances auto-hébergées)
 */
const ERRORS_ENABLE_LOG_VIEW = true;

/**
 * Utilisation de cron pour les tâches automatiques
 *
 * Si "true" on s'attend à ce qu'une tâche automatisée appelle
 * le script cron.php à la racine toutes les 24 heures. Sinon Garradin
 * effectuera les actions automatiques quand quelqu'un se connecte à
 * l'administration ou visite le site.
 *
 * Défaut : false
 */
const USE_CRON = false;

/**
 * Activation de l'envoi de fichier directement par le serveur web.
 * (X-SendFile)
 *
 * Permet d'améliorer la rapidité d'envoi des fichiers.
 * Supporte les serveurs web suivants :
 * - Apache avec mod_xsendfile (paquet libapache2-mod-xsendfile)
 * - Lighttpd
 *
 * N'activer que si vous êtes sûr que le module est installé et activé (sinon 
 * les fichiers ne pourront être vus ou téléchargés).
 * Nginx n'est PAS supporté, car X-Accel-Redirect ne peut gérer que des fichiers
 * qui sont *dans* le document root du vhost, ce qui n'est pas le cas ici.
 *
 * Défaut : false
 */
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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
 *
 * Défaut : false
 */
const SMTP_HOST = false;

/**
 * Port du serveur SMTP
 * 
 * 25 = port standard pour connexion non chiffrée (465 pour Gmail)
 * 587 = port standard pour connexion SSL
 *
 * Défaut : 587
 */
const SMTP_PORT = 587;

/**
 * Login utilisateur pour le server SMTP
 * 
 * mettre à null pour utiliser un serveur local ou anonyme
 *
 * Défaut : null
 */

//const SMTP_USER = 'garradin@monserveur.com';

/**
 * Mot de passe pour le serveur SMTP
 * 
 * mettre à null pour utiliser un serveur local ou anonyme
 *
 * Défaut : null
 */

//const SMTP_PASSWORD = 'abcd';

/**
 * Sécurité du serveur SMTP
 * 
 * NONE = pas de chiffrement
 * SSL = connexion SSL native
 * TLS = connexion TLS native (le plus sécurisé)
 * STARTTLS = utilisation de STARTTLS (moyennement sécurisé)
 *
 * Défaut : STARTTLS
 */
const SMTP_SECURITY = 'STARTTLS';

/**
 * Activer les sauvegardes automatiques
 * 
 * Utile à désactiver si vous avez déjà des sauvegardes effectuées
 * automatiquement au niveau du système.
 * 
 * Sinon les sauvegardes seront effectuées soit par la tâche cron
 * 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;







|









|









|









|











|


|


|

|



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
 *
 * Défaut : false
 */
const SMTP_HOST = false;

/**
 * Port du serveur SMTP
 *
 * 25 = port standard pour connexion non chiffrée (465 pour Gmail)
 * 587 = port standard pour connexion SSL
 *
 * Défaut : 587
 */
const SMTP_PORT = 587;

/**
 * Login utilisateur pour le server SMTP
 *
 * mettre à null pour utiliser un serveur local ou anonyme
 *
 * Défaut : null
 */

//const SMTP_USER = 'garradin@monserveur.com';

/**
 * Mot de passe pour le serveur SMTP
 *
 * mettre à null pour utiliser un serveur local ou anonyme
 *
 * Défaut : null
 */

//const SMTP_PASSWORD = 'abcd';

/**
 * Sécurité du serveur SMTP
 *
 * NONE = pas de chiffrement
 * SSL = connexion SSL native
 * TLS = connexion TLS native (le plus sécurisé)
 * STARTTLS = utilisation de STARTTLS (moyennement sécurisé)
 *
 * Défaut : STARTTLS
 */
const SMTP_SECURITY = 'STARTTLS';

/**
 * Activer les sauvegardes automatiques
 *
 * Utile à désactiver si vous avez déjà des sauvegardes effectuées
 * automatiquement au niveau du système.
 *
 * Sinon les sauvegardes seront effectuées soit par la tâche cron
 * 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;

Modified src/include/init.php from [bfe2446b5f] to [b52a5657db].

94
95
96
97
98
99
100
101
102


103
104
105
106
107
108
109
    'CACHE_ROOT'            => DATA_ROOT . '/cache',
    'DB_FILE'               => DATA_ROOT . '/association.sqlite',
    'DB_SCHEMA'             => ROOT . '/include/data/schema.sql',
    'PLUGINS_ROOT'          => DATA_ROOT . '/plugins',
    'PREFER_HTTPS'          => false,
    'ALLOW_MODIFIED_IMPORT' => true,
    'PLUGINS_SYSTEM'        => '',
    'SHOW_ERRORS'           => false,
    'MAIL_ERRORS'           => false,


    'USE_CRON'              => false,
    'ENABLE_XSENDFILE'      => false,
    'SMTP_HOST'             => false,
    'SMTP_USER'             => null,
    'SMTP_PASSWORD'         => null,
    'SMTP_PORT'             => 587,
    'SMTP_SECURITY'         => 'STARTTLS',







|

>
>







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    'CACHE_ROOT'            => DATA_ROOT . '/cache',
    'DB_FILE'               => DATA_ROOT . '/association.sqlite',
    'DB_SCHEMA'             => ROOT . '/include/data/schema.sql',
    'PLUGINS_ROOT'          => DATA_ROOT . '/plugins',
    'PREFER_HTTPS'          => false,
    'ALLOW_MODIFIED_IMPORT' => true,
    'PLUGINS_SYSTEM'        => '',
    'SHOW_ERRORS'           => true,
    'MAIL_ERRORS'           => false,
    'ERRORS_REPORT_URL'     => null,
    'ERRORS_ENABLE_LOG_VIEW'=> true,
    'USE_CRON'              => false,
    'ENABLE_XSENDFILE'      => false,
    'SMTP_HOST'             => false,
    'SMTP_USER'             => null,
    'SMTP_PASSWORD'         => null,
    'SMTP_PORT'             => 587,
    'SMTP_SECURITY'         => 'STARTTLS',
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
188
189
190
191
192
    {
        $classname = ltrim($classname, '\\');

        if (array_key_exists($classname, self::$loaded))
        {
            return true;
        }
        
        // Plugins
        if (substr($classname, 0, 16) == 'Garradin\\Plugin\\')
        {
            $classname = substr($classname, 16);
            $plugin_name = substr($classname, 0, strpos($classname, '\\'));
            $filename = str_replace('\\', '/', substr($classname, strpos($classname, '\\')+1));
            
            $path = Plugin::getPath(strtolower($plugin_name)) . '/lib/' . $filename . '.php';
        }
        else
        {
            // PSR-0 autoload
            $filename = str_replace('\\', '/', $classname);
            $path = ROOT . '/include/lib/' . $filename . '.php';
        }
        
        if (!file_exists($path))
        {
            throw new \Exception('File '.$path.' doesn\'t exists');
        }

        self::$loaded[$classname] = true;








|






|








|







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    {
        $classname = ltrim($classname, '\\');

        if (array_key_exists($classname, self::$loaded))
        {
            return true;
        }

        // Plugins
        if (substr($classname, 0, 16) == 'Garradin\\Plugin\\')
        {
            $classname = substr($classname, 16);
            $plugin_name = substr($classname, 0, strpos($classname, '\\'));
            $filename = str_replace('\\', '/', substr($classname, strpos($classname, '\\')+1));

            $path = Plugin::getPath(strtolower($plugin_name)) . '/lib/' . $filename . '.php';
        }
        else
        {
            // PSR-0 autoload
            $filename = str_replace('\\', '/', $classname);
            $path = ROOT . '/include/lib/' . $filename . '.php';
        }

        if (!file_exists($path))
        {
            throw new \Exception('File '.$path.' doesn\'t exists');
        }

        self::$loaded[$classname] = true;

210
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

// activer l'envoi de mails si besoin est
if (MAIL_ERRORS)
{
    ErrorManager::setEmail(MAIL_ERRORS);
}

ErrorManager::setExtraDebugEnv([
    'Garradin version' => garradin_version(),
    'Garradin data root' => DATA_ROOT,

    ]);






ErrorManager::setProductionErrorTemplate('<!DOCTYPE html><html><head><title>Erreur interne</title>
    <style type="text/css">
    body {font-family: sans-serif; }
    code, p, h1 { max-width: 400px; margin: 1em auto; display: block; }
    code { text-align: right; color: #666; }
    a { color: blue; }

    </style></head><body><h1>Erreur interne</h1><p>Désolé mais le serveur a rencontré une erreur interne
    et ne peut répondre à votre requête. Merci de ré-essayer plus tard.</p>
    <p>Si vous suspectez un bug dans Garradin, vous pouvez suivre 
    <a href="http://dev.kd2.org/garradin/Rapporter+un+bug">ces instructions</a>
    pour le rapporter.</p>
    <if(email)><p>Un-e responsable a été notifié-e et cette erreur sera corrigée dès que possible.</p></if>
    <if(log)><code>Référence : <b>{$ref}</b></code></if>
    <p><a href="' . WWW_URL . '">&larr; Retour à la page d\'accueil</a></p>
    </body></html>');

ErrorManager::setHtmlFooter('<hr /><section><article>Cette erreur est peut-être un bug dans Garradin&nbsp;? En ce cas vous pouvez le rapporter en suivant <a href="http://dev.kd2.org/garradin/Rapporter+un+bug">ces instructions</a>.</section></article>');






















function user_error($e)
{
    if (PHP_SAPI == 'cli')
    {
        echo $e->getMessage();
    }







|
|
|
>
|
>
>
>
>
>







>





|
|



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







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

// activer l'envoi de mails si besoin est
if (MAIL_ERRORS)
{
    ErrorManager::setEmail(MAIL_ERRORS);
}

ErrorManager::setContext([
    'rootDirectory'      => ROOT,
    'garradin_data_root' => DATA_ROOT,
    'garradin_version'   => garradin_version(),
]);

if (ERRORS_REPORT_URL)
{
    ErrorManager::setRemoteReporting(ERRORS_REPORT_URL, false);
}

ErrorManager::setProductionErrorTemplate('<!DOCTYPE html><html><head><title>Erreur interne</title>
    <style type="text/css">
    body {font-family: sans-serif; }
    code, p, h1 { max-width: 400px; margin: 1em auto; display: block; }
    code { text-align: right; color: #666; }
    a { color: blue; }
    form { text-align: center; }
    </style></head><body><h1>Erreur interne</h1><p>Désolé mais le serveur a rencontré une erreur interne
    et ne peut répondre à votre requête. Merci de ré-essayer plus tard.</p>
    <p>Si vous suspectez un bug dans Garradin, vous pouvez suivre 
    <a href="http://dev.kd2.org/garradin/Rapporter+un+bug">ces instructions</a>
    pour le rapporter.</p>
    <if(sent)><p>Un-e responsable a été notifié-e et cette erreur sera corrigée dès que possible.</p></if>
    <if(logged)><code>L\'erreur a été enregistrée dans les journaux système (error.log) sous la référence : <b>{$ref}</b></code></if>
    <p><a href="' . WWW_URL . '">&larr; Retour à la page d\'accueil</a></p>
    </body></html>');

ErrorManager::setHtmlHeader('<!DOCTYPE html><meta charset="utf-8" /><style type="text/css">
    body { font-family: sans-serif; } * { margin: 0; padding: 0; }
    u, code b, i, h3 { font-style: normal; font-weight: normal; text-decoration: none; }
    #icn { color: #fff; font-size: 2em; float: right; margin: 1em; padding: 1em; background: #900; border-radius: 50%; }
    section header { background: #fdd; padding: 1em; }
    section article { margin: 1em; }
    section article h3, section article h4 { font-size: 1em; font-family: mono; }
    code { border: 1px dotted #ccc; display: block; }
    code b { margin-right: 1em; color: #999; }
    code u { background: #fcc; display: inline-block; width: 100%; }
    table { border-collapse: collapse; margin: 1em; } td, th { border: 1px solid #ccc; padding: .2em .5em; text-align: left; 
    vertical-align: top; }
    input { padding: .3em; margin: .5em; font-size: 1.2em; cursor: pointer; }
</style>
<pre id="icn"> \__/<br /> (xx)<br />//||\\\\</pre>
<section>
    <article>
    <h1>Une erreur s\'est produite</h1>
    <if(report)><form method="post" action="https://garradin.eu/report/"><p><input type="hidden" name="report" value="{$report_json}" /><input type="submit" value="Rapporter l\'erreur aux développeur⋅euses de Garradin &rarr;" /></p></form></if>
    </article>
</section>
');

function user_error($e)
{
    if (PHP_SAPI == 'cli')
    {
        echo $e->getMessage();
    }

Modified src/include/lib/Garradin/Compta/Rapports.php from [193a79d665] to [4e82cf4b4a].

333
334
335
336
337
338
339
340
341
342
343
344
345
                continue;
            }

            foreach ($p['comptes'] as $id=>$solde)
            {
                if ($solde == 0)
                {
                    unset($source[$parent]['comptes'][$id]);
                }
            }
        }
    }
}







|





333
334
335
336
337
338
339
340
341
342
343
344
345
                continue;
            }

            foreach ($p['comptes'] as $id=>$solde)
            {
                if ($solde == 0)
                {
                    unset($source['comptes'][$parent]['comptes'][$id]);
                }
            }
        }
    }
}

Modified src/include/lib/Garradin/DB.php from [69073dd36f] to [41264a6c51].

46
47
48
49
50
51
52




53
54
55
56
57
58
59
    public function connect()
    {
        if (parent::connect())
        {
            // Activer les contraintes des foreign keys
            $this->db->exec('PRAGMA foreign_keys = ON;');





            $this->db->createFunction('transliterate_to_ascii', ['Garradin\Utils', 'transliterateToAscii']);
        }
    }

    public function close()
    {
        parent::close();







>
>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    public function connect()
    {
        if (parent::connect())
        {
            // Activer les contraintes des foreign keys
            $this->db->exec('PRAGMA foreign_keys = ON;');

            // 10 secondes
            $this->db->busyTimeout(10 * 1000);
            $this->db->exec('PRAGMA journal_mode = TRUNCATE;');

            $this->db->createFunction('transliterate_to_ascii', ['Garradin\Utils', 'transliterateToAscii']);
        }
    }

    public function close()
    {
        parent::close();

Modified src/include/lib/Garradin/Membres.php from [3d4bebf63e] to [b2dcaceab5].

92
93
94
95
96
97
98





99
100
101
102
103
104
105
                    {
                        $data[$key] = 0;
                    }
                }
                elseif ($config->type == 'email')
                {
                    $data[$key] = strtolower($data[$key]);





                }
                elseif ($config->type == 'select' && !in_array($data[$key], $config->options))
                {
                    throw new UserException('Le champ "' . $config->title . '" ne correspond pas à un des choix proposés.');
                }
                elseif ($config->type == 'multiple')
                {







>
>
>
>
>







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
                    {
                        $data[$key] = 0;
                    }
                }
                elseif ($config->type == 'email')
                {
                    $data[$key] = strtolower($data[$key]);

                    if (!SMTP::checkEmailIsValid($data[$key], false))
                    {
                        throw new UserException(sprintf('Adresse email invalide "%s" pour le champ "%s".', $data[$key], $config->title));
                    }
                }
                elseif ($config->type == 'select' && !in_array($data[$key], $config->options))
                {
                    throw new UserException('Le champ "' . $config->title . '" ne correspond pas à un des choix proposés.');
                }
                elseif ($config->type == 'multiple')
                {
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
            {
                throw new UserException('Ce numéro de membre est déjà attribué à un autre membre.');
            }
        }

        $this->_checkFields($data, true, $require_password);

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

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







|







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
            {
                throw new UserException('Ce numéro de membre est déjà attribué à un autre membre.');
            }
        }

        $this->_checkFields($data, true, $require_password);

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

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

Modified src/include/lib/Garradin/Membres/Import.php from [f63328115c] to [e0ef7a7acb].

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
	/**
	 * Importer un CSV générique
	 * @param  string $path              Chemin vers le CSV
	 * @param  array  $translation_table Tableau indiquant la correspondance à effectuer entre les colonnes
	 * du CSV et les champs de Garradin. Par exemple : ['Date création fiche' => 'date_inscription']
	 * @return boolean                   TRUE en cas de succès
	 */
	public function fromArray(array $table, $translation_table, $skip_lines = 0)
	{
		$db = DB::getInstance();
		$db->begin();
		$membres = new Membres;
		$champs = Config::getInstance()->get('champs_membres');

		$nb_columns = count($translation_table);







|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
	/**
	 * Importer un CSV générique
	 * @param  string $path              Chemin vers le CSV
	 * @param  array  $translation_table Tableau indiquant la correspondance à effectuer entre les colonnes
	 * du CSV et les champs de Garradin. Par exemple : ['Date création fiche' => 'date_inscription']
	 * @return boolean                   TRUE en cas de succès
	 */
	public function fromArray(array $table, $translation_table, $current_user_id, $skip_lines = 0)
	{
		$db = DB::getInstance();
		$db->begin();
		$membres = new Membres;
		$champs = Config::getInstance()->get('champs_membres');

		$nb_columns = count($translation_table);
124
125
126
127
128
129
130
131










132












133

134
135
136
137
138
139
140
					}
				}
				else
				{
					$data[$garradin_field] = $row[$column_index];
				}
			}











			try {












				$membres->add($data, false);

			}
			catch (UserException $e)
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : ' . $e->getMessage());
			}
		}








>
>
>
>
>
>
>
>
>
>

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







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
					}
				}
				else
				{
					$data[$garradin_field] = $row[$column_index];
				}
			}

			if (!empty($data['numero']) && $data['numero'] > 0)
			{
				$numero = (int)$data['numero'];
			}
			else
			{
				unset($data['numero']);
				$numero = false;
			}

			try {
				if ($numero && ($id = $membres->getIDWithNumero($numero)))
				{
					if ($id === $current_user_id)
					{
						// Ne pas modifier le membre courant, on risque de se tirer une balle dans le pied
						continue;
					}

					$membres->edit($id, $data);
				}
				else
				{
					$membres->add($data, false);
				}
			}
			catch (UserException $e)
			{
				$db->rollback();
				throw new UserException('Erreur sur la ligne ' . $line . ' : ' . $e->getMessage());
			}
		}

Modified src/include/lib/Garradin/Template.php from [1f698e9c13] to [808469b185].

64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
		$this->register_function('format_droits', [$this, 'formatDroits']);

		$this->register_function('csrf_field', function ($params) {
			return Form::tokenHTML($params['key']);
		});

		$this->register_modifier('strlen', 'strlen');

		$this->register_modifier('get_country_name', ['Garradin\Utils', 'getCountryName']);
		$this->register_modifier('format_sqlite_date_to_french', ['Garradin\Utils', 'sqliteDateToFrench']);
		$this->register_modifier('format_bytes', ['Garradin\Utils', 'format_bytes']);
		$this->register_modifier('format_tel', [$this, 'formatPhoneNumber']);
		$this->register_modifier('abs', 'abs');
		$this->register_modifier('display_champ_membre', [$this, 'displayChampMembre']);
		
		$this->register_modifier('get_nom_compte', function ($compte) {
			if (is_null($compte))
			{
				return '';
			}

			if (!isset($this->liste_comptes))







>






|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
		$this->register_function('format_droits', [$this, 'formatDroits']);

		$this->register_function('csrf_field', function ($params) {
			return Form::tokenHTML($params['key']);
		});

		$this->register_modifier('strlen', 'strlen');
		$this->register_modifier('dump', ['KD2\ErrorManager', 'dump']);
		$this->register_modifier('get_country_name', ['Garradin\Utils', 'getCountryName']);
		$this->register_modifier('format_sqlite_date_to_french', ['Garradin\Utils', 'sqliteDateToFrench']);
		$this->register_modifier('format_bytes', ['Garradin\Utils', 'format_bytes']);
		$this->register_modifier('format_tel', [$this, 'formatPhoneNumber']);
		$this->register_modifier('abs', 'abs');
		$this->register_modifier('display_champ_membre', [$this, 'displayChampMembre']);

		$this->register_modifier('get_nom_compte', function ($compte) {
			if (is_null($compte))
			{
				return '';
			}

			if (!isset($this->liste_comptes))

Modified src/templates/admin/config/_menu.tpl from [53db92aa96] to [cd582bc2fa].

1
2
3
4
5
6
7

8
<ul class="actions">
	<li{if $current == 'index'} class="current"{/if}><a href="{$admin_url}config/">Général</a></li>
	<li{if $current == 'categories'} class="current"{/if}><a href="{$admin_url}config/categories/">Catégories de membres</a></li>
	<li{if $current == 'fiches_membres'} class="current"{/if}><a href="{$admin_url}config/membres.php">Fiche des membres</a></li>
	<li{if $current == 'site'} class="current"{/if}><a href="{$admin_url}config/site.php">Site public</a></li>
	<li{if $current == 'donnees'} class="current"{/if}><a href="{$admin_url}config/donnees/">Sauvegarde et restauration</a></li>
	<li{if $current == 'plugins'} class="current"{/if}><a href="{$admin_url}config/plugins.php">Extensions</a></li>

</ul>







>

1
2
3
4
5
6
7
8
9
<ul class="actions">
	<li{if $current == 'index'} class="current"{/if}><a href="{$admin_url}config/">Général</a></li>
	<li{if $current == 'categories'} class="current"{/if}><a href="{$admin_url}config/categories/">Catégories de membres</a></li>
	<li{if $current == 'fiches_membres'} class="current"{/if}><a href="{$admin_url}config/membres.php">Fiche des membres</a></li>
	<li{if $current == 'site'} class="current"{/if}><a href="{$admin_url}config/site.php">Site public</a></li>
	<li{if $current == 'donnees'} class="current"{/if}><a href="{$admin_url}config/donnees/">Sauvegarde et restauration</a></li>
	<li{if $current == 'plugins'} class="current"{/if}><a href="{$admin_url}config/plugins.php">Extensions</a></li>
	<li{if $current == 'logs'} class="current"{/if}><a href="{$admin_url}config/logs.php">Journaux</a></li>
</ul>

Added src/templates/admin/config/logs.tpl version [bdfa29c9cf].

















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
{include file="admin/_head.tpl" title="Journaux" current="config" custom_css=["styles/config.css"]}

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

{if ERRORS_ENABLE_LOG_VIEW}
<ul class="actions sub">
	<li{if $type != 'errors'} class="current"{/if}><a href="{$self_url_no_qs}">Actions utilisateurs</a></li>
	<li{if $type == 'errors'} class="current"{/if}><a href="?type=errors">Erreurs système</a></li>
</ul>
{/if}

{if isset($reports) && isset($id)}
	<section class="error">
		{foreach from=$main.errors item="error"}
			<h2 class="ruler">{$error.type}: {$error.message} [Code: {$error.errorCode}]</h2>
			{if !empty($error.backtrace)}
				{foreach from=$error.backtrace item=trace}
				<article class="trace">
					{if $trace.function}
						<h4>
							{$trace.function}{if !empty($trace.args)} ({$trace.args|count} arg.){/if}
						{if !empty($trace.args)}
							<table>
							{foreach from=$trace.args key=name item=arg}
								<tr>
									<th>{$name}</th>
									<td>{$arg}</td>
								</tr>
							{/foreach}
							</table>
						{/if}
						</h4>
					{/if}
					{if $trace.file}<h5>{$trace.file}:{$trace.line}</h5>{/if}
					{if !empty($trace.code)}
						<pre>{foreach from=$trace.code item=line key=n}{if $n == $trace.line}<b>{/if}<i>{$n}</i> {$line}{if $n == $trace.line}</b>{/if}<br />{/foreach}</pre>
					{/if}
				</article>
				{/foreach}
			{/if}
		{/foreach}

		{foreach from=$reports item=report}
		<article class="event">
			<h2 class="ruler">Occurence du {$report.context.date|date_fr}</h2>
			<table class="list">
				{foreach from=$report.context key="k" item="v"}
				<tr>
					<th>{$k}</th>
					<td>{if $k == 'date'}{$v|date_fr}{else}{$v}{/if}</td>
				</tr>
				{/foreach}
			</table>
		</article>
		{/foreach}
	</section>
{elseif isset($errors)}
	<p class="help">
		Liste des erreurs système et de code rencontrées par Garradin.
		Cliquer sur un des bugs pour le rapporter aux développeur⋅euses de Garradin.
	</p>

	{if !count($errors)}
		<p class="alert">Aucune erreur n'a été trouvée dans le journal error.log</p>
	{else}
		<table class="list">
			<thead>
				<tr>
					<th>Réf.</th>
					<td>Erreur</td>
					<td>Occurences</td>
					<td>Dernière fois</td>
					<td></td>
				</tr>
			</thead>
			<tbody>
				{foreach from=$errors item=error key=ref}
				<tr>
					<th><a href="?type=errors&id={$ref}">{$ref}</a></th>
					<td>
						{$error.message}<br />
						<tt>{$error.source}</tt>
					</td>
					<td>{$error.count}</td>
					<td>{$error.last_seen|date_fr}</td>
					<td class="actions"><a title="Voir les détails" class="icn" href="?type=errors&id={$ref}">𝍢</a></td>
				</tr>
				{/foreach}
			</tbody>
		</table>
	{/if}
{else}
	<p class="help">
		Cette page permet de suivre les actions effectuées par les utilisateurs.
	</p>

	{if empty($list)}
		<p class="alert">
			Aucune entrée dans le journal d'actions.
		</p>
	{/if}
{/if}

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

Added src/www/admin/config/logs.php version [3641ab6b53].





































































































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

use KD2\ErrorManager;

require_once __DIR__ . '/_inc.php';

if (qg('type') == 'errors' && ERRORS_ENABLE_LOG_VIEW)
{
    $reports = ErrorManager::getReportsFromLog(null, qg('id'));
    $reports = array_reverse($reports, true);

    foreach ($reports as &$report)
    {
        $report->context->date = strtotime($report->context->date);
    }

    unset($report);

    $errors = [];

    if (qg('id'))
    {
        $tpl->assign('id', qg('id'));
        $tpl->assign('main', $reports[0]);
        $tpl->assign('reports', $reports);
    }
    else
    {
        foreach ($reports as $report)
        {
            if (!isset($errors[$report->context->id]))
            {
                $errors[$report->context->id] = [
                    'message' => $report->errors[0]->message,
                    'source' => sprintf('%s:%d', $report->errors[0]->backtrace[0]->file, $report->errors[0]->backtrace[0]->line),
                    'count' => 0,
                ];
            }

            $errors[$report->context->id]['last_seen'] = $report->context->date;
            $errors[$report->context->id]['count']++;
        }

        $tpl->assign('errors', $errors);
    }
}

$tpl->assign('type', qg('type'));
$tpl->display('admin/config/logs.tpl');

Modified src/www/admin/membres/import.php from [06d138ee5f] to [ad7949cf6c].

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

    $csv_file = json_decode(f('csv_encoded'), true);

    if (!$form->hasErrors())
    {
        try
        {
            $import->fromArray($csv_file, f('csv_translate'), f('skip_first_line') ? 1 : 0);
            Utils::redirect(ADMIN_URL . 'membres/import.php?ok');
        }
        catch (UserException $e)
        {
            $form->addError($e->getMessage());
        }
    }







|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

    $csv_file = json_decode(f('csv_encoded'), true);

    if (!$form->hasErrors())
    {
        try
        {
            $import->fromArray($csv_file, f('csv_translate'), $user->id, f('skip_first_line') ? 1 : 0);
            Utils::redirect(ADMIN_URL . 'membres/import.php?ok');
        }
        catch (UserException $e)
        {
            $form->addError($e->getMessage());
        }
    }

Added src/www/admin/static/styles/config.css version [ec00c1eea1].













































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
.error .trace {
	border: 1px solid #ccc;
	margin: 1em;
}

.error .trace h4 {
	background: #ccc;
	padding: .5em;
}

.error .trace h5 {
	background: #ddd;
	padding: .5em;
}

.error .trace pre i {
	color: #999;
}

.error .trace pre b {
	background: #fcc;
	display: inline-block;
	width: 100%;
}

.error .trace h4 table {
	float: right;
	background: #ddd;
}

.error .trace h4 table td, .error .trace h4 table th {
	border: 1px solid #999;
	padding: .2em .5em;
}

.error .event table {
	margin: .5em;
}