Overview
Comment:Merge back changes from trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 13b04f66ad5d61c6ec9c2ffb0ce64a3ebad7e668
User & Date: bohwaz on 2020-10-26 20:45:27
Other Links: branch diff | manifest | tags
Context
2020-10-28
01:52
Add account selector to fees check-in: 3f5008e7e3 user: bohwaz tags: dev
2020-10-26
20:45
Merge back changes from trunk check-in: 13b04f66ad user: bohwaz tags: dev
20:41
Fix title check-in: fbb5ea84be user: bohwaz tags: dev
2020-09-08
18:53
Limiter la longueur du champ de recherche check-in: 0277842dc6 user: bohwaz tags: trunk, stable
Changes

Modified src/include/lib/Garradin/Config.php from [0818359e22] to [8204105051].

119
120
121
122
123
124
125

126
127




128
129
130
131
132
133
134
        $values = [];
        $db = DB::getInstance();

        if (isset($this->modified['image_fond']))
        {
            if ($current = $db->firstColumn('SELECT valeur FROM config WHERE cle = \'image_fond\';'))
            {

                $f = new Fichiers($current);
                $f->remove();




            }

            if (strlen($this->config['image_fond']) > 0)
            {
                $f = Fichiers::storeFromBase64('Image_fond_admin.png', $this->config['image_fond']);
                $this->config['image_fond'] = $f->id;
                unset($f);







>
|
|
>
>
>
>







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
        $values = [];
        $db = DB::getInstance();

        if (isset($this->modified['image_fond']))
        {
            if ($current = $db->firstColumn('SELECT valeur FROM config WHERE cle = \'image_fond\';'))
            {
                try {
                    $f = new Fichiers($current);
                    $f->remove();
                }
                catch (\InvalidArgumentException $e) {
                    // Ignore: the file has already been deleted
                }
            }

            if (strlen($this->config['image_fond']) > 0)
            {
                $f = Fichiers::storeFromBase64('Image_fond_admin.png', $this->config['image_fond']);
                $this->config['image_fond'] = $f->id;
                unset($f);

Modified src/include/lib/Garradin/Fichiers.php from [90881d3690] to [c77d409dba].

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
319
320
321

322




323
324
325
326
327
328
329
	 * Envoie une miniature à la taille indiquée au client HTTP
	 * @return void
	 */
	public function serveThumbnail($width = null)
	{
		if (!$this->image)
		{
			throw new \LogicException('Il n\'est pas possible de fournir une miniature pour un fichier qui n\'est pas une image.');
		}

		if (!$width)
		{
			$width = reset(self::$allowed_thumb_sizes);
		}

		if (!in_array($width, self::$allowed_thumb_sizes))
		{
			throw new UserException('Cette taille de miniature n\'est pas autorisée.');
		}

		$cache_id = 'fichiers.' . $this->id_contenu . '.thumb.' . (int)$width;
		$path = Static_Cache::getPath($cache_id);

		// La miniature n'existe pas dans le cache statique, on la crée
		if (!Static_Cache::exists($cache_id))
		{
			$source = $this->getFilePathFromCache();


			(new Image($source))->resize($width)->save($path);




		}

		return $this->_serve($path, $this->type);
	}

	/**
	 * Servir un fichier local en HTTP







|




















>
|
>
>
>
>







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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
	 * Envoie une miniature à la taille indiquée au client HTTP
	 * @return void
	 */
	public function serveThumbnail($width = null)
	{
		if (!$this->image)
		{
			throw new UserException('Il n\'est pas possible de fournir une miniature pour un fichier qui n\'est pas une image.');
		}

		if (!$width)
		{
			$width = reset(self::$allowed_thumb_sizes);
		}

		if (!in_array($width, self::$allowed_thumb_sizes))
		{
			throw new UserException('Cette taille de miniature n\'est pas autorisée.');
		}

		$cache_id = 'fichiers.' . $this->id_contenu . '.thumb.' . (int)$width;
		$path = Static_Cache::getPath($cache_id);

		// La miniature n'existe pas dans le cache statique, on la crée
		if (!Static_Cache::exists($cache_id))
		{
			$source = $this->getFilePathFromCache();

			try {
				(new Image($source))->resize($width)->save($path);
			}
			catch (\RuntimeException $e) {
				throw new UserException('Impossible de créer la miniature');
			}
		}

		return $this->_serve($path, $this->type);
	}

	/**
	 * Servir un fichier local en HTTP
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
	{
		$content = base64_decode($content);
		return self::storeFile($name, null, $content);
	}

	/**
	 * Upload de fichier (interne)
	 * 
	 * @param  string $name
	 * @param  string $path Chemin du fichier
	 * @param  string $content Ou contenu du fichier
	 * @return Fichiers
	 */
	static protected function storeFile($name, $path = null, $content = null)
	{







|







487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
	{
		$content = base64_decode($content);
		return self::storeFile($name, null, $content);
	}

	/**
	 * Upload de fichier (interne)
	 *
	 * @param  string $name
	 * @param  string $path Chemin du fichier
	 * @param  string $content Ou contenu du fichier
	 * @return Fichiers
	 */
	static protected function storeFile($name, $path = null, $content = null)
	{
516
517
518
519
520
521
522

















523
524
525
526
527
528
529
			$ext = substr($name, strrpos($name, '.')+1);
			$ext = strtolower($ext);

			$type = \KD2\FileInfo::getMimeTypeFromFileExtension($ext);
		}

		$is_image = preg_match('/^image\/(?:png|jpe?g|gif)$/', $type);


















		$db = DB::getInstance();

		$db->begin();

		// Il peut arriver que l'on renvoie ici un fichier déjà stocké, auquel cas, ne pas le re-stocker
		if (!($id_contenu = $db->firstColumn('SELECT id FROM fichiers_contenu WHERE hash = ?;', $hash))) {







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







521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
			$ext = substr($name, strrpos($name, '.')+1);
			$ext = strtolower($ext);

			$type = \KD2\FileInfo::getMimeTypeFromFileExtension($ext);
		}

		$is_image = preg_match('/^image\/(?:png|jpe?g|gif)$/', $type);

		// Check that it's a real image
		if ($is_image) {
			try {
				if ($path && !$content) {
					$i = new Image($path);
				}
				else {
					$i = Image::createFromBlob($bytes);
				}

				unset($i);
			}
			catch (\RuntimeException $e) {
				throw new UserException('Fichier image invalide');
			}
		}

		$db = DB::getInstance();

		$db->begin();

		// Il peut arriver que l'on renvoie ici un fichier déjà stocké, auquel cas, ne pas le re-stocker
		if (!($id_contenu = $db->firstColumn('SELECT id FROM fichiers_contenu WHERE hash = ?;', $hash))) {

Modified src/include/lib/Garradin/Form.php from [55cc8a4f8c] to [c87eeb804a].

114
115
116
117
118
119
120


121
122
123
124
125
126
127
			case 'required_with':
			case 'required_with_all':
			case 'required_without':
			case 'required_without_all':
				return sprintf('Le champ %s est vide.', $element);
			case 'min':
				return sprintf('Le champ %s doit faire au moins %d caractères.', $element, $params[0]);


			case 'file':
				return sprintf('Le fichier envoyé n\'est pas valide.');
			case 'confirmed':
				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':







>
>







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
			case 'required_with':
			case 'required_with_all':
			case 'required_without':
			case 'required_without_all':
				return sprintf('Le champ %s est vide.', $element);
			case 'min':
				return sprintf('Le champ %s doit faire au moins %d caractères.', $element, $params[0]);
			case 'max':
				return sprintf('Le champ %s doit faire moins de %d caractères.', $element, $params[0]);
			case 'file':
				return sprintf('Le fichier envoyé n\'est pas valide.');
			case 'confirmed':
				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':

Modified src/include/lib/Garradin/Membres.php from [1b2c90a51a] to [312c7859a9].

43
44
45
46
47
48
49
50



51
52
53
54
55



56
57
58
59
60
61
62
                }
            }

            if (isset($data[$key]))
            {
                if ($config->type == 'datetime' && trim($data[$key]) !== '')
                {
                    $dt = new \DateTime($data[$key]);



                    $data[$key] = $dt->format('Y-m-d H:i');
                }
                elseif ($config->type == 'date' && trim($data[$key]) !== '')
                {
                    $dt = new \DateTime($data[$key]);



                    $data[$key] = $dt->format('Y-m-d');
                }
                elseif ($config->type == 'tel')
                {
                    $data[$key] = Utils::normalizePhoneNumber($data[$key]);
                }
                elseif ($config->type == 'country')







|
>
>
>




|
>
>
>







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

            if (isset($data[$key]))
            {
                if ($config->type == 'datetime' && trim($data[$key]) !== '')
                {
                    $dt = \DateTime::createFromFormat('Y-m-d H:i', $data[$key]);
                    if (!$dt) {
                        throw new UserException(sprintf('Format invalide pour le champ "%s": AAAA-MM-JJ HH:mm attendu.', $config->title));
                    }
                    $data[$key] = $dt->format('Y-m-d H:i');
                }
                elseif ($config->type == 'date' && trim($data[$key]) !== '')
                {
                    $dt = \DateTime::createFromFormat('Y-m-d', $data[$key]);
                    if (!$dt) {
                        throw new UserException(sprintf('Format invalide pour le champ "%s": AAAA-MM-JJ attendu.', $config->title));
                    }
                    $data[$key] = $dt->format('Y-m-d');
                }
                elseif ($config->type == 'tel')
                {
                    $data[$key] = Utils::normalizePhoneNumber($data[$key]);
                }
                elseif ($config->type == 'country')

Modified src/include/lib/Garradin/Membres/Cotisations.php from [0f169947fd] to [aca8beb749].

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
	 * @param  integer $id Numéro de membre
	 * @return array     Liste des cotisations en cours de validité
	 */
	public function listSubscriptionsForMember($id)
	{
		$db = DB::getInstance();
		return $db->get('SELECT c.*,
			MAX(cm.date),
			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







|







343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
	 * @param  integer $id Numéro de membre
	 * @return array     Liste des cotisations en cours de validité
	 */
	public function listSubscriptionsForMember($id)
	{
		$db = DB::getInstance();
		return $db->get('SELECT c.*,
			MAX(cm.date) AS date,
			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

Modified src/include/lib/Garradin/Utils.php from [a502c14a00] to [9e0108f33f].

185
186
187
188
189
190
191





192
193
194
195
196
197
198
        if (empty($destination) || !preg_match('/^https?:\/\//', $destination))
        {
            if (empty($destination))
                $destination = WWW_URL;
            else
                $destination = WWW_URL . preg_replace('/^\//', '', $destination);
        }






        if (headers_sent())
        {
            echo
              '<html>'.
              ' <head>' .
              '  <script type="text/javascript">' .







>
>
>
>
>







185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
        if (empty($destination) || !preg_match('/^https?:\/\//', $destination))
        {
            if (empty($destination))
                $destination = WWW_URL;
            else
                $destination = WWW_URL . preg_replace('/^\//', '', $destination);
        }

        if (PHP_SAPI == 'cli') {
            echo 'Please visit ' . $destination . PHP_EOL;
            exit;
        }

        if (headers_sent())
        {
            echo
              '<html>'.
              ' <head>' .
              '  <script type="text/javascript">' .

Modified src/include/lib/Garradin/Wiki.php from [f692dbeb62] to [6ab9be5a8f].

285
286
287
288
289
290
291




292
293
294
295
296
297
298
        ], 'id = :id', ['id' => (int)$id]);

        return true;
    }

    public function search($search)
    {




        $query = sprintf('SELECT
            p.uri, r.*, snippet(wiki_recherche, \'<b>\', \'</b>\', \'...\', -1, -50) AS snippet,
            rank(matchinfo(wiki_recherche), 0, 1.0, 1.0) AS points
            FROM wiki_recherche AS r INNER JOIN wiki_pages AS p ON p.id = r.id
            WHERE %s AND wiki_recherche MATCH ?
            ORDER BY points DESC LIMIT 0,50;', $this->_getLectureClause('p.'));








>
>
>
>







285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
        ], 'id = :id', ['id' => (int)$id]);

        return true;
    }

    public function search($search)
    {
        if (strlen($search) > 100) {
            throw new UserException('Recherche trop longue : maximum 100 caractères');
        }

        $query = sprintf('SELECT
            p.uri, r.*, snippet(wiki_recherche, \'<b>\', \'</b>\', \'...\', -1, -50) AS snippet,
            rank(matchinfo(wiki_recherche), 0, 1.0, 1.0) AS points
            FROM wiki_recherche AS r INNER JOIN wiki_pages AS p ON p.id = r.id
            WHERE %s AND wiki_recherche MATCH ?
            ORDER BY points DESC LIMIT 0,50;', $this->_getLectureClause('p.'));

Modified src/www/admin/mes_infos_securite.php from [a4c11759e5] to [c7676c4b61].

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

namespace Garradin;

require_once __DIR__ . '/_inc.php';

$confirm = false;

if (f('confirm'))
{
    $form->check('edit_me_security', [
        'passe'       => 'confirmed|min:6',
        'passe_check' => 'required',
        'code' => 'required_with:otp_secret',
    ]);

    if (f('passe_check') && !$session->checkPassword(f('passe_check'), $user->passe))
    {
        $form->addError('Le mot de passe fourni ne correspond pas au mot de passe actuel. Merci de bien vouloir renseigner votre mot de passe courant pour confirmer les changements.');



    }
    elseif (f('code') && !$session->checkOTP(f('otp_secret'), f('code')))
    {
        $form->addError('Le code TOTP entré n\'est pas valide.');
    }

    if (!$form->hasErrors())













|





>
>
>







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

namespace Garradin;

require_once __DIR__ . '/_inc.php';

$confirm = false;

if (f('confirm'))
{
    $form->check('edit_me_security', [
        'passe'       => 'confirmed|min:6',
        'passe_check' => 'required',
        'code'        => 'min:6|max:6',
    ]);

    if (f('passe_check') && !$session->checkPassword(f('passe_check'), $user->passe))
    {
        $form->addError('Le mot de passe fourni ne correspond pas au mot de passe actuel. Merci de bien vouloir renseigner votre mot de passe courant pour confirmer les changements.');
    }
    elseif (f('otp_secret') && f('otp_secret') != 'disable' && !f('code')) {
        $form->addError('Le code OTP est obligatoire');
    }
    elseif (f('code') && !$session->checkOTP(f('otp_secret'), f('code')))
    {
        $form->addError('Le code TOTP entré n\'est pas valide.');
    }

    if (!$form->hasErrors())