Overview
Comment:Merge trunk changes
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA3-256: a8800bc9926291130dc5fb16aab11f71e22f1e7079c9888ea1c638ddb6065984
User & Date: bohwaz on 2021-05-06 14:10:10
Other Links: branch diff | manifest | tags
Context
2021-05-07
01:00
Progress on web editor check-in: 879ddf7e69 user: bohwaz tags: dev
2021-05-06
14:10
Merge trunk changes check-in: a8800bc992 user: bohwaz tags: dev
14:02
Fix wiki decryption check-in: ebb7cd1655 user: bohwaz tags: dev
02:12
Fix web search link check-in: 1b784acf2e user: bohwaz tags: trunk, stable
Changes

Modified src/Makefile from [4afd2ef457] to [c52b2382ef].

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
		rsync --files-from=dependencies.list -r ./ /tmp/garradin-build/garradin/src/include/lib/
	mv www/admin/static/mini.css /tmp/garradin-build/garradin/src/www/admin/static/admin.css
	cd /tmp/garradin-build/garradin/src/www/admin/static; \
		rm -f styles/[0-9]*.css; \
		rm -f font/*.css font/*.json
	cd /tmp/garradin-build/garradin/src; \
		rm -f Makefile include/lib/KD2/data/countries.en.json
	cd /tmp/garradin-build/garradin/src/plugins; \
		wget https://fossil.kd2.org/garradin-plugins/uv/welcome.tar.gz
	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:







|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
		rsync --files-from=dependencies.list -r ./ /tmp/garradin-build/garradin/src/include/lib/
	mv www/admin/static/mini.css /tmp/garradin-build/garradin/src/www/admin/static/admin.css
	cd /tmp/garradin-build/garradin/src/www/admin/static; \
		rm -f styles/[0-9]*.css; \
		rm -f font/*.css font/*.json
	cd /tmp/garradin-build/garradin/src; \
		rm -f Makefile include/lib/KD2/data/countries.en.json
	cd /tmp/garradin-build/garradin/src/data; mkdir plugins && cd plugins; \
		wget https://fossil.kd2.org/garradin-plugins/uv/welcome.tar.gz
	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:

Modified src/include/data/champs_membres.ini from [adac130192] to [1af4967b02].

43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
;	install:
;		true = sera ajouté aux fiches membres à l'installation
;		false = sera seulement présent dans les champs supplémentaires possibles (défaut)

[numero]
type = number
title = "Numéro de membre"

mandatory = true
install = true
editable = false
list_row = 1

[nom]
type = text
title = "Nom & prénom"







>
|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
;	install:
;		true = sera ajouté aux fiches membres à l'installation
;		false = sera seulement présent dans les champs supplémentaires possibles (défaut)

[numero]
type = number
title = "Numéro de membre"
help = "Doit être unique, laisser vide pour que le numéro soit attribué automatiquement"
mandatory = false
install = true
editable = false
list_row = 1

[nom]
type = text
title = "Nom & prénom"

Modified src/include/lib/Garradin/DynamicList.php from [21ed3dc2ae] to [2a8411f96b].

1
2
3
4
5
6
7
8
9
10
11
12
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<?php

namespace Garradin;

class DynamicList
{
	protected $columns;
	protected $tables;
	protected $conditions;
	protected $group;
	protected $order;
	protected $modifier;
................................................................................
	}

	public function groupBy(string $value)
	{
		$this->group = $value;
	}

	public function count()
	{
		if (null === $this->count_result) {
			$sql = sprintf('SELECT %s FROM %s WHERE %s;', $this->count, $this->tables, $this->conditions);
			$this->count_result = DB::getInstance()->firstColumn($sql);
		}

		return $this->count_result;
	}

	public function export(string $name, string $format = 'csv')
	{
		$this->setPageSize(null);
		$columns = [];





|







 







|






|







1
2
3
4
5
6
7
8
9
10
11
12
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<?php

namespace Garradin;

class DynamicList implements \Countable
{
	protected $columns;
	protected $tables;
	protected $conditions;
	protected $group;
	protected $order;
	protected $modifier;
................................................................................
	}

	public function groupBy(string $value)
	{
		$this->group = $value;
	}

	public function count(): int
	{
		if (null === $this->count_result) {
			$sql = sprintf('SELECT %s FROM %s WHERE %s;', $this->count, $this->tables, $this->conditions);
			$this->count_result = DB::getInstance()->firstColumn($sql);
		}

		return (int) $this->count_result;
	}

	public function export(string $name, string $format = 'csv')
	{
		$this->setPageSize(null);
		$columns = [];

Modified src/include/lib/Garradin/Files/Files.php from [a99c4bec9a] to [c7cc1da4fa].

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
...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273



























			$path = trim($path . '/' . $part, '/');
			$breadcrumbs[$path] = $part;
		}

		return $breadcrumbs;
	}

	static public function getQuota(): int
	{
		return FILE_STORAGE_QUOTA ?? self::callStorage('getQuota');
	}

	static public function getUsedQuota(bool $force_refresh = false): int
	{
		if ($force_refresh || Static_Cache::expired('used_quota', 3600)) {
			$quota = self::callStorage('getTotalSize');
			Static_Cache::store('used_quota', $quota);
		}
		else {
			$quota = (int) Static_Cache::get('used_quota');
		}

		return $quota;
	}

	static public function getRemainingQuota(bool $force_refresh = false): int
	{
		if (FILE_STORAGE_QUOTA !== null) {
			return FILE_STORAGE_QUOTA - self::getUsedQuota($force_refresh);
		}

		return self::callStorage('getRemainingQuota');
	}
................................................................................
	{
		if (!self::$quota) {
			return;
		}

		$remaining = self::getRemainingQuota(true);

		if (($remaining - $size) < 0) {
			throw new ValidationException('L\'espace disque est insuffisant pour réaliser cette opération');
		}
	}

	static public function enableQuota(): void
	{
		self::$quota = true;
	}

	static public function disableQuota(): void
	{
		self::$quota = false;
	}
}


































|




|






|





|







 







|













|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
...
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
			$path = trim($path . '/' . $part, '/');
			$breadcrumbs[$path] = $part;
		}

		return $breadcrumbs;
	}

	static public function getQuota(): float
	{
		return FILE_STORAGE_QUOTA ?? self::callStorage('getQuota');
	}

	static public function getUsedQuota(bool $force_refresh = false): float
	{
		if ($force_refresh || Static_Cache::expired('used_quota', 3600)) {
			$quota = self::callStorage('getTotalSize');
			Static_Cache::store('used_quota', $quota);
		}
		else {
			$quota = (float) Static_Cache::get('used_quota');
		}

		return $quota;
	}

	static public function getRemainingQuota(bool $force_refresh = false): float
	{
		if (FILE_STORAGE_QUOTA !== null) {
			return FILE_STORAGE_QUOTA - self::getUsedQuota($force_refresh);
		}

		return self::callStorage('getRemainingQuota');
	}
................................................................................
	{
		if (!self::$quota) {
			return;
		}

		$remaining = self::getRemainingQuota(true);

		if (($remaining - (float) $size) < 0) {
			throw new ValidationException('L\'espace disque est insuffisant pour réaliser cette opération');
		}
	}

	static public function enableQuota(): void
	{
		self::$quota = true;
	}

	static public function disableQuota(): void
	{
		self::$quota = false;
	}

	static public function getVirtualTableName(): string
	{
		if (FILE_STORAGE_BACKEND == 'SQLite') {
			return 'files';
		}

		return 'tmp_files';
	}

	static public function syncVirtualTable(string $parent = '')
	{
		if (FILE_STORAGE_BACKEND == 'SQLite') {
			// No need to create a virtual table, use the real one
			return;
		}

		$db = DB::getInstance();
		$db->begin();
		$db->exec('CREATE TEMP TABLE IF NOT EXISTS tmp_files AS SELECT * FROM files WHERE 0;');

		foreach (Files::list(File::CONTEXT_TRANSACTION) as $file) {
			$db->insert('tmp_files', $file->asArray(true));
		}

		$db->commit();
	}
}

Modified src/include/lib/Garradin/Files/Storage/FileSystem.php from [c12d0e525f] to [c060f854cf].

3
4
5
6
7
8
9


10
11
12
13
14
15
16
...
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
namespace Garradin\Files\Storage;

use Garradin\Files\Files;
use Garradin\Entities\Files\File;
use Garradin\DB;
use Garradin\Utils;



/**
 * This class provides storage in the file system
 * You need to configure FILE_STORAGE_CONFIG to give a file path
 */
class FileSystem implements StorageInterface
{
	static protected $_size;
................................................................................
			// file_image.jpeg
			$files[$file->getType() . '_' .$file->getFilename()] = self::_SplToFile($file);
		}

		return Utils::knatcasesort($files);
	}

	static public function getTotalSize(): int
	{
		if (null !== self::$_size) {
			return self::$_size;
		}

		$total = 0;

		$path = self::_getRoot();

		foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD) as $p) {
			$total += $p->getSize();
		}

		self::$_size = (int) $total;

		return self::$_size;
	}

	/**
	 * @see https://www.crazyws.fr/dev/fonctions-php/fonction-disk-free-space-et-disk-total-space-pour-ovh-2JMH9.html
	 * @see https://github.com/jdel/sspks/commit/a890e347f32e9e3e50a0dd82398947633872bf38
	 */
	static public function getQuota(): int
	{
		$quota = @disk_total_space(self::_getRoot());
		return $quota === false ? \PHP_INT_MAX : (int) $quota;
	}

	static public function getRemainingQuota(): int
	{
		$quota = @disk_free_space(self::_getRoot());
		return $quota === false ? \PHP_INT_MAX : (int) $quota;
	}

	static public function truncate(): void
	{
		Utils::deleteRecursive(self::_getRoot());
	}








>
>







 







|













|








|

|
|


|


|







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
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
254
namespace Garradin\Files\Storage;

use Garradin\Files\Files;
use Garradin\Entities\Files\File;
use Garradin\DB;
use Garradin\Utils;

use const Garradin\DATA_ROOT;

/**
 * This class provides storage in the file system
 * You need to configure FILE_STORAGE_CONFIG to give a file path
 */
class FileSystem implements StorageInterface
{
	static protected $_size;
................................................................................
			// file_image.jpeg
			$files[$file->getType() . '_' .$file->getFilename()] = self::_SplToFile($file);
		}

		return Utils::knatcasesort($files);
	}

	static public function getTotalSize(): float
	{
		if (null !== self::$_size) {
			return self::$_size;
		}

		$total = 0;

		$path = self::_getRoot();

		foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD) as $p) {
			$total += $p->getSize();
		}

		self::$_size = (float) $total;

		return self::$_size;
	}

	/**
	 * @see https://www.crazyws.fr/dev/fonctions-php/fonction-disk-free-space-et-disk-total-space-pour-ovh-2JMH9.html
	 * @see https://github.com/jdel/sspks/commit/a890e347f32e9e3e50a0dd82398947633872bf38
	 */
	static public function getQuota(): float
	{
		$quota = disk_total_space(self::_getRoot());
		return $quota === false ? (float) \PHP_INT_MAX : (float) $quota;
	}

	static public function getRemainingQuota(): float
	{
		$quota = @disk_free_space(self::_getRoot());
		return $quota === false ? (float) \PHP_INT_MAX : (float) $quota;
	}

	static public function truncate(): void
	{
		Utils::deleteRecursive(self::_getRoot());
	}

Modified src/include/lib/Garradin/Files/Storage/SQLite.php from [0ae2ccbb40] to [95f7674e72].

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
		if ($file->parent) {
			self::touch($file->parent);
		}

		return true;
	}

	static public function getTotalSize(): int
	{
		return (int) DB::getInstance()->firstColumn('SELECT SUM(size) FROM files;');
	}

	/**
	 * @see https://www.crazyws.fr/dev/fonctions-php/fonction-disk-free-space-et-disk-total-space-pour-ovh-2JMH9.html
	 * @see https://github.com/jdel/sspks/commit/a890e347f32e9e3e50a0dd82398947633872bf38
	 */
	static public function getQuota(): int
	{
		$quota = @disk_total_space(DATA_ROOT);
		return $quota === false ? \PHP_INT_MAX : (int) $quota;
	}

	static public function getRemainingQuota(): int
	{
		$quota = @disk_free_space(DATA_ROOT);
		return $quota === false ? \PHP_INT_MAX : (int) $quota;
	}

	static public function truncate(): void
	{
		$db = DB::getInstance();
		$db->exec('DELETE FROM files_contents; DELETE FROM files; VACUUM;');
	}







|

|






|


|


|


|







185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
		if ($file->parent) {
			self::touch($file->parent);
		}

		return true;
	}

	static public function getTotalSize(): float
	{
		return (float) DB::getInstance()->firstColumn('SELECT SUM(size) FROM files;');
	}

	/**
	 * @see https://www.crazyws.fr/dev/fonctions-php/fonction-disk-free-space-et-disk-total-space-pour-ovh-2JMH9.html
	 * @see https://github.com/jdel/sspks/commit/a890e347f32e9e3e50a0dd82398947633872bf38
	 */
	static public function getQuota(): float
	{
		$quota = @disk_total_space(DATA_ROOT);
		return $quota === false ? (float) \PHP_INT_MAX : (float) $quota;
	}

	static public function getRemainingQuota(): float
	{
		$quota = @disk_free_space(DATA_ROOT);
		return $quota === false ? (float) \PHP_INT_MAX : (float) $quota;
	}

	static public function truncate(): void
	{
		$db = DB::getInstance();
		$db->exec('DELETE FROM files_contents; DELETE FROM files; VACUUM;');
	}

Modified src/include/lib/Garradin/Files/Storage/StorageInterface.php from [a0c0da8a45] to [dea0474d61].

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
	 * Moves a file to a new path, when its name or path has changed
	 */
	static public function move(File $file, string $new_path): bool;

	/**
	 * Return total size of used space by files stored in this backed
	 */
	static public function getTotalSize(): int;

	/**
	 * Return total disk space
	 * This will only be called if FILE_STORAGE_QUOTA constant is null
	 */
	static public function getQuota(): int;

	/**
	 * Return available free disk space
	 * This will only be called if FILE_STORAGE_QUOTA constant is null
	 */
	static public function getRemainingQuota(): int;

	/**
	 * Delete all stored content in this backend
	 */
	static public function truncate(): void;

	/**







|





|





|







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
	 * Moves a file to a new path, when its name or path has changed
	 */
	static public function move(File $file, string $new_path): bool;

	/**
	 * Return total size of used space by files stored in this backed
	 */
	static public function getTotalSize(): float;

	/**
	 * Return total disk space
	 * This will only be called if FILE_STORAGE_QUOTA constant is null
	 */
	static public function getQuota(): float;

	/**
	 * Return available free disk space
	 * This will only be called if FILE_STORAGE_QUOTA constant is null
	 */
	static public function getRemainingQuota(): float;

	/**
	 * Delete all stored content in this backend
	 */
	static public function truncate(): void;

	/**

Added src/include/lib/Garradin/Files/Transactions.php version [181762b574].



























































































































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

namespace Garradin\Files;

use Garradin\Entities\Files\File;
use Garradin\DynamicList;

class Transactions
{
	const LIST_COLUMNS = [
		'id' => [
			'select' => 't.id',
			'label' => 'N°',
		],
		'label' => [
			'select' => 't.label',
			'label' => 'Libellé',
		],
		'date' => [
			'label' => 'Date',
			'select' => 't.date',
			'order' => 't.date %s, t.id %1$s',
		],
		'reference' => [
			'label' => 'Pièce comptable',
			'select' => 't.reference',
		],
		'year' => [
			'select' => 'y.label',
			'label' => 'Exercice',
			'order' => 'y.end_date %s, t.date %1$s, t.id %1$s',
		],
		'path' => [
		],
	];

	static public function list()
	{
		Files::syncVirtualTable(File::CONTEXT_TRANSACTION);

		$columns = self::LIST_COLUMNS;

		$tables = sprintf('%s f
			INNER JOIN acc_transactions t ON t.id = f.name
			INNER JOIN acc_years y ON t.id_year = y.id', Files::getVirtualTableName());

		$sum = 0;

		// Only fetch directories with an ID as the name
		$conditions = sprintf('f.parent = \'%s\' AND f.type = %d AND printf("%%d", f.name) = name', File::CONTEXT_TRANSACTION, File::TYPE_DIRECTORY);

		$list = new DynamicList($columns, $tables, $conditions);
		$list->orderBy('year', true);
		$list->setCount('COUNT(DISTINCT t.id)');
		$list->setModifier(function (&$row) {
			$row->date = \DateTime::createFromFormat('!Y-m-d', $row->date);
		});

		return $list;
	}
}

Added src/include/lib/Garradin/Files/Users.php version [2aef448cd6].



































































































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

namespace Garradin\Files;

use Garradin\Entities\Files\File;
use Garradin\DynamicList;
use Garradin\Config;

class Users
{
	const LIST_COLUMNS = [
		'number' => [
			'select' => 'm.numero',
			'label' => 'Numéro',
		],
		'identity' => [
			'select' => '',
			'label' => '',
		],
		'path' => [
		],
	];

	static public function list()
	{
		Files::syncVirtualTable(File::CONTEXT_USER);

		$config = Config::getInstance();
		$name_field = $config->get('champ_identite');
		$champs = $config->get('champs_membres');

		$columns = self::LIST_COLUMNS;
		$columns['identity']['select'] = 'm.' . $name_field;
		$columns['identity']['label'] = $champs->get($name_field)->title;

		$tables = sprintf('%s f INNER JOIN membres m ON m.id = f.name', Files::getVirtualTableName());

		$sum = 0;

		// Only fetch directories with an ID as the name
		$conditions = sprintf('f.parent = \'%s\' AND f.type = %d AND printf("%%d", f.name) = name', File::CONTEXT_USER, File::TYPE_DIRECTORY);

		$list = new DynamicList($columns, $tables, $conditions);
		$list->orderBy('number', false);
		$list->setCount('COUNT(DISTINCT m.id)');

		return $list;
	}
}

Modified src/templates/docs/index.tpl from [8d6804db55] to [a651c91f9d].

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
..
61
62
63
64
65
66
67
68
69



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140
141
142
143

144
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
			<li{if $context == File::CONTEXT_SKELETON} class="current"{/if}><a href="./?p=<?=File::CONTEXT_SKELETON?>">Squelettes du site web</a></li>
		{/if}
	</ul>

</nav>

<nav class="breadcrumbs">













	<ul>
	{foreach from=$breadcrumbs item="name" key="bc_path"}
		<li><a href="?p={$bc_path}">{$name}</a></li>
	{/foreach}
	</ul>

	{if $context == File::CONTEXT_USER && $context_ref}
		{linkbutton href="!membres/fiche.php?id=%d"|args:$context_ref|local_url label="Fiche du membre" shape="user"}
	{elseif $context == File::CONTEXT_TRANSACTION && $context_ref}
		{linkbutton href="!acc/transactions/details.php?id=%d"|args:$context_ref|local_url label="Détails de l'écriture" shape="menu"}
	{/if}

	{if count($breadcrumbs) > 1}
		{linkbutton href="?p=%s"|args:$parent_path label="Retour au répertoire parent" shape="left"}
	{/if}

	<aside class="quota">
		<h4><b>{$quota_left|size_in_bytes}</b> libres sur <i>{$quota_max|size_in_bytes}</i></h4>
		<span class="meter"><span style="width: {$quota_percent}%"></span></span>
	</aside>
</nav>

................................................................................
		Utiliser le <a href="{"!membres/ajouter.php"|local_url}">formulaire de création</a> pour enregistrer un membre.
	{else}
		Utiliser le <a href="{"!acc/transactions/new.php"|local_url}">formulaire de saisie</a> pour créer une nouvelle écriture.
	{/if}
</p>
{/if}

{if count($files)}
<form method="post" action="{"!docs/action.php"|local_url}" target="_dialog">



<table class="list">
	<thead>
		<tr>
			{if $can_delete}
				<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all" /><label for="f_all"></label></td>
			{/if}
			<th>Nom</th>
			<td>Modifié</td>
			<td>Type</td>
			<td>Taille</td>
			<td></td>
		</tr>
	</thead>

	<tbody>

	{foreach from=$files item="file"}
		{if $file.type == $file::TYPE_DIRECTORY}
		<tr>
			{if $can_delete}
			<td class="check">
				{input type="checkbox" name="check[]" value=$file.path}
			</td>
			{/if}
			<th><a href="?p={$file.path}">{$file.name}</a></th>
			<td></td>
			<td>Répertoire</td>
			<td></td>
			<td class="actions">
			{if $can_write && ($context == File::CONTEXT_SKELETON || $context == File::CONTEXT_DOCUMENTS)}
				{linkbutton href="!common/files/rename.php?p=%s"|args:$file.path label="Renommer" shape="minus" target="_dialog"}
			{/if}
			{if $can_delete}
				{linkbutton href="!common/files/delete.php?p=%s"|args:$file.path label="Supprimer" shape="delete" target="_dialog"}
			{/if}
			</td>
		</tr>
		{else}
		</tr>
			{if $can_delete}
			<td class="check">
				{input type="checkbox" name="check[]" value=$file.path}
			</td>
			{/if}
			<td>
				{if $file->canPreview()}
					<a href="{"!common/files/preview.php?p=%s"|local_url|args:$file.path}" target="_dialog" data-mime="{$file.mime}">{$file.name}</a>
				{else}
					<a href="{$file->url(true)}" target="_blank">{$file.name}</a>
				{/if}
			</td>
			<td>{$file.modified|date}</td>
			<td>{$file.mime}</td>
			<td>{$file.size|size_in_bytes}</td>
			<td class="actions">
				{if $can_write && $file->getEditor()}
					{linkbutton href="!common/files/edit.php?p=%s"|args:$file.path label="Modifier" shape="edit" target="_dialog" data-dialog-height="90%"}
				{/if}
				{if $file->canPreview()}
					{linkbutton href="!common/files/preview.php?p=%s"|args:$file.path label="Voir" shape="eye" target="_dialog" data-mime=$file.mime}
				{/if}
				{linkbutton href=$file->url(true) label="Télécharger" shape="download"}
				{if $can_write && ($context == File::CONTEXT_SKELETON || $context == File::CONTEXT_DOCUMENTS)}

					{linkbutton href="!common/files/rename.php?p=%s"|args:$file.path label="Renommer" shape="minus" target="_dialog"}
				{/if}
				{if $can_delete}
					{linkbutton href="!common/files/delete.php?p=%s"|args:$file.path label="Supprimer" shape="delete" target="_dialog"}
				{/if}
			</td>
		</tr>
		{/if}
	{/foreach}

	</tbody>

	{if $can_delete}
	<tfoot>
		<tr>
			<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all2" /><label for="f_all2"></label></td>
			<td class="actions" colspan="5">
				<em>Pour les fichiers cochés :</em>
					<input type="hidden" name="parent" value="{$path}" />
					<select name="action">
						<option value="">— Choisir une action à effectuer —</option>
						{if $context == File::CONTEXT_DOCUMENTS}
						<option value="move">Déplacer</option>
						{/if}
						<option value="delete">Supprimer</option>
					</select>
					<noscript>
						{button type="submit" value="OK" shape="right" label="Valider"}
					</noscript>



				{/if}

















			</td>








		</tr>

	</tfoot>
</table>




</form>
{else}
	<p class="alert block">Il n'y a aucun fichier dans ce répertoire.</p>
{/if}

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







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





<
<
<
<
<
|
<
<
<
<







 







|

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

|

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

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






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
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

139
140
141
142
143
144
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
204
205
206
207
208
209
210
211
212
			<li{if $context == File::CONTEXT_SKELETON} class="current"{/if}><a href="./?p=<?=File::CONTEXT_SKELETON?>">Squelettes du site web</a></li>
		{/if}
	</ul>

</nav>

<nav class="breadcrumbs">
	{if count($breadcrumbs) > 1}
		{linkbutton href="?p=%s"|args:$parent_path label="Retour au répertoire parent" shape="left"}
	{/if}

{if $context == File::CONTEXT_TRANSACTION}
	{if $context_ref}
		{linkbutton href="!acc/transactions/details.php?id=%d"|args:$context_ref|local_url label="Détails de l'écriture" shape="menu"}
	{/if}
{elseif $context == File::CONTEXT_USER}
	{if $context_ref}
		{linkbutton href="!membres/fiche.php?id=%d"|args:$context_ref|local_url label="Fiche du membre" shape="user"}
	{/if}
{else}
	<ul>
	{foreach from=$breadcrumbs item="name" key="bc_path"}
		<li><a href="?p={$bc_path}">{$name}</a></li>
	{/foreach}
	</ul>





{/if}





	<aside class="quota">
		<h4><b>{$quota_left|size_in_bytes}</b> libres sur <i>{$quota_max|size_in_bytes}</i></h4>
		<span class="meter"><span style="width: {$quota_percent}%"></span></span>
	</aside>
</nav>

................................................................................
		Utiliser le <a href="{"!membres/ajouter.php"|local_url}">formulaire de création</a> pour enregistrer un membre.
	{else}
		Utiliser le <a href="{"!acc/transactions/new.php"|local_url}">formulaire de saisie</a> pour créer une nouvelle écriture.
	{/if}
</p>
{/if}

{if count($list)}
<form method="post" action="{"!docs/action.php"|local_url}" target="_dialog">

	{if is_array($list)}

		<table class="list">
			<thead>
				<tr>
					{if $can_delete}
						<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all" /><label for="f_all"></label></td>
					{/if}
					<th>Nom</th>
					<td>Modifié</td>
					<td>Type</td>
					<td>Taille</td>
					<td></td>
				</tr>
			</thead>

			<tbody>

			{foreach from=$list item="file"}
				{if $file.type == $file::TYPE_DIRECTORY}
				<tr>
					{if $can_delete}
					<td class="check">
						{input type="checkbox" name="check[]" value=$file.path}
					</td>
					{/if}
					<th><a href="?p={$file.path}">{$file.name}</a></th>
					<td></td>
					<td>Répertoire</td>
					<td></td>
					<td class="actions">
					{if $can_write && ($context == File::CONTEXT_SKELETON || $context == File::CONTEXT_DOCUMENTS)}
						{linkbutton href="!common/files/rename.php?p=%s"|args:$file.path label="Renommer" shape="minus" target="_dialog"}
					{/if}
					{if $can_delete}
						{linkbutton href="!common/files/delete.php?p=%s"|args:$file.path label="Supprimer" shape="delete" target="_dialog"}
					{/if}
					</td>
				</tr>
				{else}
				</tr>
					{if $can_delete}
					<td class="check">
						{input type="checkbox" name="check[]" value=$file.path}
					</td>
					{/if}
					<td>
						{if $file->canPreview()}
							<a href="{"!common/files/preview.php?p=%s"|local_url|args:$file.path}" target="_dialog" data-mime="{$file.mime}">{$file.name}</a>
						{else}
							<a href="{$file->url(true)}" target="_blank">{$file.name}</a>
						{/if}
					</td>
					<td>{$file.modified|date}</td>
					<td>{$file.mime}</td>
					<td>{$file.size|size_in_bytes}</td>
					<td class="actions">
						{if $can_write && $file->getEditor()}
							{linkbutton href="!common/files/edit.php?p=%s"|args:$file.path label="Modifier" shape="edit" target="_dialog" data-dialog-height="90%"}
						{/if}
						{if $file->canPreview()}
							{linkbutton href="!common/files/preview.php?p=%s"|args:$file.path label="Voir" shape="eye" target="_dialog" data-mime=$file.mime}
						{/if}
						{linkbutton href=$file->url(true) label="Télécharger" shape="download"}

						{if $can_write}
							{linkbutton href="!common/files/rename.php?p=%s"|args:$file.path label="Renommer" shape="minus" target="_dialog"}
						{/if}
						{if $can_delete}
							{linkbutton href="!common/files/delete.php?p=%s"|args:$file.path label="Supprimer" shape="delete" target="_dialog"}
						{/if}
					</td>
				</tr>
				{/if}
			{/foreach}

			</tbody>

			{if $can_delete}
			<tfoot>
				<tr>
					<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all2" /><label for="f_all2"></label></td>
					<td class="actions" colspan="5">
						<em>Pour les fichiers cochés :</em>
							<input type="hidden" name="parent" value="{$path}" />
							<select name="action">
								<option value="">— Choisir une action à effectuer —</option>
								{if $context == File::CONTEXT_DOCUMENTS}
								<option value="move">Déplacer</option>
								{/if}
								<option value="delete">Supprimer</option>
							</select>
							<noscript>
								{button type="submit" value="OK" shape="right" label="Valider"}
							</noscript>
					</td>
				</tr>
			</tfoot>
			{/if}
		</table>
	{elseif $list instanceof \Garradin\DynamicList}

		{include file="common/dynamic_list_head.tpl" check=false}


		{foreach from=$list->iterate() item="item"}
			<tr>
				{if $context == File::CONTEXT_TRANSACTION}
					<td class="num"><a href="{$admin_url}acc/transactions/details.php?id={$item.id}">#{$item.id}</a></td>
					<th><a href="?p={$item.path}">{$item.label}</a></th>
					<td>{$item.date|date_short}</td>
					<td>{$item.reference}</td>
					<td>{$item.year}</td>
					<td class="actions">
						{linkbutton href="!docs/?p=%s"|args:$item.path label="Fichiers" shape="menu"}
						{linkbutton href="!acc/transactions/details.php?id=%d"|args:$item.id label="Écriture" shape="search"}
					</td>
				{else}
					<td class="num"><a href="{$admin_url}membres/fiche.php?id={$item.id}">#{$item.number}</a></td>
					<th><a href="?p={$item.path}">{$item.identity}</a></th>
					<td class="actions">
						{linkbutton href="!docs/?p=%s"|args:$item.path label="Fichiers" shape="menu"}
						{linkbutton href="!membres/fiche.php?id=%d"|args:$item.number label="Fiche membre" shape="user"}
					</td>
				{/if}
			</tr>
		{/foreach}
		</tbody>
		</table>

	{/if}


</form>
{else}
	<p class="alert block">Il n'y a aucun fichier dans ce répertoire.</p>
{/if}

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

Modified src/www/admin/common/files/rename.php from [7b6a31350b] to [608f53cc1f].

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

if (!$file->checkWriteAccess($session)) {
    throw new UserException('Vous n\'avez pas le droit de modifier ce fichier.');
}

$context = $file->context();

if ($context != File::CONTEXT_DOCUMENTS && $context != File::CONTEXT_SKELETON) {
	throw new UserException('Vous n\'avez pas le droit de renommer ce fichier.');
}

$csrf_key = 'file_rename_' . $file->pathHash();

$form->runIf('rename', function () use ($file) {
	$file->changeFileName(f('new_name'));
}, $csrf_key, '!');

$tpl->assign(compact('file', 'csrf_key'));

$tpl->display('common/files/rename.tpl');







|












14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

if (!$file->checkWriteAccess($session)) {
    throw new UserException('Vous n\'avez pas le droit de modifier ce fichier.');
}

$context = $file->context();

if ($context == File::CONTEXT_CONFIG || $context == File::CONTEXT_WEB) {
	throw new UserException('Vous n\'avez pas le droit de renommer ce fichier.');
}

$csrf_key = 'file_rename_' . $file->pathHash();

$form->runIf('rename', function () use ($file) {
	$file->changeFileName(f('new_name'));
}, $csrf_key, '!');

$tpl->assign(compact('file', 'csrf_key'));

$tpl->display('common/files/rename.tpl');

Modified src/www/admin/config/plugins.php from [b0c3c722c2] to [097a1ffa72].

55
56
57
58
59
60
61


    $tpl->assign('liste_telecharges', Plugin::listDownloaded());
    $tpl->assign('liste_installes', Plugin::listInstalled());
}

$tpl->assign('garradin_website', WEBSITE);

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









>
>
55
56
57
58
59
60
61
62
63
    $tpl->assign('liste_telecharges', Plugin::listDownloaded());
    $tpl->assign('liste_installes', Plugin::listInstalled());
}

$tpl->assign('garradin_website', WEBSITE);

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

Plugin::upgradeAllIfRequired();

Modified src/www/admin/docs/index.php from [9eee8d2a3a] to [8becda408f].

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
..
39
40
41
42
43
44
45
46
47
48
<?php

namespace Garradin;

use Garradin\Files\Files;


use Garradin\Entities\Files\File;

require_once __DIR__ . '/_inc.php';

$path = trim(qg('p')) ?: File::CONTEXT_DOCUMENTS;

$files = Files::list($path);



// We consider that the first file has the same rights as the others
if (count($files)) {
	$first = current($files);




	if (!$first->checkReadAccess($session)) {








		throw new UserException('Vous n\'avez pas accès à ce répertoire');
	}


















	$can_delete = $first->checkDeleteAccess($session);
	$can_write = $first->checkWriteAccess($session);
}
else {
	$can_delete = $can_write = false;
}

$context = Files::getContext($path);
$context_ref = Files::getContextRef($path);




$can_create = File::checkCreateAccess($path, $session);
$can_upload = $can_create && (($context == File::CONTEXT_DOCUMENTS || $context == File::CONTEXT_SKELETON)
	|| (($context == File::CONTEXT_USER || $context == File::CONTEXT_TRANSACTION) && $context_ref));
$can_mkdir = $can_create && ($context == File::CONTEXT_DOCUMENTS || $context == File::CONTEXT_SKELETON);

$breadcrumbs = Files::getBreadcrumbs($path);
................................................................................
$parent_path = Utils::dirname($path);

$quota_used = Files::getUsedQuota();
$quota_max = Files::getQuota();
$quota_left = Files::getRemainingQuota();
$quota_percent = $quota_max ? round(($quota_used / $quota_max) * 100) : 100;

$tpl->assign(compact('path', 'files', 'can_write', 'can_delete', 'can_mkdir', 'can_upload', 'context', 'context_ref', 'breadcrumbs', 'parent_path', 'quota_used', 'quota_max', 'quota_percent', 'quota_left'));

$tpl->display('docs/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
..
69
70
71
72
73
74
75
76
77
78
<?php

namespace Garradin;

use Garradin\Files\Files;
use Garradin\Files\Transactions;
use Garradin\Files\Users;
use Garradin\Entities\Files\File;

require_once __DIR__ . '/_inc.php';

$path = trim(qg('p')) ?: File::CONTEXT_DOCUMENTS;

$context = Files::getContext($path);
$context_ref = Files::getContextRef($path);
$list = null;



// Specific lists for some contexts
if ($context == File::CONTEXT_TRANSACTION) {
	if (!$session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_READ)) {
		throw new UserException('Vous n\'avez pas accès à ce répertoire');
	}


	if (!$context_ref) {
		$list = Transactions::list();
		$can_delete = $can_write = false;
	}
}
elseif ($context == File::CONTEXT_USER) {
	if (!$session->canAccess($session::SECTION_USERS, $session::ACCESS_READ)) {
		throw new UserException('Vous n\'avez pas accès à ce répertoire');
	}

	if (!$context_ref) {
		$list = Users::list();
		$can_delete = $can_write = false;
	}
}

if (null == $list) {
	$list = Files::list($path);

	// We consider that the first file has the same rights as the others
	if (count($list)) {
		$first = current($list);

		if (!$first->checkReadAccess($session)) {
			throw new UserException('Vous n\'avez pas accès à ce répertoire');
		}

		$can_delete = $first->checkDeleteAccess($session);
		$can_write = $first->checkWriteAccess($session);
	}
	else {
		$can_delete = $can_write = false;
	}
}


elseif ($list instanceof DynamicList) {
	$list->loadFromQueryString();
}

$can_create = File::checkCreateAccess($path, $session);
$can_upload = $can_create && (($context == File::CONTEXT_DOCUMENTS || $context == File::CONTEXT_SKELETON)
	|| (($context == File::CONTEXT_USER || $context == File::CONTEXT_TRANSACTION) && $context_ref));
$can_mkdir = $can_create && ($context == File::CONTEXT_DOCUMENTS || $context == File::CONTEXT_SKELETON);

$breadcrumbs = Files::getBreadcrumbs($path);
................................................................................
$parent_path = Utils::dirname($path);

$quota_used = Files::getUsedQuota();
$quota_max = Files::getQuota();
$quota_left = Files::getRemainingQuota();
$quota_percent = $quota_max ? round(($quota_used / $quota_max) * 100) : 100;

$tpl->assign(compact('path', 'list', 'can_write', 'can_delete', 'can_mkdir', 'can_upload', 'context', 'context_ref', 'breadcrumbs', 'parent_path', 'quota_used', 'quota_max', 'quota_percent', 'quota_left'));

$tpl->display('docs/index.tpl');