Overview
Comment:Last progress on user forms, now mostly working
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | templates
Files: files | file ages | folders
SHA3-256: 64eda05d62fcd327e6db78a801d979e98603731e93cd408c526b714e88b7bf78
User & Date: bohwaz on 2022-07-29 03:23:46
Other Links: branch diff | manifest | tags
Context
2022-07-29
03:26
Disable edit for now, as it's not done yet check-in: 798e3268e0 user: bohwaz tags: templates
03:23
Last progress on user forms, now mostly working check-in: 64eda05d62 user: bohwaz tags: templates
2022-07-28
23:43
Migrate away from documents, hello user forms check-in: 81d19a359b user: bohwaz tags: templates
Changes

Modified src/include/data/1.2.0_schema.sql from [c64b561387] to [f16cfdc412].

450
451
452
453
454
455
456
457
-- List of forms special templates
(
    id INTEGER NOT NULL PRIMARY KEY,
    id_form INTEGER NOT NULL REFERENCES user_forms (id) ON DELETE CASCADE,
    name TEXT NOT NULL
);

CREATE UNIQUE INDEX IF NOT EXISTS user_forms_templates ON user_forms_templates (id_form, name);







|
450
451
452
453
454
455
456
457
-- List of forms special templates
(
    id INTEGER NOT NULL PRIMARY KEY,
    id_form INTEGER NOT NULL REFERENCES user_forms (id) ON DELETE CASCADE,
    name TEXT NOT NULL
);

CREATE UNIQUE INDEX IF NOT EXISTS user_forms_templates_name ON user_forms_templates (id_form, name);

Modified src/include/lib/Garradin/Entities/UserForm.php from [c2ad7e5999] to [4937cc4b94].

1
2
3
4
5








6
7
8
9

10
11
12
13
14
15
16
17
<?php

namespace Garradin\Entities;

use \Garradin\Entity;









class UserForm extends Entity
{
	const ROOT = File::CONTEXT_SKELETON . '/forms';

	const CONFIG_FILE = 'form.json';

	const CONFIG_TEMPLATE = 'config.html';

	const SNIPPET_TRANSACTION = 'snippets/transaction_details.html';
	const SNIPPET_USER = 'snippets/user_details.html';
	const SNIPPET_HOME_ICON = 'snippets/home_icon.html';





|
>
>
>
>
>
>
>
>




>
|







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\Entities;

use Garradin\Entity;
use Garradin\DB;
use Garradin\Files\Files;
use Garradin\UserTemplate\UserTemplate;
use Garradin\Membres\Session;

use Garradin\Entities\Files\File;

use const Garradin\{ROOT, WWW_URL};

class UserForm extends Entity
{
	const ROOT = File::CONTEXT_SKELETON . '/forms';
	const DIST_ROOT = ROOT . '/skel-dist/forms';
	const META_FILE = 'form.json';

	const CONFIG_TEMPLATE = 'config.html';

	const SNIPPET_TRANSACTION = 'snippets/transaction_details.html';
	const SNIPPET_USER = 'snippets/user_details.html';
	const SNIPPET_HOME_ICON = 'snippets/home_icon.html';

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
	/**
	 * Directory name
	 */
	protected string $name;

	protected string $label;
	protected ?string $description;

	/**
	 * List of snippets/special template files
	 */
	protected array $templates;

	protected \stdClass $config;

	public function selfCheck(): void
	{
		$this->assert(preg_match('/^[a-z]+(?:_[a-z0-9])*$/', $this->name), 'Nom unique de formulaire invalide');
		$this->assert(trim($this->label) !== '', 'Le libellé ne peut rester vide');
	}

	/**
	 * Fills information from form.json file
	 */
	public function updateFromJSON(): bool
	{
		if ($file = Files::get($this->path())) {
			$json = $file->fetch();
		}
		elseif (file_exists($this->distPath())) {
			$json = file_get_contents($this->distPath());
		}
		else {
			return false;
		}







		$this->label = $json->label;
		$this->description = $json->description;

		return true;
	}

	public function updateTemplates(): void
	{
		$check = self::SNIPPETS + [self::CONFIG_TEMPLATE];
		$templates = [];
		$db = DB::getInstance();

		$db->begin();
		$db->delete('user_forms_templates', 'id_form = ' . (int)$this->id());

		foreach ($check as $file => $label) {







<
<
<
<
<
<
|



|








|


|
|





>
>
>
>
>
>

|






|







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
	/**
	 * Directory name
	 */
	protected string $name;

	protected string $label;
	protected ?string $description;






	protected ?\stdClass $config;

	public function selfCheck(): void
	{
		$this->assert(preg_match('/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/', $this->name), 'Nom unique de formulaire invalide: ' . $this->name);
		$this->assert(trim($this->label) !== '', 'Le libellé ne peut rester vide');
	}

	/**
	 * Fills information from form.json file
	 */
	public function updateFromJSON(): bool
	{
		if ($file = Files::get($this->path(self::META_FILE))) {
			$json = $file->fetch();
		}
		elseif (file_exists($this->distPath(self::META_FILE))) {
			$json = file_get_contents($this->distPath(self::META_FILE));
		}
		else {
			return false;
		}

		$json = json_decode($json);

		if (!isset($json->label)) {
			return false;
		}

		$this->label = $json->label;
		$this->description = $json->description ?? null;

		return true;
	}

	public function updateTemplates(): void
	{
		$check = self::SNIPPETS + [self::CONFIG_TEMPLATE => 'Config'];
		$templates = [];
		$db = DB::getInstance();

		$db->begin();
		$db->delete('user_forms_templates', 'id_form = ' . (int)$this->id());

		foreach ($check as $file => $label) {
89
90
91
92
93
94
95
96
97
98
99
100
101
102





103
104
105
106
107
108
109
	public function path(string $file = null): string
	{
		return self::ROOT . '/' . $this->name . ($file ? '/' . $file : '');
	}

	public function distPath(string $file = null): string
	{
		return ROOT . '/skel-dist/' . $this->name . ($file ? '/' . $file : '');
	}

	public function dir(): ?File
	{
		return Files::get(self::ROOT . $this->name);
	}






	public function hasConfig(): bool
	{
		return DB::getInstance()->test('user_forms_templates', 'id_form = ? AND name = ?', $this->id(), self::CONFIG_TEMPLATE);
	}

	public function canDelete(): bool







|






>
>
>
>
>







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
	public function path(string $file = null): string
	{
		return self::ROOT . '/' . $this->name . ($file ? '/' . $file : '');
	}

	public function distPath(string $file = null): string
	{
		return ROOT . '/skel-dist/forms/' . $this->name . ($file ? '/' . $file : '');
	}

	public function dir(): ?File
	{
		return Files::get(self::ROOT . $this->name);
	}

	public function hasDist(): bool
	{
		return file_exists($this->distPath());
	}

	public function hasConfig(): bool
	{
		return DB::getInstance()->test('user_forms_templates', 'id_form = ? AND name = ?', $this->id(), self::CONFIG_TEMPLATE);
	}

	public function canDelete(): bool
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

	public function url(string $file = '', array $params = null)
	{
		if (null !== $params) {
			$params = '?' . http_build_query($params);
		}

		return sprintf('%sform/%s/%s/%s%s', WWW_URL, $this->context, $this->id, $file, $params);
	}

	public function displayWeb(string $file)
	{
		try {
			$this->template($file)->displayWeb();
		}
		catch (Brindille_Exception $e) {
			printf('<div style="border: 5px solid orange; padding: 10px; background: yellow;"><h2>Erreur dans le code du document</h2><p>%s</p></div>', nl2br(htmlspecialchars($e->getMessage())));
		}
	}

	public function fetch(string $file)
	{
		try {
			return $this->template($file)->fetch();


		}
		catch (Brindille_Exception $e) {
			return sprintf('<div style="border: 5px solid orange; padding: 10px; background: yellow;"><h2>Erreur dans le code du document</h2><p>%s</p></div>', nl2br(htmlspecialchars($e->getMessage())));
		}
	}

	public function template(string $file)
	{




		if (!preg_match('!^[\w\d_-]+(?:\.[\w\d_-]+)*$!i', $file)) {
			throw new \InvalidArgumentException('Invalid skeleton name');
		}

		$ut = new UserTemplate(self::ROOT . '/' . $this->name . '/' . $file);


		return $ut;
	}

}







|












|


|
>
>








>
>
>
>
|



|
>





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

	public function url(string $file = '', array $params = null)
	{
		if (null !== $params) {
			$params = '?' . http_build_query($params);
		}

		return sprintf('%sform/%s/%s%s', WWW_URL, $this->name, $file, $params);
	}

	public function displayWeb(string $file)
	{
		try {
			$this->template($file)->displayWeb();
		}
		catch (Brindille_Exception $e) {
			printf('<div style="border: 5px solid orange; padding: 10px; background: yellow;"><h2>Erreur dans le code du document</h2><p>%s</p></div>', nl2br(htmlspecialchars($e->getMessage())));
		}
	}

	public function fetch(string $file, array $variables = [])
	{
		try {
			$t = $this->template($file);
			$t->assignArray($variables);
			return $t->fetch();
		}
		catch (Brindille_Exception $e) {
			return sprintf('<div style="border: 5px solid orange; padding: 10px; background: yellow;"><h2>Erreur dans le code du document</h2><p>%s</p></div>', nl2br(htmlspecialchars($e->getMessage())));
		}
	}

	public function template(string $file)
	{
		if ($file == self::CONFIG_TEMPLATE) {
			Session::getInstance()->requireAccess(Session::SECTION_CONFIG, Session::ACCESS_ADMIN);
		}

		if (!preg_match('!^(?:snippets/)?[\w\d_-]+(?:\.[\w\d_-]+)*$!i', $file)) {
			throw new \InvalidArgumentException('Invalid skeleton name');
		}

		$ut = new UserTemplate('forms/' . $this->name . '/' . $file);
		$ut->assign('form', $this->asArray(false) + ['url' => $this->url()]);

		return $ut;
	}

}

Modified src/include/lib/Garradin/Upgrade.php from [f601f746e5] to [ff6c8a08b2].

455
456
457
458
459
460
461
462

463
464
465
466
467
468
469
			if (version_compare($v, '1.1.28', '<')) {
				$db->createFunction('html_decode', 'htmlspecialchars_decode');
				$db->exec('UPDATE files_search SET content = html_decode(content);');
			}

			if (version_compare($v, '1.2.0', '<')) {
				$db->begin();
				$db->import(ROOT . '/include/data/1.2.0_migration.sql');

				$files = $db->firstColumn('SELECT value FROM config WHERE key = \'files\';');
				$files = json_decode($files);
				$files->signature = null;
				$db->exec(sprintf('REPLACE INTO config (key, value) VALUES (\'files\', %s);', $db->quote(json_encode($files))));
				$db->commit();

				// Move skeletons from skel/ to skel/web/







|
>







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
			if (version_compare($v, '1.1.28', '<')) {
				$db->createFunction('html_decode', 'htmlspecialchars_decode');
				$db->exec('UPDATE files_search SET content = html_decode(content);');
			}

			if (version_compare($v, '1.2.0', '<')) {
				$db->begin();
				$db->import(ROOT . '/include/data/1.2.0_schema.sql');

				$files = $db->firstColumn('SELECT value FROM config WHERE key = \'files\';');
				$files = json_decode($files);
				$files->signature = null;
				$db->exec(sprintf('REPLACE INTO config (key, value) VALUES (\'files\', %s);', $db->quote(json_encode($files))));
				$db->commit();

				// Move skeletons from skel/ to skel/web/

Modified src/include/lib/Garradin/UserTemplate/CommonModifiers.php from [84113d4c5d] to [bd7144db61].

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
			}

			if ((string) $current_value === '0') {
				$current_value = '';
			}

			$currency = Config::getInstance()->get('monnaie');
			$input = sprintf('<nobr><input type="text" pattern="-?[0-9]*([.,][0-9]{1,2})?" inputmode="decimal" size="8" class="money" %s value="%s" /><b>%s</b></nobr>', $attributes_string, htmlspecialchars($current_value), $currency);
		}
		else {
			$value = isset($attributes['value']) ? '' : sprintf(' value="%s"', htmlspecialchars((string)$current_value));
			$input = sprintf('<input type="%s" %s %s />', $type, $attributes_string, $value);
		}

		// No label? then we only want the input without the widget







|







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
			}

			if ((string) $current_value === '0') {
				$current_value = '';
			}

			$currency = Config::getInstance()->get('monnaie');
			$input = sprintf('<nobr><input type="text" pattern="-?[0-9]*([.,][0-9]{1,2})?" inputmode="decimal" size="8" class="money" %s value="%s" /><b>%s</b></nobr>', $attributes_string, htmlspecialchars((string) $current_value), $currency);
		}
		else {
			$value = isset($attributes['value']) ? '' : sprintf(' value="%s"', htmlspecialchars((string)$current_value));
			$input = sprintf('<input type="%s" %s %s />', $type, $attributes_string, $value);
		}

		// No label? then we only want the input without the widget

Modified src/include/lib/Garradin/UserTemplate/Functions.php from [0c1e69f4fe] to [704cf68144].

12
13
14
15
16
17
18

19
20
21
22
23
24
25
use Garradin\Template;
use Garradin\Utils;
use Garradin\UserException;
use Garradin\Web\Skeleton;
use Garradin\Users\Emails;
use Garradin\Files\Files;
use Garradin\Entities\Files\File;


use const Garradin\{ROOT, WWW_URL};

class Functions
{
	const FUNCTIONS_LIST = [
		'include',







>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use Garradin\Template;
use Garradin\Utils;
use Garradin\UserException;
use Garradin\Web\Skeleton;
use Garradin\Users\Emails;
use Garradin\Files\Files;
use Garradin\Entities\Files\File;
use Garradin\Entities\UserForm;

use const Garradin\{ROOT, WWW_URL};

class Functions
{
	const FUNCTIONS_LIST = [
		'include',
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
		}

		$db->exec(sprintf('CREATE INDEX IF NOT EXISTS documents_%s_%s ON documents_data (%s);', $id, implode(',', $indexes)));
	}

	static public function save(array $params, Brindille $tpl, int $line): void
	{
		$id = Utils::basename(Utils::dirname($tpl->_tpl_path));

		if (!$id) {
			throw new Brindille_Exception('Unique document name could not be found');
		}



		if (empty($params['key'])) {

			$params['key'] = Utils::uuid();
		}












		$key = $params['key'];

		unset($params['key']);

		$validate = null;

		if (isset($params['validate_schema'])) {
			$validate = $params['validate_schema'];
			unset($params['validate_schema']);
		}

		$db = DB::getInstance();
		$exists = $db->first('SELECT value FROM documents_data WHERE key = ? AND document = ?;', $key, $id);

















		// Merge before update
		if ($exists) {
			$params = array_merge(json_decode($exists, true), $params);
		}

		if ($validate) {
			$schema = self::read(['file' => $validate_schema], $tpl, $line);

			try {
				$s = JSONSchema::fromString($schema);
				$s->validate($params);
			}
			catch (\RuntimeException $e) {
				throw new Brindille_Exception(sprintf("line %d: error in validating data:\n%s\n\n%s",
					$line, $e->getMessage(), json_encode($params, JSON_PRETTY_PRINT)));
			}
		}

		$params = json_encode($params);



		$db = DB::getInstance();

		$db->preparedQuery('REPLACE INTO documents_data (document, key, value) VALUES (?, ?, ?);', $id, $key, $params);






	}

	static public function mail(array $params, Brindille $tpl, int $line)
	{
		if (empty($params['to'])) {
			throw new Brindille_Exception(sprintf('Ligne %d: argument "to" manquant pour la fonction "mail"', $line));
		}







|

|



>
>
|
>
|
|

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









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



|















|

>
>
|
>
|
>
>
>
>
>
>







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
		}

		$db->exec(sprintf('CREATE INDEX IF NOT EXISTS documents_%s_%s ON documents_data (%s);', $id, implode(',', $indexes)));
	}

	static public function save(array $params, Brindille $tpl, int $line): void
	{
		$name = Utils::basename(Utils::dirname($tpl->_tpl_path));

		if (!$name) {
			throw new Brindille_Exception('Unique document name could not be found');
		}

		$table = 'user_forms_' . $name;

		if (!empty($params['key'])) {
			if ($params['key'] == 'uuid') {
				$params['key'] = Utils::uuid();
			}

			$where = 'key = ?';
			$where_value = $params['key'];
		}
		elseif (!empty($params['id'])) {
			$where = 'id = ?';
			$where_value = $params['id'];
		}
		else {
			throw new Brindille_Exception('Aucun paramètre "id" ou "key" n\'a été renseigné');
		}

		$key = $params['key'] ?? null;

		unset($params['key'], $params['id']);

		$validate = null;

		if (isset($params['validate_schema'])) {
			$validate = $params['validate_schema'];
			unset($params['validate_schema']);
		}

		$db = DB::getInstance();

		if ($key == 'config') {
			$exists = $db->firstColumn(sprintf('SELECT config FROM %s WHERE name = ?;', UserForm::TABLE), $name);
		}
		else {
			$db->exec(sprintf('
				CREATE TABLE IF NOT EXISTS %s (
					id INTEGER NOT NULL PRIMARY KEY,
					key TEXT NULL,
					value TEXT NOT NULL
				);
				CREATE UNIQUE INDEX IF NOT EXISTS %1$s_key ON %1$s (key);', $table));

			$exists = $db->first(sprintf('SELECT value FROM %s WHERE %s AND document = ?;', $table, $where), $where_value, $id);
		}

		$exists = json_decode((string) $exists, true);

		// Merge before update
		if ($exists) {
			$params = array_merge($exists, $params);
		}

		if ($validate) {
			$schema = self::read(['file' => $validate_schema], $tpl, $line);

			try {
				$s = JSONSchema::fromString($schema);
				$s->validate($params);
			}
			catch (\RuntimeException $e) {
				throw new Brindille_Exception(sprintf("line %d: error in validating data:\n%s\n\n%s",
					$line, $e->getMessage(), json_encode($params, JSON_PRETTY_PRINT)));
			}
		}

		$value = json_encode($params);

		if ($key == 'config') {
			$db->update(UserForm::TABLE, ['config' => $value], 'name = :name', compact('name'));
			return;
		}

		if (!$exists) {
			$db->insert($table, compact('value', 'key'));
		}
		else {
			$db->update($table, compact('value'), sprintf('%s = :match', $where), ['match' => $where_value]);
		}
	}

	static public function mail(array $params, Brindille $tpl, int $line)
	{
		if (empty($params['to'])) {
			throw new Brindille_Exception(sprintf('Ligne %d: argument "to" manquant pour la fonction "mail"', $line));
		}
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
	{
		throw new UserException($params['message']);
	}

	static protected function getFilePath(array $params, string $arg_name, UserTemplate $ut, int $line)
	{
		if (empty($params[$arg_name])) {
			throw new Brindille_Exception(sprintf('Ligne %d: argument "%s" manquant pour la fonction "include"', $arg_name, $line));
		}

		if (strpos($params[$arg_name], '..') !== false) {
			throw new Brindille_Exception(sprintf('Ligne %d: argument "%s" invalide', $line, $arg_name));
		}

		$path = $params[$arg_name];







|







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
	{
		throw new UserException($params['message']);
	}

	static protected function getFilePath(array $params, string $arg_name, UserTemplate $ut, int $line)
	{
		if (empty($params[$arg_name])) {
			throw new Brindille_Exception(sprintf('Ligne %d: argument "%s" manquant', $arg_name, $line));
		}

		if (strpos($params[$arg_name], '..') !== false) {
			throw new Brindille_Exception(sprintf('Ligne %d: argument "%s" invalide', $line, $arg_name));
		}

		$path = $params[$arg_name];
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
		}
		catch (\InvalidArgumentException $e) {
			throw new Brindille_Exception(sprintf('Ligne %d : fonction "include" : le fichier à inclure "%s" n\'existe pas', $line, $path));
		}

		$params['included_from'] = array_merge($from, [$path]);

		$include->assignArray($params);
		$include->display();
	}

	static public function http(array $params, UserTemplate $tpl): void
	{
		if (headers_sent()) {
			return;







|







271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
		}
		catch (\InvalidArgumentException $e) {
			throw new Brindille_Exception(sprintf('Ligne %d : fonction "include" : le fichier à inclure "%s" n\'existe pas', $line, $path));
		}

		$params['included_from'] = array_merge($from, [$path]);

		$include->assignArray(array_merge($ut->getAllVariables(), $params));
		$include->display();
	}

	static public function http(array $params, UserTemplate $tpl): void
	{
		if (headers_sent()) {
			return;
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
			if (!isset($codes[$params['code']])) {
				throw new Brindille_Exception('Code HTTP inconnu');
			}

			header(sprintf('HTTP/1.1 %d %s', $params['code'], $codes[$params['code']]), true);
		}

		if (isset($params['type'])) {
			if ($params['type'] == 'pdf') {
				$params['type'] = 'application/pdf';
			}

			header('Content-Type: ' . $params['type'], true);
			$tpl->setContentType($params['type']);
		}

		if (isset($params['download'])) {
			header(sprintf('Content-Disposition: attachment; filename="%s"', Utils::safeFileName($params['download'])), true);
		}
	}
}







|





<







351
352
353
354
355
356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
			if (!isset($codes[$params['code']])) {
				throw new Brindille_Exception('Code HTTP inconnu');
			}

			header(sprintf('HTTP/1.1 %d %s', $params['code'], $codes[$params['code']]), true);
		}

		if (!empty($params['type'])) {
			if ($params['type'] == 'pdf') {
				$params['type'] = 'application/pdf';
			}

			header('Content-Type: ' . $params['type'], true);

		}

		if (isset($params['download'])) {
			header(sprintf('Content-Disposition: attachment; filename="%s"', Utils::safeFileName($params['download'])), true);
		}
	}
}

Modified src/include/lib/Garradin/UserTemplate/Sections.php from [75ce8d8e62] to [d56883e034].

1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
<?php

namespace Garradin\UserTemplate;

use KD2\Brindille_Exception;
use Garradin\Config;
use Garradin\DB;
use Garradin\Utils;

use Garradin\Membres\Session;
use Garradin\Entities\Web\Page;
use Garradin\Web\Web;
use Garradin\Files\Files;
use Garradin\Entities\Files\File;

use const Garradin\WWW_URL;








>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

namespace Garradin\UserTemplate;

use KD2\Brindille_Exception;
use Garradin\Config;
use Garradin\DB;
use Garradin\Utils;
use Garradin\UserException;
use Garradin\Membres\Session;
use Garradin\Entities\Web\Page;
use Garradin\Web\Web;
use Garradin\Files\Files;
use Garradin\Entities\Files\File;

use const Garradin\WWW_URL;
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
		}

		return self::$_cache[$id];
	}

	static public function load(array $params, UserTemplate $tpl, int $line): \Generator
	{
		$id = Utils::basename(Utils::dirname($tpl->_tpl_path));

		if (!$id) {
			throw new Brindille_Exception('Unique document name could not be found');
		}

		if (!array_key_exists('where', $params)) {
			$params['where'] = 'document = :document';
		}
		else {
			$params['where'] = 'document = :document AND ' . $params['where'];
		}

		$params[':document'] = $id;

		if (isset($params['key'])) {
			$params['where'] .= ' AND key = :key';
			$params['limit'] = 1;
			$params[':key'] = $params['key'];
			unset($params['key']);
		}







		$params['select'] = isset($params['select']) ? $params['select'] : 'value AS json';
		$params['tables'] = 'documents_data';


		foreach (self::sql($params, $tpl, $line) as $row) {


			if (isset($row['json'])) {
				$json = json_decode($row['json'], true);

				if (is_array($json)) {
					unset($row['json']);
					$row = array_merge($row, $json);
				}
			}

			yield $row;









		}
	}

	static public function accounts_sums(array $params, UserTemplate $tpl, int $line): \Generator
	{
		$db = DB::getInstance();








|

|



|
|

<
<
<
<
<







>
>
>
>
>
|
>

|

>
|
>
>
|
|

|
|
|
|
|

|
>
>
>
>
>
>
>
>
>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58





59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
		}

		return self::$_cache[$id];
	}

	static public function load(array $params, UserTemplate $tpl, int $line): \Generator
	{
		$name = Utils::basename(Utils::dirname($tpl->_tpl_path));

		if (!$name) {
			throw new Brindille_Exception('Unique document name could not be found');
		}

		if (!isset($params['where'])) {
			$params['where'] = '1';
		}






		if (isset($params['key'])) {
			$params['where'] .= ' AND key = :key';
			$params['limit'] = 1;
			$params[':key'] = $params['key'];
			unset($params['key']);
		}
		elseif (isset($params['id'])) {
			$params['where'] .= ' AND id = :id';
			$params['limit'] = 1;
			$params[':id'] = $params['id'];
			unset($params['id']);
		}

		$params['select'] = isset($params['select']) ? $params['select'] : 'value AS json';
		$params['tables'] = 'user_forms_' . $name;

		try {
			$query = self::sql($params, $tpl, $line);

			foreach ($query as $row) {
				if (isset($row['json'])) {
					$json = json_decode($row['json'], true);

					if (is_array($json)) {
						unset($row['json']);
						$row = array_merge($row, $json);
					}
				}

				yield $row;
			}
		}
		catch (Brindille_Exception $e) {
			// Table does not exists: return nothing
			if (false !== strpos($e->getMessage(), 'no such table: ' . $params['tables'])) {
				return;
			}

			throw $e;
		}
	}

	static public function accounts_sums(array $params, UserTemplate $tpl, int $line): \Generator
	{
		$db = DB::getInstance();

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
		if (empty($params['level']) || !isset($convert[$params['level']])) {
			throw new Brindille_Exception(sprintf("Ligne %d: 'restrict' niveau d'accès inconnu : %s", $line, $params['level'] ?? ''));
		}

		$ok = $session->canAccess($params['section'] ?? '', $convert[$params['level']]);

		if ($ok) {
			yield [];
		}

		if (!empty($params['block'])) {
			throw new UserException('Vous n\'avez pas accès à cette page.');
		}

		return null;







|







239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
		if (empty($params['level']) || !isset($convert[$params['level']])) {
			throw new Brindille_Exception(sprintf("Ligne %d: 'restrict' niveau d'accès inconnu : %s", $line, $params['level'] ?? ''));
		}

		$ok = $session->canAccess($params['section'] ?? '', $convert[$params['level']]);

		if ($ok) {
			return null;
		}

		if (!empty($params['block'])) {
			throw new UserException('Vous n\'avez pas accès à cette page.');
		}

		return null;
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553

			if (!empty($params['debug'])) {
				echo sprintf('<pre style="padding: 5px; background: yellow; white-space: normal;">%s</pre>', htmlspecialchars($statement->getSQL(true)));
			}

			$result = $statement->execute();
		}
		catch (\Exception $e) {
			throw new Brindille_Exception(sprintf("à la ligne %d erreur SQL :\n%s\n\nRequête exécutée :\n%s", $line, $db->lastErrorMsg(), $sql));
		}

		while ($row = $result->fetchArray(\SQLITE3_ASSOC))
		{
			if (isset($params['assign'])) {
				$tpl->assign($params['assign'], $row, 0);
			}

			yield $row;
		}
	}
}







|













547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567

			if (!empty($params['debug'])) {
				echo sprintf('<pre style="padding: 5px; background: yellow; white-space: normal;">%s</pre>', htmlspecialchars($statement->getSQL(true)));
			}

			$result = $statement->execute();
		}
		catch (\KD2\DB\DB_Exception $e) {
			throw new Brindille_Exception(sprintf("à la ligne %d erreur SQL :\n%s\n\nRequête exécutée :\n%s", $line, $db->lastErrorMsg(), $sql));
		}

		while ($row = $result->fetchArray(\SQLITE3_ASSOC))
		{
			if (isset($params['assign'])) {
				$tpl->assign($params['assign'], $row, 0);
			}

			yield $row;
		}
	}
}

Modified src/include/lib/Garradin/UserTemplate/UserForms.php from [3a7808a8ca] to [6bc2c4aa69].

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

namespace Garradin;

use Garradin\Entities\UserForm;









class UserForms
{
	/**
	 * Lists all forms from files and stores a cache
	 */
	static public function refresh(): void
	{
		$existing = DB::getInstance()->getAssoc(sprintf('SELECT id, name FROM %s;', UserForm::TABLE));
		$list = [];

		foreach (Files::list(UserForm::ROOT) as $file) {
			if ($file->type != $file::TYPE_DIRECTORY) {
				continue;
			}

			$list[] = $file->name;
		}

		foreach (glob(self::DIST_SKEL_ROOT . '/*') as $file) {
			if (!is_dir($file)) {
				continue;
			}

			$list[] = Utils::basename($file);
		}

		$list = array_unique($list);
		sort($list);

		$create = array_diff($list, $existing);
		$delete = array_diff($existing, $list);
		$existing = array_diff($list, $create);



		foreach ($create as $name) {
			self::create($name);
		}

		foreach ($delete as $name) {
			self::get($name)->delete();


|



>
>
>
>
>
>
>
>


















|













>
>







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

namespace Garradin\UserTemplate;

use Garradin\Entities\UserForm;

use Garradin\Files\Files;
use Garradin\DB;
use Garradin\Utils;

use const Garradin\ROOT;

use \KD2\DB\EntityManager as EM;

class UserForms
{
	/**
	 * Lists all forms from files and stores a cache
	 */
	static public function refresh(): void
	{
		$existing = DB::getInstance()->getAssoc(sprintf('SELECT id, name FROM %s;', UserForm::TABLE));
		$list = [];

		foreach (Files::list(UserForm::ROOT) as $file) {
			if ($file->type != $file::TYPE_DIRECTORY) {
				continue;
			}

			$list[] = $file->name;
		}

		foreach (glob(UserForm::DIST_ROOT . '/*') as $file) {
			if (!is_dir($file)) {
				continue;
			}

			$list[] = Utils::basename($file);
		}

		$list = array_unique($list);
		sort($list);

		$create = array_diff($list, $existing);
		$delete = array_diff($existing, $list);
		$existing = array_diff($list, $create);

		//echo '<pre>'; var_dump(compact('create', 'delete', 'existing', 'list')); exit;

		foreach ($create as $name) {
			self::create($name);
		}

		foreach ($delete as $name) {
			self::get($name)->delete();
67
68
69
70
71
72
73












74
75
76
77

78
79
80
81
82
83
84
85
86
		return $uf;
	}

	static public function list(): array
	{
		return EM::getInstance(UserForm::class)->all('SELECT * FROM @TABLE ORDER BY label COLLATE NOCASE ASC;');
	}













	static public function listForSnippet(string $snippet): array
	{
		return EM::getInstance(UserForm::class)->all('SELECT * FROM @TABLE

			WHERE templates LIKE ?
			ORDER BY label COLLATE NOCASE ASC;', sprintf('%%"%s%"%', $snippet));
	}

	static public function get(string $name): ?UserForm
	{
		return EM::findOne(UserForm::class, 'SELECT * FROM @TABLE WHERE name = ?;', $name);
	}








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



|
>
|
|







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

	static public function list(): array
	{
		return EM::getInstance(UserForm::class)->all('SELECT * FROM @TABLE ORDER BY label COLLATE NOCASE ASC;');
	}

	static public function getSnippets(string $snippet, array $variables = []): string
	{
		$out = '';

		foreach (self::listForSnippet($snippet) as $form) {
			$out .= $form->fetch($snippet, $variables);
			$out .= PHP_EOL;
		}

		return $out;
	}

	static public function listForSnippet(string $snippet): array
	{
		return EM::getInstance(UserForm::class)->all('SELECT f.* FROM @TABLE f
			INNER JOIN user_forms_templates t ON t.id_form = f.id
			WHERE t.name = ?
			ORDER BY f.label COLLATE NOCASE ASC;', $snippet);
	}

	static public function get(string $name): ?UserForm
	{
		return EM::findOne(UserForm::class, 'SELECT * FROM @TABLE WHERE name = ?;', $name);
	}

Modified src/include/lib/Garradin/UserTemplate/UserTemplate.php from [d7cda2cc08] to [4374d626dd].

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	protected $code = null;
	protected $cache_path = USER_TEMPLATES_CACHE_ROOT;

	protected $escape_default = 'html';

	static protected $root_variables;

	protected $content_type = null;

	static public function getRootVariables()
	{
		if (null !== self::$root_variables) {
			return self::$root_variables;
		}

		static $keys = ['adresse_asso', 'champ_identifiant', 'champ_identite', 'couleur1', 'couleur2', 'email_asso', 'monnaie', 'nom_asso', 'pays', 'site_asso', 'telephone_asso', 'files'];







<
<







29
30
31
32
33
34
35


36
37
38
39
40
41
42
	protected $code = null;
	protected $cache_path = USER_TEMPLATES_CACHE_ROOT;

	protected $escape_default = 'html';

	static protected $root_variables;



	static public function getRootVariables()
	{
		if (null !== self::$root_variables) {
			return self::$root_variables;
		}

		static $keys = ['adresse_asso', 'champ_identifiant', 'champ_identite', 'couleur1', 'couleur2', 'email_asso', 'monnaie', 'nom_asso', 'pays', 'site_asso', 'telephone_asso', 'files'];
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
			header(sprintf('Content-Disposition: attachment; filename="%s"', Utils::safeFileName($filename)));
		}

		header('Content-type: application/pdf');
		Utils::streamPDF($html);
	}

	public function setContentType(string $type): void
	{
		$this->content_type = $type;
	}

	public function displayWeb(): void
	{
		$content = $this->fetch();







		$type = $this->content_type ?: 'text/html';

		header(sprintf('Content-Type: %s;charset=utf-8', $type), true);

		if ($type == 'application/pdf') {
			Utils::streamPDF($content);
		}
		else {
			echo $content;
		}
	}
}







<
<
<
<





>
>
>
>
>
>
|
>










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
			header(sprintf('Content-Disposition: attachment; filename="%s"', Utils::safeFileName($filename)));
		}

		header('Content-type: application/pdf');
		Utils::streamPDF($html);
	}






	public function displayWeb(): void
	{
		$content = $this->fetch();

		foreach (headers_list() as $header) {
			if (preg_match('/^Content-Type: (.*)$/', $header, $match)) {
				$type = $match[1];
				break;
			}
		}

		$type = $type ?? 'text/html';
		header(sprintf('Content-Type: %s;charset=utf-8', $type), true);

		if ($type == 'application/pdf') {
			Utils::streamPDF($content);
		}
		else {
			echo $content;
		}
	}
}

Modified src/include/lib/Garradin/Web/Web.php from [56b13472d4] to [0d0cf483a3].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

namespace Garradin\Web;

use Garradin\Entities\Web\Page;
use Garradin\Entities\Files\File;
use Garradin\Web\Skeleton;
use Garradin\Files\Files;
use Garradin\API;
use Garradin\Config;
use Garradin\DB;
use Garradin\Plugin;
use Garradin\UserForms;
use Garradin\Utils;
use Garradin\UserException;
use Garradin\ValidationException;
use Garradin\Membres\Session;

use KD2\DB\EntityManager as EM;













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

namespace Garradin\Web;

use Garradin\Entities\Web\Page;
use Garradin\Entities\Files\File;
use Garradin\Web\Skeleton;
use Garradin\Files\Files;
use Garradin\API;
use Garradin\Config;
use Garradin\DB;
use Garradin\Plugin;
use Garradin\UserTemplate\UserForms;
use Garradin\Utils;
use Garradin\UserException;
use Garradin\ValidationException;
use Garradin\Membres\Session;

use KD2\DB\EntityManager as EM;

Modified src/skel-dist/forms/_footer.html from [8c4d109e74] to [8dcde0cdfe].

1
2
3
4
5
6

7
8
9
10
11
12
13
</main>

</div>

{{if $_GET.print == 'print'}}
<script type="text/javascript">

window.print();
window.close();
</script>
{{/if}}

</body>
</html>




|

>

<





1
2
3
4
5
6
7
8

9
10
11
12
13
</main>

</div>

{{if $_GET.print == 'yes'}}
<script type="text/javascript">
window.onafterprint = window.close;
window.print();

</script>
{{/if}}

</body>
</html>

Modified src/skel-dist/forms/_header.html from [8b87cdcb91] to [faa609c763].

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
{{if $_GET.print == 'pdf'}}
	{{:http type="pdf" download="%s.pdf"|args:$title}}
{{/if}}
<!DOCTYPE html>
<html>
<head>
	<title>{{$title}}</title>
	<style type="text/css">
	{{:include file="_forms/form.css" page_size=$page_size}}
	</style>
</head>

<body>

{{if !$_GET.print}}
<div id="buttons">
	{{:linkbutton href="%s&print=yes"|args:$self_url label="Imprimer" shape="print"}}
	{{:linkbutton href="%s&print=pdf"|args:$self_url label="Télécharger en PDF" shape="download"}}
</div>
{{/if}}

<div id="page">

<header class="organization">
	{{if $config.files.logo}}








|







|
|







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
{{if $_GET.print == 'pdf'}}
	{{:http type="pdf" download="%s.pdf"|args:$title}}
{{/if}}
<!DOCTYPE html>
<html>
<head>
	<title>{{$title}}</title>
	<style type="text/css">
	{{:include file="forms/form.css" page_size=$page_size}}
	</style>
</head>

<body>

{{if !$_GET.print}}
<div id="buttons">
	{{:linkbutton href="%s&print=yes"|args:$request_url label="Imprimer" shape="print"}}
	{{:linkbutton href="%s&print=pdf"|args:$request_url label="Télécharger en PDF" shape="download"}}
</div>
{{/if}}

<div id="page">

<header class="organization">
	{{if $config.files.logo}}

Modified src/skel-dist/forms/bilan_pc/form.json from [7daf140f3d] to [c544f2f588].

1
2
3
4
5
{
	"name": "Bilan selon le modèle 2020",
	"pdf": true,
	"preview": true
}

|
|
<

1
2
3

4
{
	"name": "Bilan au modèle du plan comptable 2020",
	"description": "Bilan annuel basé sur le plan comptable des associations 2020"

}

Modified src/skel-dist/forms/form.css from [ab562df65c] to [52da7a6aaf].

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

	#buttons {
		display: none;
	}
}

@media screen {
	body {
		background: #666;
	}
	#page {
		margin: 0 auto;
		padding: .5cm;
		background: #fff;
		{{if $page_size == 'A5'}}
		width: 148mm;
		height: 210mm;
		{{else}}
		width: 210mm;
		height: 297mm;
		{{/if}}
	}
}

body {
	background: #fff;
	font-family: Arial, Helvetica, sans-serif;
	color: #000;
	font-size: 10pt;
}

main {
	font-size: 1.1em;







|

















<







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

	#buttons {
		display: none;
	}
}

@media screen {
	html, body {
		background: #666;
	}
	#page {
		margin: 0 auto;
		padding: .5cm;
		background: #fff;
		{{if $page_size == 'A5'}}
		width: 148mm;
		height: 210mm;
		{{else}}
		width: 210mm;
		height: 297mm;
		{{/if}}
	}
}

body {

	font-family: Arial, Helvetica, sans-serif;
	color: #000;
	font-size: 10pt;
}

main {
	font-size: 1.1em;
82
83
84
85
86
87
88






















	text-align: center;
}

#buttons a {
	margin: 1em;
	font-size: 1.2em;
}





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
	text-align: center;
}

#buttons a {
	margin: 1em;
	font-size: 1.2em;
}


a.icn-btn {
    cursor: pointer;
    color: #fff;
    border: 1px solid #ccc;
    background: #333;
    user-select: none;
    display: inline-block;
    font-size: inherit;
    border-radius: .2em;
    padding: .2em .4em;
    margin: .2em .5em;
    white-space: pre;
    transition: box-shadow .3s;
    text-decoration: underline;
}

a.icn-btn:hover {
	text-decoration: none;
	box-shadow: 0px 0px 5px orange;
}

Added src/skel-dist/forms/recu_don/config.html version [96e2ba191b].













































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
{{:admin_header title="Configuration du reçu fiscal"}}

{{if $_POST.save}}
	{{if trim($_POST.accounts) == ''}}
		<p class="error block">Les numéros de comptes éligibles n'ont pas été renseignés.</p>
	{{else}}
		{{:save key="config"
			accounts=$_POST.accounts
		}}
		{{:http redirect="?ok=1"}}
	{{/if}}
{{/if}}

{{if $_GET.ok}}
	<p class="block confirm">Configuration enregistrée.</p>
{{/if}}

<form method="post" action="">

<fieldset>
	<legend>Configuration du modèle de reçu</legend>
	<dl>
		{{:input required=false name="accounts" type="text" label="Numéros de comptes éligibles aux reçus" source=$form.config default="756"}}
		<dd class="help">
			Pour chaque numéro de compte indiqué dans ce champ, le reçu de don sera proposé (en dessous de la fiche de l'écriture).<br />
			Séparer les numéros de compte avec des virgules, par exemple : <tt>754, 756</tt>.<br />
			Laisser vide pour que le reçu de don soit proposé quel que soit le compte.
		</dd>
	</dl>
</fieldset>

<p class="submit">
	{{:button type="submit" name="save" label="Enregistrer" shape="right" class="main"}}
</p>

</form>

{{:admin_footer}}

Modified src/skel-dist/forms/recu_don/index.html from [e5fa711c57] to [fa8b4c3bc1].

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
		<p class="submit">
			{{:button type="submit" name="save" label="Voir le reçu (PDF)" shape="right" class="main"}}
	</form>

	{{:admin_footer}}

{{else}}
	{{#transactions id=$_GET}}
		{{:include file="forms/_header.html" page_size="A5" title="Reçu de don %d - %s"|args:$id:$users_names}}

			<h1>Reçu de don</h1>

			<h4>Référence n°{{$id}} — {{$date|date_short}}</h4>

			<p>L'association «&nbsp;{{$config.nom_asso}}&nbsp;» atteste avoir reçu de la part de&nbsp;:</p>







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
		<p class="submit">
			{{:button type="submit" name="save" label="Voir le reçu (PDF)" shape="right" class="main"}}
	</form>

	{{:admin_footer}}

{{else}}
	{{#transactions id=$_GET.id}}
		{{:include file="forms/_header.html" page_size="A5" title="Reçu de don %d - %s"|args:$id:$users_names}}

			<h1>Reçu de don</h1>

			<h4>Référence n°{{$id}} — {{$date|date_short}}</h4>

			<p>L'association «&nbsp;{{$config.nom_asso}}&nbsp;» atteste avoir reçu de la part de&nbsp;:</p>

Modified src/skel-dist/forms/recu_don/snippets/transaction_details.html from [dce502db35] to [26090d459b].


1

2
3



4
5
6
7
8


9
10
11
12
13
14
15
16
17
18

{{:assign show=false}}


{{if $form.config.accounts}}



	{{#foreach from=$transaction.lines item="line"}}
		{{if $line.account_code|regexp_match:$form.config.accounts}}
			{{:assign show=true}}
		{{/if}}
	{{/foreach}}


{{/if}}

{{if $show}}
	<h2 class="ruler">Reçu de don</h2>
	<p>
		{{:linkbutton href="%s?id=%d"|args:$form.url:$transaction.id target="_dialog" label="Prévisualiser" shape="eye"}}
		{{:linkbutton href="%s?id=%d&print=pdf"|args:$form.url:$transaction.id label="Télécharger en PDF" shape="download"}}
		{{:linkbutton href="%s?id=%d&print=yes"|args:$form.url:$transaction.id target="_blank" label="Imprimer" shape="print"}}
	</p>
{{/if}}
>
|
>


>
>
>
|
|



>
>



|






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
{{if $form.config.accounts === null}}
	{{:assign form.config.accounts='756'}}
{{/if}}

{{if $form.config.accounts}}
	{{:assign show=false}}
	{{:assign accounts=$form.config.accounts|regexp_replace:"/\s*,\s*/":"|"}}
	{{:assign accounts="/%s/"|args:$accounts}}
	{{#foreach from=$transaction_lines item="line"}}
		{{if $line.account_code|regexp_match:$accounts}}
			{{:assign show=true}}
		{{/if}}
	{{/foreach}}
{{else}}
	{{:assign show=true}}
{{/if}}

{{if $show}}
	<h2 class="ruler">{{$form.label}}</h2>
	<p>
		{{:linkbutton href="%s?id=%d"|args:$form.url:$transaction.id target="_dialog" label="Prévisualiser" shape="eye"}}
		{{:linkbutton href="%s?id=%d&print=pdf"|args:$form.url:$transaction.id label="Télécharger en PDF" shape="download"}}
		{{:linkbutton href="%s?id=%d&print=yes"|args:$form.url:$transaction.id target="_blank" label="Imprimer" shape="print"}}
	</p>
{{/if}}

Modified src/skel-dist/forms/recu_fiscal/config.html from [3b59a2f4e6] to [a1f47b7d13].

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
{{if $logged_user.perm_accounting < $access_level.write}}
	{{:error message="Seuls les membres avec accès en écriture à la comptabilité peuvent générer ce document"}}
{{/if}}
{{:admin_header title="Configuration du reçu fiscal"}}

{{if $_POST.save}}
	{{if !$_POST.compte_especes}}
		<p class="error block">Le code du compte d'espèces n'a pas été renseigné.</p>
	{{elseif !$_POST.compte_cheques}}


		<p class="error block">Le code du compte d'espèces n'a pas été renseigné.</p>


	{{else}}
		{{:save key="config"
			objet=$_POST.objet
			compte_especes=$_POST.compte_especes

			compte_cheques=$_POST.compte_cheques
			art200=$_POST.art200
			art238=$_POST.art238
			art885=$_POST.art885
		}}
		<p class="block confirm">Configuration enregistrée.</p>
	{{/if}}
{{/if}}

{{#load key="config"}}
	{{:assign .="cerfa_config"}}
{{/load}}

<form method="post" action="">

<fieldset>
	<legend>Configuration du modèle de reçu fiscal</legend>
	<dl>
		{{:input required=true name="objet" type="textarea" label="Objet de l'association" source=$cerfa_config cols="70" rows="4"}}






		{{:input required=true name="compte_especes" type="text" label="Code du compte de caisse" help="par exemple 530" source=$cerfa_config default="530"}}




		{{:input required=true name="compte_cheques" type="text" label="Code du compte pour les chèques" help="par exemple 5112" source=$cerfa_config default="5112"}}
		<dd class="help">Ces comptes sont utilisés pour déterminer le type de versement utilisé.</dd>



		<dt>Réduction d'impôt éligibles&nbsp;:</dt>
		{{:input type="checkbox" name="art200" value="1" source=$cerfa_config label="Article 200"}}
		{{:input type="checkbox" name="art238" value="1" source=$cerfa_config label="Article 238 bis"}}
		{{:input type="checkbox" name="art885" value="1" source=$cerfa_config label="Article 885-0V bis A"}}
	</dl>
</fieldset>

<p class="submit">
	{{:button type="submit" name="save" label="Enregistrer" shape="right" class="main"}}
</p>

</form>

{{:admin_footer}}
<
<
<



|
|
|
>
>
|
>
>


|
|
>
|




|



|
|
|






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

|
|
|













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



{{:admin_header title="Configuration du reçu fiscal"}}

{{if $_POST.save}}
	{{if $_POST.topic|trim == ''}}
		<p class="error block">L'objet de l'association n'a pas été renseigné.</p>
	{{elseif $_POST.accounts|trim == ''}}
		<p class="error block">Les comptes éligibles aux reçus n'ont pas été renseignés.</p>
	{{elseif $_POST.accounts_cash|trim == ''}}
		<p class="error block">Les comptes d'espèces n'ont pas été renseignés.</p>
	{{elseif $_POST.accounts_cheques|trim == ''}}
		<p class="error block">Les comptes de chèques n'ont pas été renseignés.</p>
	{{else}}
		{{:save key="config"
			accounts=$_POST.accounts
			topic=$_POST.topic
			accounts_cash=$_POST.accounts_cash
			accounts_cheques=$_POST.accounts_cheques
			art200=$_POST.art200
			art238=$_POST.art238
			art885=$_POST.art885
		}}
		{{:http redirect="?ok=1"}}
	{{/if}}
{{/if}}

{{if $_GET.ok}}
	<p class="block confirm">Configuration enregistrée.</p>
{{/if}}

<form method="post" action="">

<fieldset>
	<legend>Configuration du modèle de reçu fiscal</legend>
	<dl>
		{{:input required=true name="topic" type="textarea" label="Objet de l'association" source=$form.config cols="70" rows="4" help="Inscrire ici l'objet de l'association, conformément aux statuts."}}
		{{:input required=false name="accounts" type="text" label="Numéros des comptes éligibles aux reçus" source=$form.config default="756"}}
		<dd class="help">
			Pour chaque numéro de compte indiqué dans ce champ, le reçu de don sera proposé (en dessous de la fiche de l'écriture).<br />
			Séparer les numéros de compte avec des virgules, par exemple : <tt>754, 756</tt>.<br />
			Laisser vide pour que le reçu de don soit proposé quel que soit le compte.
		</dd>
		{{:input required=true name="accounts_cash" type="text" label="Numéros des compte de caisse" source=$form.config default="530"}}
		<dd class="help">
			Utilisé pour cocher automatiquement la bonne case dans le reçu.<br />
			Séparer les numéros de compte avec des virgules, par exemple : <tt>530, 5301</tt>.
		</dd>
		{{:input required=true name="accounts_cheques" type="text" label="Numéros des comptes pour les chèques" source=$form.config default="5112"}}
		<dd class="help">
			Utilisé pour cocher automatiquement la bonne case dans le reçu.<br />
			Séparer les numéros de compte avec des virgules, par exemple : <tt>5112, 5113</tt>.
		</dd>
		<dt>Réduction d'impôt éligibles&nbsp;:</dt>
		{{:input type="checkbox" name="art200" value="1" source=$form.config label="Article 200"}}
		{{:input type="checkbox" name="art238" value="1" source=$form.config label="Article 238 bis"}}
		{{:input type="checkbox" name="art885" value="1" source=$form.config label="Article 885-0V bis A"}}
	</dl>
</fieldset>

<p class="submit">
	{{:button type="submit" name="save" label="Enregistrer" shape="right" class="main"}}
</p>

</form>

{{:admin_footer}}

Modified src/skel-dist/forms/recu_fiscal/index.html from [527766fe40] to [760b9275f6].

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
{{#restrict block=true section="accounting" level="read"}}
{{/restrict}}

{{if !$_GET.id_transaction}}
	{{:error message="Aucun numéro d'écriture n'a été fourni"}}
{{/if}}

{{if !$form.config}}
	{{* Valeurs par défaut *}}
	{{:assign var="form[config]"
		objet=""

		compte_especes="530"
		compte_cheques="5112"
		art200=0
		art238=0
		art885=0}}
{{/if}}

{{#transactions id=$_GET.id}}




<!DOCTYPE html>
<html>
<head>
	<title>Reçu fiscal n°{{$id}} - {{$users_names}}</title>
	<style type="text/css">
	@media print {
		@page {
			size: A4 portrait;
			margin: 0;
			padding: 0;
		}



|





|
|
>
|
|






>
>
>
>



|







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
{{#restrict block=true section="accounting" level="read"}}
{{/restrict}}

{{if !$_GET.id}}
	{{:error message="Aucun numéro d'écriture n'a été fourni"}}
{{/if}}

{{if !$form.config}}
	{{* Valeurs par défaut *}}
	{{:assign var="form.config"
		topic=""
		accounts="756"
		accounts_cash="530"
		accounts_cheques="5112"
		art200=0
		art238=0
		art885=0}}
{{/if}}

{{#transactions id=$_GET.id}}
{{:assign title="Reçu fiscal %d - %s"|args:$id:$users_names}}
{{if $_GET.print == 'pdf'}}
	{{:http type="pdf" download="%s.pdf"|args:$title}}
{{/if}}
<!DOCTYPE html>
<html>
<head>
	<title>{{$title}}</title>
	<style type="text/css">
	@media print {
		@page {
			size: A4 portrait;
			margin: 0;
			padding: 0;
		}
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
	#p2 {
		background-image: url('data:image/png;base64,{{:read base64=true file="./cerfa-2.png"}}');
	}

	b {
		font-weight: normal;
	}





	</style>
</head>

<body>

<div class="page" id="p1">
	<div style="top: 10mm; left: 170mm;">{{$id}}</div>

	<div style="top: 35mm; left: 20mm;">{{$config.nom_asso}}</div>
	<div style="top: 46mm; left: 14mm; height: 11mm; width: 180mm; background: #fff;">{{$config.adresse_asso}}</div>

	<div style="top: 64mm; left: 14mm; background: white; width: 180mm; height: 17mm; overflow: hidden;">{{$cerfa_config.objet|escape|nl2br}}</div>

	<div style="top: 128.5mm; left: 15mm;">X</div>
</div>

<div class="page" id="p2">
{{#transaction_users id_transaction=$id}}
	<div style="top: 18mm; left: 14mm; background: #fff; width: 175mm;padding: 2mm;">{{$name}}</div>
	<div style="top: 13mm; left: 100mm; background: #fff; width: 50mm; height: 6mm;"></div>
	<div style="top: 32mm; left: 18mm;">{{$adresse}}</div>
	<div style="top: 37mm; left: 38mm;">{{$code_postal}}</div>
	<div style="top: 37mm; left: 80mm;">{{$ville}}</div>
{{/transaction_users}}

	<div style="top: 63mm; left: 87mm;">***{{$credit|raw|money}}***</div>
	<div style="top: 73mm; left: 58mm;">{{$credit|money_raw|spell_out_number}} euros</div>

	<div style="top: 82mm; left: 69mm;">{{$date|date:'d'}}</div>
	<div style="top: 82mm; left: 82mm;">{{$date|date:'m'}}</div>
	<div style="top: 82mm; left: 99mm;">{{$date|date:'Y'}}</div>

	<div style="top: 96mm; left: 53mm;">{{if $cerfa_config.art200}}X{{/if}}</div>
	<div style="top: 96mm; left: 103mm;">{{if $cerfa_config.art238}}X{{/if}}</div>
	<div style="top: 96mm; left: 153.0mm;">{{if $cerfa_config.art885}}X{{/if}}</div>

	<div style="top: 113mm; left: 115mm;">X</div>

{{* Abandon de frais par bénévoles *}}
{{if $accounts_codes|strpos:'75412'}}
	<div style="top: 136mm; left: 115mm;">X</div>
{{else}}
	<div style="top: 136mm; left: 15mm;">X</div>
{{/if}}

	{{* Moyen de paiement *}}
	{{if $accounts_codes|strpos:$cerfa_config.compte_especes !== false}}
	<div style="top: 158.2mm; left: 15mm;">X</div>
	{{elseif $accounts_codes|strpos:$cerfa_config.compte_cheques !== false}}
	<div style="top: 158.2mm; left: 57.3mm;">X</div>
	{{else}}
	<div style="top: 158.2mm; left: 115.2mm;">X</div>
	{{/if}}

	{{* Date du jour *}}
	<div style="top: 239mm; left: 139mm;">{{$now|date:'d'}}</div>
	<div style="top: 239mm; left: 148mm;">{{$now|date:'m'}}</div>
	<div style="top: 239mm; left: 156mm;">{{$now|date:'Y'}}</div>

	<div style="top: 243mm; left: 137mm;"><img src="{{:signature_url}}" alt="Signature" style="max-width: 50mm; max-height: 17mm;" /></div>
</div>

{{/transactions}}

</body>
</html>







>
>
>
>
>











|




















|
|
|











|

|










|






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
	#p2 {
		background-image: url('data:image/png;base64,{{:read base64=true file="./cerfa-2.png"}}');
	}

	b {
		font-weight: normal;
	}

	.signature img {
		max-width: 50mm;
		max-height: 17mm;
	}
	</style>
</head>

<body>

<div class="page" id="p1">
	<div style="top: 10mm; left: 170mm;">{{$id}}</div>

	<div style="top: 35mm; left: 20mm;">{{$config.nom_asso}}</div>
	<div style="top: 46mm; left: 14mm; height: 11mm; width: 180mm; background: #fff;">{{$config.adresse_asso}}</div>

	<div style="top: 64mm; left: 14mm; background: white; width: 180mm; height: 17mm; overflow: hidden;">{{$form.config.topic|escape|nl2br}}</div>

	<div style="top: 128.5mm; left: 15mm;">X</div>
</div>

<div class="page" id="p2">
{{#transaction_users id_transaction=$id}}
	<div style="top: 18mm; left: 14mm; background: #fff; width: 175mm;padding: 2mm;">{{$name}}</div>
	<div style="top: 13mm; left: 100mm; background: #fff; width: 50mm; height: 6mm;"></div>
	<div style="top: 32mm; left: 18mm;">{{$adresse}}</div>
	<div style="top: 37mm; left: 38mm;">{{$code_postal}}</div>
	<div style="top: 37mm; left: 80mm;">{{$ville}}</div>
{{/transaction_users}}

	<div style="top: 63mm; left: 87mm;">***{{$credit|raw|money}}***</div>
	<div style="top: 73mm; left: 58mm;">{{$credit|money_raw|spell_out_number}} euros</div>

	<div style="top: 82mm; left: 69mm;">{{$date|date:'d'}}</div>
	<div style="top: 82mm; left: 82mm;">{{$date|date:'m'}}</div>
	<div style="top: 82mm; left: 99mm;">{{$date|date:'Y'}}</div>

	<div style="top: 96mm; left: 53mm;">{{if $form.config.art200}}X{{/if}}</div>
	<div style="top: 96mm; left: 103mm;">{{if $form.config.art238}}X{{/if}}</div>
	<div style="top: 96mm; left: 153.0mm;">{{if $form.config.art885}}X{{/if}}</div>

	<div style="top: 113mm; left: 115mm;">X</div>

{{* Abandon de frais par bénévoles *}}
{{if $accounts_codes|strpos:'75412'}}
	<div style="top: 136mm; left: 115mm;">X</div>
{{else}}
	<div style="top: 136mm; left: 15mm;">X</div>
{{/if}}

	{{* Moyen de paiement *}}
	{{if $accounts_codes|strpos:$form.config.accounts_cash !== false}}
	<div style="top: 158.2mm; left: 15mm;">X</div>
	{{elseif $accounts_codes|strpos:$form.config.accounts_cheques !== false}}
	<div style="top: 158.2mm; left: 57.3mm;">X</div>
	{{else}}
	<div style="top: 158.2mm; left: 115.2mm;">X</div>
	{{/if}}

	{{* Date du jour *}}
	<div style="top: 239mm; left: 139mm;">{{$now|date:'d'}}</div>
	<div style="top: 239mm; left: 148mm;">{{$now|date:'m'}}</div>
	<div style="top: 239mm; left: 156mm;">{{$now|date:'Y'}}</div>

	<div style="top: 243mm; left: 137mm;">{{:signature}}</div>
</div>

{{/transactions}}

</body>
</html>

Added src/skel-dist/forms/recu_fiscal/snippets/transaction_details.html version [f5406982e9].



>
1
{{:include file="forms/recu_don/snippets/transaction_details.html"}}

Modified src/templates/acc/transactions/details.tpl from [edef472c69] to [17617c01f2].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{include file="admin/_head.tpl" title="Écriture n°%d"|args:$transaction.id current="acc"}


{if isset($_GET['created'])}
	<p class="block confirm">
		L'écriture a bien été créée.
	</p>
{/if}

<nav class="tabs">
{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN) && !$transaction->validated && !$tr_year->closed}
	{linkbutton href="edit.php?id=%d"|args:$transaction.id shape="edit" label="Modifier cette écriture"}
	{linkbutton href="delete.php?id=%d"|args:$transaction.id shape="delete" label="Supprimer cette écriture"}
{/if}
{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE)}
	{linkbutton href="new.php?copy=%d"|args:$transaction.id shape="plus" label="Dupliquer cette écriture"}
{/if}
</nav>










|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{include file="admin/_head.tpl" title="Écriture n°%d"|args:$transaction.id current="acc"}


{if isset($_GET['created'])}
	<p class="block confirm">
		L'écriture a bien été créée.
	</p>
{/if}

<nav class="tabs">
{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN) && !$transaction->validated && !$transaction_year->closed}
	{linkbutton href="edit.php?id=%d"|args:$transaction.id shape="edit" label="Modifier cette écriture"}
	{linkbutton href="delete.php?id=%d"|args:$transaction.id shape="delete" label="Supprimer cette écriture"}
{/if}
{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE)}
	{linkbutton href="new.php?copy=%d"|args:$transaction.id shape="plus" label="Dupliquer cette écriture"}
{/if}
</nav>
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
	<dt>Date</dt>
	<dd>{$transaction.date|date:'l j F Y (d/m/Y)'}</dd>
	<dt>Numéro pièce comptable</dt>
	<dd>{if $transaction.reference}{$transaction.reference}{else}-{/if}</dd>

	<dt>Exercice</dt>
	<dd>
		<a href="{$admin_url}acc/reports/ledger.php?year={$transaction.id_year}">{$tr_year.label}</a>
		| Du {$tr_year.start_date|date_short} au {$tr_year.end_date|date_short}
		| <strong>{if $tr_year.closed}Clôturé{else}En cours{/if}</strong>
	</dd>

	<dt>Écriture créée par</dt>
	<dd>
		{if $transaction.id_creator}
			{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_READ)}
				<a href="{$admin_url}membres/fiche.php?id={$transaction.id_creator}">{$creator_name}</a>







|
|
|







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
	<dt>Date</dt>
	<dd>{$transaction.date|date:'l j F Y (d/m/Y)'}</dd>
	<dt>Numéro pièce comptable</dt>
	<dd>{if $transaction.reference}{$transaction.reference}{else}-{/if}</dd>

	<dt>Exercice</dt>
	<dd>
		<a href="{$admin_url}acc/reports/ledger.php?year={$transaction.id_year}">{$transaction_year.label}</a>
		| Du {$transaction_year.start_date|date_short} au {$transaction_year.end_date|date_short}
		| <strong>{if $transaction_year.closed}Clôturé{else}En cours{/if}</strong>
	</dd>

	<dt>Écriture créée par</dt>
	<dd>
		{if $transaction.id_creator}
			{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_READ)}
				<a href="{$admin_url}membres/fiche.php?id={$transaction.id_creator}">{$creator_name}</a>
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
			<td class="money">Crédit</td>
			<td>Libellé ligne</td>
			<td>Référence ligne</td>
			<td>Projet</td>
		</tr>
	</thead>
	<tbody>
		{foreach from=$transaction->getLinesWithAccounts(false) item="line"}
		<tr>
			<td class="num"><a href="{$admin_url}acc/accounts/journal.php?id={$line.id_account}&amp;year={$transaction.id_year}">{$line.account_code}</a></td>
			<td>{$line.account_label}</td>
			<td class="money">{if $line.debit}{$line.debit|escape|money}{/if}</td>
			<td class="money">{if $line.credit}{$line.credit|escape|money}{/if}</td>
			<td>{$line.label}</td>
			<td>{$line.reference}</td>







|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
			<td class="money">Crédit</td>
			<td>Libellé ligne</td>
			<td>Référence ligne</td>
			<td>Projet</td>
		</tr>
	</thead>
	<tbody>
		{foreach from=$transaction_lines item="line"}
		<tr>
			<td class="num"><a href="{$admin_url}acc/accounts/journal.php?id={$line.id_account}&amp;year={$transaction.id_year}">{$line.account_code}</a></td>
			<td>{$line.account_label}</td>
			<td class="money">{if $line.debit}{$line.debit|escape|money}{/if}</td>
			<td class="money">{if $line.credit}{$line.credit|escape|money}{/if}</td>
			<td>{$line.label}</td>
			<td>{$line.reference}</td>
145
146
147
148
149
150
151
152


153

{if $files_edit || count($files)}
<div class="attachments">
	<h3 class="ruler">Fichiers joints</h3>
	{include file="common/files/_context_list.tpl" files=$files edit=$files_edit path=$file_parent}
</div>
{/if}



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








>
>

145
146
147
148
149
150
151
152
153
154
155

{if $files_edit || count($files)}
<div class="attachments">
	<h3 class="ruler">Fichiers joints</h3>
	{include file="common/files/_context_list.tpl" files=$files edit=$files_edit path=$file_parent}
</div>
{/if}

{$snippets|raw}

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

Modified src/templates/admin/_head.tpl from [822d283ed5] to [ebf2bf8ea3].

1
2
3
4
5
6
7
8
9
10
<?php
if (!isset($current)) {
    $current = null;
}
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr" class="{if $dialog}dialog{/if}" data-version="{$version_hash}" data-url="{$admin_url}">
<head>
    <meta charset="utf-8" />
    <meta name="v" content="{$version_hash}" />


|







1
2
3
4
5
6
7
8
9
10
<?php
if (!isset($current)) {
    $current = '';
}
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr" class="{if $dialog}dialog{/if}" data-version="{$version_hash}" data-url="{$admin_url}">
<head>
    <meta charset="utf-8" />
    <meta name="v" content="{$version_hash}" />

Added src/templates/admin/config/forms/index.tpl version [4d1cc84ea3].



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
{include file="admin/_head.tpl" title="Formulaires & modèles" current="config"}

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

<table class="list">
	<tbody>
		{foreach from=$list item="form"}
		<tr>
			<td><h3>{$form.label}</h3>{$form.description|escape|nl2br}</td>
			<td class="actions">
				{linkbutton label="Modifier" href="edit.php?form=%s"|args:$form.name shape="edit" target="_dialog"}
				{if $form->hasConfig()}
					<br />{linkbutton label="Configurer" href=$form->url($form::CONFIG_TEMPLATE) shape="settings" target="_dialog"}
				{/if}
				{if $form->canDelete()}
					<br />
					{if $form->hasDist()}
						{linkbutton label="Remettre à zéro" href="delete.php?form=%s"|args:$form.name shape="reset" target="_dialog"}
					{else}
						{linkbutton label="Supprimer" href="delete.php?form=%s"|args:$form.name shape="delete" target="_dialog"}
					{/if}
				{/if}
			</td>
		</tr>
		{/foreach}
	</tbody>
</table>

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

Modified src/www/admin/acc/transactions/details.php from [48aefb0f02] to [0d689a34de].

1
2
3
4


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31



32
<?php
namespace Garradin;

use Garradin\Accounting\Transactions;



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

$transaction = Transactions::get((int) qg('id'));

if (!$transaction) {
	throw new UserException('Cette écriture n\'existe pas');
}

$csrf_key = 'details_' . $transaction->id();

$form->runIf('mark_paid', function () use ($transaction) {
	$transaction->markPaid();
	$transaction->save();
}, $csrf_key, Utils::getSelfURI());

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

$tpl->assign('files', $transaction->listFiles());
$tpl->assign('tr_year', $transaction->year());
$tpl->assign('creator_name', $transaction->id_creator ? (new Membres)->getNom($transaction->id_creator) : null);

$tpl->assign('files_edit', $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE));
$tpl->assign('file_parent', $transaction->getAttachementsDirectory());
$tpl->assign('related_users', $transaction->listLinkedUsers());
$tpl->assign('related_transactions', $transaction->listRelatedTransactions());





$tpl->display('acc/transactions/details.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
<?php
namespace Garradin;

use Garradin\Accounting\Transactions;
use Garradin\UserTemplate\UserForms;
use Garradin\Entities\UserForm;

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

$transaction = Transactions::get((int) qg('id'));

if (!$transaction) {
	throw new UserException('Cette écriture n\'existe pas');
}

$csrf_key = 'details_' . $transaction->id();

$form->runIf('mark_paid', function () use ($transaction) {
	$transaction->markPaid();
	$transaction->save();
}, $csrf_key, Utils::getSelfURI());

$variables = compact('csrf_key', 'transaction') + [
	'transaction_lines'    => $transaction->getLinesWithAccounts(false),
	'transaction_year'     => $transaction->year(),
	'files'                => $transaction->listFiles(),
	'creator_name'         => $transaction->id_creator ? (new Membres)->getNom($transaction->id_creator) : null,

	'files_edit'           => $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE),
	'file_parent'          => $transaction->getAttachementsDirectory(),
	'related_users'        => $transaction->listLinkedUsers(),
	'related_transactions' => $transaction->listRelatedTransactions()
];

$tpl->assign($variables);
$tpl->assign('snippets', UserForms::getSnippets(UserForm::SNIPPET_TRANSACTION, $variables));

$tpl->display('acc/transactions/details.tpl');

Added src/www/admin/config/forms/index.php version [0fba1ce665].





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace Garradin;

use Garradin\UserTemplate\UserForms;

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

UserForms::refresh();

$list = UserForms::list();

$tpl->assign(compact('list'));

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