Comment: | Merge trunk |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | templates |
Files: | files | file ages | folders |
SHA3-256: |
bc5af9deb3c12e6bcc44970bbe047c1d |
User & Date: | bohwaz on 2021-12-10 12:18:58 |
Other Links: | branch diff | manifest | tags |
2021-12-10
| ||
12:24 | Another try at templates check-in: e114b79178 user: bohwaz tags: templates | |
12:18 | Merge trunk check-in: bc5af9deb3 user: bohwaz tags: templates | |
12:16 | Generate change event from date picker check-in: 2850e9dfe9 user: bohwaz tags: trunk, stable | |
2021-11-25
| ||
12:58 | Merge trunk check-in: b65e1fc90e user: bohwaz tags: templates | |
Modified src/VERSION from [37dd332b3b] to [64a3dc4a4c].
|
| | | 1 | 1.1.16 |
Modified src/include/init.php from [df903d941d] to [63c4993e30].
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 228 229 230 231 232 | } } if (!defined('Garradin\ADMIN_BACKGROUND_IMAGE')) { define('Garradin\ADMIN_BACKGROUND_IMAGE', ADMIN_URL . 'static/gdin_bg.png'); } const WEBSITE = 'https://fossil.kd2.org/garradin/'; const PLUGINS_URL = 'https://garradin.eu/plugins/list.json'; const USER_TEMPLATES_CACHE_ROOT = CACHE_ROOT . '/utemplates'; const STATIC_CACHE_ROOT = CACHE_ROOT . '/static'; const SHARED_USER_TEMPLATES_CACHE_ROOT = SHARED_CACHE_ROOT . '/utemplates'; const SMARTYER_CACHE_ROOT = SHARED_CACHE_ROOT . '/compiled'; | > | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | } } if (!defined('Garradin\ADMIN_BACKGROUND_IMAGE')) { define('Garradin\ADMIN_BACKGROUND_IMAGE', ADMIN_URL . 'static/gdin_bg.png'); } const HELP_URL = 'https://garradin.eu/aide'; const WEBSITE = 'https://fossil.kd2.org/garradin/'; const PLUGINS_URL = 'https://garradin.eu/plugins/list.json'; const USER_TEMPLATES_CACHE_ROOT = CACHE_ROOT . '/utemplates'; const STATIC_CACHE_ROOT = CACHE_ROOT . '/static'; const SHARED_USER_TEMPLATES_CACHE_ROOT = SHARED_CACHE_ROOT . '/utemplates'; const SMARTYER_CACHE_ROOT = SHARED_CACHE_ROOT . '/compiled'; |
︙ | ︙ |
Modified src/include/lib/Garradin/Accounting/Graph.php from [af4ec17e7a] to [b470030203].
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | use KD2\Graphics\SVG\Plot; use KD2\Graphics\SVG\Plot_Data; use KD2\Graphics\SVG\Pie; use KD2\Graphics\SVG\Pie_Data; class Graph { const URL_LIST = [ ADMIN_URL . 'acc/reports/graph_plot.php?type=assets&%s' => 'Évolution banques et caisses', ADMIN_URL . 'acc/reports/graph_plot.php?type=result&%s' => 'Évolution dépenses et recettes', ADMIN_URL . 'acc/reports/graph_plot.php?type=debts&%s' => 'Évolution créances (positif) et dettes (négatif)', ADMIN_URL . 'acc/reports/graph_pie.php?type=revenue&%s' => 'Répartition recettes', | > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | use KD2\Graphics\SVG\Plot; use KD2\Graphics\SVG\Plot_Data; use KD2\Graphics\SVG\Pie; use KD2\Graphics\SVG\Pie_Data; use KD2\Graphics\SVG\Bar; use KD2\Graphics\SVG\Bar_Data_Set; class Graph { const URL_LIST = [ ADMIN_URL . 'acc/reports/graph_plot.php?type=assets&%s' => 'Évolution banques et caisses', ADMIN_URL . 'acc/reports/graph_plot.php?type=result&%s' => 'Évolution dépenses et recettes', ADMIN_URL . 'acc/reports/graph_plot.php?type=debts&%s' => 'Évolution créances (positif) et dettes (négatif)', ADMIN_URL . 'acc/reports/graph_pie.php?type=revenue&%s' => 'Répartition recettes', |
︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 62 63 64 | 'revenue' => ['position' => Account::REVENUE, 'exclude_type' => Account::TYPE_VOLUNTEERING], 'expense' => ['position' => Account::EXPENSE, 'exclude_type' => Account::TYPE_VOLUNTEERING], 'assets' => ['type' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING]], ]; const WEEKLY_INTERVAL = 604800; // 7 days const MONTHLY_INTERVAL = 2635200; // 1 month static public function plot(string $type, array $criterias, int $interval = self::WEEKLY_INTERVAL, int $width = 700) { if (!array_key_exists($type, self::PLOT_TYPES)) { throw new \InvalidArgumentException('Unknown type'); } | > > > > > > > > > > > > > > > > > | 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 | 'revenue' => ['position' => Account::REVENUE, 'exclude_type' => Account::TYPE_VOLUNTEERING], 'expense' => ['position' => Account::EXPENSE, 'exclude_type' => Account::TYPE_VOLUNTEERING], 'assets' => ['type' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING]], ]; const WEEKLY_INTERVAL = 604800; // 7 days const MONTHLY_INTERVAL = 2635200; // 1 month static public function clearCache(string $type, array $criterias, int $interval = self::WEEKLY_INTERVAL, int $width = 700): void { if (!array_key_exists($type, self::PLOT_TYPES)) { throw new \InvalidArgumentException('Unknown type'); } $cache_id = sha1('plot' . json_encode(func_get_args())); Static_Cache::remove($cache_id); } static public function clearCacheAllYears(): void { self::clearCache('assets', [], Graph::MONTHLY_INTERVAL, 600); self::clearCache('result', [], Graph::MONTHLY_INTERVAL, 600); } static public function plot(string $type, array $criterias, int $interval = self::WEEKLY_INTERVAL, int $width = 700) { if (!array_key_exists($type, self::PLOT_TYPES)) { throw new \InvalidArgumentException('Unknown type'); } |
︙ | ︙ | |||
175 176 177 178 179 180 181 182 183 184 185 186 187 188 | { $pie->add(new Pie_Data(abs($others) / 100, 'Autres', '#ccc')); } $pie->togglePercentage(true); $out = $pie->output(); Static_Cache::store($cache_id, $out); return $out; } static protected function getColors() | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | { $pie->add(new Pie_Data(abs($others) / 100, 'Autres', '#ccc')); } $pie->togglePercentage(true); $out = $pie->output(); Static_Cache::store($cache_id, $out); return $out; } static public function bar(string $type, array $criterias) { if (!array_key_exists($type, self::PLOT_TYPES)) { throw new \InvalidArgumentException('Unknown type'); } $cache_id = sha1('bar' . json_encode(func_get_args())); if (!Static_Cache::expired($cache_id)) { return Static_Cache::get($cache_id); } $bar = new Bar(600, 300); $lines = self::PLOT_TYPES[$type]; $data = []; $colors = self::getColors(); foreach ($lines as $label => $line_criterias) { $color = current($colors); next($colors); $line_criterias = array_merge($criterias, $line_criterias); $years = Reports::getSumsPerYear($line_criterias); if (count($years) < 1) { continue; } // Invert sums for banks, cash, etc. if ('assets' === $type || 'debts' === $type || ('result' === $type && $line_criterias['position'] == Account::EXPENSE)) { array_walk($years, function (&$v) { $v->sum = $v->sum * -1; }); } array_walk($years, function (&$v) { $v->sum = (int)$v->sum/100; }); foreach ($years as $year) { $start = Utils::date_fr($year->start_date, 'Y'); $end = Utils::date_fr($year->end_date, 'Y'); $year_label = $start == $end ? $start : sprintf('%s-%s', $start, substr($end, -2)); $year_id = $year_label . '-' . $year->id; if (!isset($data[$year_id])) { $data[$year_id] = new Bar_Data_Set($year_label); } $data[$year_id]->add($year->sum, $label, $color); } } ksort($data); foreach ($data as $group) { $bar->add($group); } $out = $bar->output(); Static_Cache::store($cache_id, $out); return $out; } static protected function getColors() |
︙ | ︙ |
Modified src/include/lib/Garradin/Accounting/Reports.php from [d37a6b0099] to [b455a0ddd1].
︙ | ︙ | |||
156 157 158 159 160 161 162 163 164 165 166 167 168 169 | return; } $current->items[] = $total($current, $by_year); yield $current; } static public function getSumsByInterval(array $criterias, int $interval) { $where = self::getWhereClause($criterias); $where_interval = !empty($criterias['year']) ? sprintf(' WHERE id_year = %d', $criterias['year']) : ''; $db = DB::getInstance(); | > > > > > > > > > > > > > > > | 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 | return; } $current->items[] = $total($current, $by_year); yield $current; } static public function getSumsPerYear(array $criterias): array { $where = self::getWhereClause($criterias); $sql = sprintf('SELECT y.id, y.start_date, y.end_date, y.label, SUM(l.credit) - SUM(l.debit) AS sum FROM acc_transactions t INNER JOIN acc_transactions_lines l ON l.id_transaction = t.id INNER JOIN acc_accounts a ON a.id = l.id_account INNER JOIN acc_years y ON y.id = t.id_year WHERE %s GROUP BY t.id_year ORDER BY y.end_date;', $where, $where); return DB::getInstance()->getGrouped($sql); } static public function getSumsByInterval(array $criterias, int $interval) { $where = self::getWhereClause($criterias); $where_interval = !empty($criterias['year']) ? sprintf(' WHERE id_year = %d', $criterias['year']) : ''; $db = DB::getInstance(); |
︙ | ︙ | |||
234 235 236 237 238 239 240 | { $where = self::getWhereClause($criterias); $order = $order ?: 'a.code COLLATE NOCASE'; $reverse = $reverse ? '* -1' : ''; $remove_zero = $remove_zero ? 'HAVING sum != 0' : ''; | < | | > > | > > | > > > > > > > > > > > > > > > > > > > > > < < < | | < < < > > | > > > > > > | > | | | < < < < > > | > > | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | { $where = self::getWhereClause($criterias); $order = $order ?: 'a.code COLLATE NOCASE'; $reverse = $reverse ? '* -1' : ''; $remove_zero = $remove_zero ? 'HAVING sum != 0' : ''; $query = 'SELECT a.code, a.id, a.label, a.position, SUM(l.credit) AS credit, SUM(l.debit) AS debit, SUM(l.credit - l.debit) %s AS sum FROM %s l INNER JOIN %s t ON t.id = l.id_transaction INNER JOIN %s a ON a.id = l.id_account WHERE %s GROUP BY l.id_account %s ORDER BY %s'; // Find sums, link them to accounts $sql = sprintf($query, $reverse, Line::TABLE, Transaction::TABLE, Account::TABLE, $where, $remove_zero, $order); $db = DB::getInstance(); $out = $db->getGrouped($sql); // SQLite does not support OUTER JOIN yet :( if (isset($criterias['compare_year'])) { $where = self::getWhereClause(array_merge($criterias, ['year' => (int)$criterias['compare_year']])); $sql = sprintf($query, $reverse, Line::TABLE, Transaction::TABLE, Account::TABLE, $where, $remove_zero, $order); foreach ($db->iterate($sql) as $row) { if (!isset($out[$row->code])) { $row->sum2 = $row->sum; $row->sum = 0; $row->change = null; $out[$row->code] = $row; } else { $out[$row->code]->sum2 = $row->sum; $out[$row->code]->change = ($out[$row->code]->sum - $row->sum); } } } return $out; } static public function getTrialBalance(array $criterias): array { return self::getClosingSumsWithAccounts($criterias, null, false, false); } static public function getBalanceSheet(array $criterias): array { $accounts = ['asset' => [], 'liability' => []]; $sums = $sums2 = $change = ['asset' => 0, 'liability' => 0]; $position_criteria = ['position' => [Account::ASSET, Account::LIABILITY, Account::ASSET_OR_LIABILITY]]; $list = self::getClosingSumsWithAccounts($criterias + $position_criteria); //var_dump($list); exit; foreach ($list as $row) { if ($row->sum == 0) { // Ignore empty accounts continue; } $position = $row->position; if ($position == Account::ASSET_OR_LIABILITY) { $position = $row->sum < 0 ? 'asset' : 'liability'; $row->sum = abs($row->sum); $row->sum2 = isset($row->sum2) ? abs($row->sum2) : 0; $row->change = isset($row->change) ? $row->change * -1 : 0; } elseif ($position == Account::ASSET) { // reverse number for assets $row->sum *= -1; $row->sum2 = isset($row->sum2) ? $row->sum2 * -1 : 0; $position = 'asset'; } else { $position = 'liability'; } $accounts[$position][] = $row; } $result = self::getResult($criterias); if ($result != 0) { $accounts['liability'][] = (object) [ 'id' => null, 'label' => $result > 0 ? 'Résultat de l\'exercice courant (excédent)' : 'Résultat de l\'exercice courant (perte)', 'sum' => $result, ]; } // Calculate the total sum for assets and liabilities foreach ($accounts as $position => $rows) { $sum = 0; $sum2 = 0; foreach ($rows as $row) { $sum += $row->sum; $sum2 += $row->sum2 ?? 0; } $sums[$position] = $sum; $sums2[$position] = $sum2; $change[$position] = $sum - $sum2; } return compact('sums', 'sums2', 'change', 'accounts'); } /** * Return list of favorite accounts (accounts with a type), grouped by type, with their current sum * @return \Generator list of accounts grouped by type */ static public function getClosingSumsFavoriteAccounts(array $criterias): \Generator |
︙ | ︙ | |||
486 487 488 489 490 491 492 | } static public function getStatement(array $criterias): array { $revenue = Reports::getClosingSumsWithAccounts($criterias + ['position' => Account::REVENUE]); $expense = Reports::getClosingSumsWithAccounts($criterias + ['position' => Account::EXPENSE], null, true); | | | > > > > > > > > > > > | > | 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 | } static public function getStatement(array $criterias): array { $revenue = Reports::getClosingSumsWithAccounts($criterias + ['position' => Account::REVENUE]); $expense = Reports::getClosingSumsWithAccounts($criterias + ['position' => Account::EXPENSE], null, true); $get_sum = function (array $in, string $key = 'sum'): int { $sum = 0; foreach ($in as $row) { $sum += $row->$key ?? 0; } return $sum; }; $revenue_sum = $get_sum($revenue); $expense_sum = $get_sum($expense); $result = $revenue_sum - $expense_sum; $revenue_sum2 = $expense_sum2 = $result2 = $revenue_change = $expense_change = $result_change = null; if (isset($criterias['compare_year'])) { $revenue_sum2 = $get_sum($revenue, 'sum2'); $revenue_change = $revenue_sum - $revenue_sum2; $expense_sum2 = $get_sum($expense, 'sum2'); $expense_change = $expense_sum - $expense_sum2; $result2 = $revenue_sum2 - $expense_sum2; $result_change = $result < 0 ? $result2 - $result : $result - $result2; } return compact('revenue', 'expense', 'revenue_sum', 'expense_sum', 'result', 'revenue_sum2', 'expense_sum2', 'result2', 'revenue_change', 'expense_change', 'result_change'); } } |
Modified src/include/lib/Garradin/Accounting/Transactions.php from [7839877bed] to [31743804a6].
︙ | ︙ | |||
307 308 309 310 311 312 313 314 315 316 317 318 319 320 | $e->setDetails($transaction->asDetailsArray()); } throw $e; } $db->commit(); } static public function importCustom(Year $year, CSV_Custom $csv, int $user_id) { if ($year->closed) { throw new \InvalidArgumentException('Closed year'); } | > > | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | $e->setDetails($transaction->asDetailsArray()); } throw $e; } $db->commit(); Graph::clearCacheAllYears(); } static public function importCustom(Year $year, CSV_Custom $csv, int $user_id) { if ($year->closed) { throw new \InvalidArgumentException('Closed year'); } |
︙ | ︙ | |||
394 395 396 397 398 399 400 401 402 403 404 405 406 407 | $e->setDetails($transaction->asDetailsArray()); } throw $e; } $db->commit(); } static public function setAnalytical(?int $id_analytical, ?array $transactions = null, ?array $lines = null) { $db = DB::getInstance(); if (null !== $id_analytical && !$db->test(Account::TABLE, 'type = ? AND id = ?', Account::TYPE_ANALYTICAL, $id_analytical)) { | > > | 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | $e->setDetails($transaction->asDetailsArray()); } throw $e; } $db->commit(); Graph::clearCacheAllYears(); } static public function setAnalytical(?int $id_analytical, ?array $transactions = null, ?array $lines = null) { $db = DB::getInstance(); if (null !== $id_analytical && !$db->test(Account::TABLE, 'type = ? AND id = ?', Account::TYPE_ANALYTICAL, $id_analytical)) { |
︙ | ︙ |
Modified src/include/lib/Garradin/Accounting/Years.php from [9ef2231749] to [d161a36703].
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | return DB::getInstance()->getAssoc('SELECT id, label FROM acc_years ORDER BY end_date;'); } static public function listClosedAssoc() { return DB::getInstance()->getAssoc('SELECT id, label FROM acc_years WHERE closed = 1 ORDER BY end_date;'); } static public function listClosed() { $em = EntityManager::getInstance(Year::class); return $em->all('SELECT * FROM @TABLE WHERE closed = 1 ORDER BY end_date;'); } | > > > > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | return DB::getInstance()->getAssoc('SELECT id, label FROM acc_years ORDER BY end_date;'); } static public function listClosedAssoc() { return DB::getInstance()->getAssoc('SELECT id, label FROM acc_years WHERE closed = 1 ORDER BY end_date;'); } static public function listClosedAssocExcept(int $id) { return DB::getInstance()->getAssoc('SELECT id, label FROM acc_years WHERE closed = 1 AND id != ? ORDER BY end_date DESC;', $id); } static public function listClosed() { $em = EntityManager::getInstance(Year::class); return $em->all('SELECT * FROM @TABLE WHERE closed = 1 ORDER BY end_date;'); } |
︙ | ︙ |
Modified src/include/lib/Garradin/Config.php from [977d9aeaa8] to [6fe9dd5c30].
1 2 3 4 5 6 7 8 9 10 11 12 | <?php namespace Garradin; use Garradin\Files\Files; use Garradin\Entities\Files\File; use Garradin\Membres\Champs; use KD2\SMTP; class Config extends Entity { | > | | | > > > > > > > > > > > > | 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 | <?php namespace Garradin; use Garradin\Files\Files; use Garradin\Entities\Files\File; use Garradin\Membres\Champs; use KD2\SMTP; use KD2\Graphics\Image; class Config extends Entity { const FILES = [ 'admin_background' => File::CONTEXT_CONFIG . '/admin_bg.png', 'admin_homepage' => File::CONTEXT_CONFIG . '/admin_homepage.skriv', 'admin_css' => File::CONTEXT_CONFIG . '/admin.css', 'logo' => File::CONTEXT_CONFIG . '/logo.png', 'icon' => File::CONTEXT_CONFIG . '/icon.png', 'favicon' => File::CONTEXT_CONFIG . '/favicon.png', ]; const FILES_TYPES = [ 'admin_background' => 'image', 'admin_css' => 'code', 'admin_homepage' => 'web', 'logo' => 'image', 'icon' => 'image', 'favicon' => 'image', ]; protected $nom_asso; protected $adresse_asso; protected $email_asso; protected $telephone_asso; protected $site_asso; |
︙ | ︙ | |||
36 37 38 39 40 41 42 | protected $last_chart_change; protected $last_version_check; protected $couleur1; protected $couleur2; | | < < | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | protected $last_chart_change; protected $last_version_check; protected $couleur1; protected $couleur2; protected $files = []; protected $site_disabled; protected $_types = [ 'nom_asso' => 'string', 'adresse_asso' => '?string', 'email_asso' => 'string', |
︙ | ︙ | |||
67 68 69 70 71 72 73 | 'champ_identite' => 'string', 'last_chart_change' => '?int', 'last_version_check' => '?string', 'couleur1' => '?string', 'couleur2' => '?string', | | < | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 'champ_identite' => 'string', 'last_chart_change' => '?int', 'last_version_check' => '?string', 'couleur1' => '?string', 'couleur2' => '?string', 'files' => 'array', 'site_disabled' => 'bool', ]; static protected $_instance = null; static public function getInstance() |
︙ | ︙ | |||
129 130 131 132 133 134 135 | { if (!count($this->_modified)) { return true; } $this->selfCheck(); | | < < < < | < < < < < < < < < < < < > > > > > | 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 | { if (!count($this->_modified)) { return true; } $this->selfCheck(); $values = $this->modifiedProperties(true); $db = DB::getInstance(); $db->begin(); foreach ($values as $key => $value) { $db->preparedQuery('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?);', $key, $value); } if (!empty($values['champ_identifiant'])) { // Regenerate login index $db->exec('DROP INDEX IF EXISTS users_id_field;'); $config = Config::getInstance(); $champs = $config->get('champs_membres'); $champs->createIndexes(); } $db->commit(); if (isset($values['couleur1']) || isset($values['couleur2'])) { // Reset graph cache Static_Cache::clean(0); } $this->_modified = []; return true; } public function delete(): bool |
︙ | ︙ | |||
189 190 191 192 193 194 195 | if (!isset($source['couleur1'], $source['couleur2']) || ($source['couleur1'] == ADMIN_COLOR1 && $source['couleur2'] == ADMIN_COLOR2)) { $source['couleur1'] = null; $source['couleur2'] = null; } | < < < < < < < < < < < < < < < < < < < < < < < < < < | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | if (!isset($source['couleur1'], $source['couleur2']) || ($source['couleur1'] == ADMIN_COLOR1 && $source['couleur2'] == ADMIN_COLOR2)) { $source['couleur1'] = null; $source['couleur2'] = null; } parent::importForm($source); } protected function _filterType(string $key, $value) { switch ($this->_types[$key]) { case 'int': |
︙ | ︙ | |||
246 247 248 249 250 251 252 | $this->assert(trim($this->nom_asso) != '', 'Le nom de l\'association ne peut rester vide.'); $this->assert(trim($this->monnaie) != '', 'La monnaie ne peut rester vide.'); $this->assert(trim($this->pays) != '' && Utils::getCountryName($this->pays), 'Le pays ne peut rester vide.'); $this->assert(null === $this->site_asso || filter_var($this->site_asso, FILTER_VALIDATE_URL), 'L\'adresse URL du site web est invalide.'); $this->assert(trim($this->email_asso) != '' && SMTP::checkEmailIsValid($this->email_asso, false), 'L\'adresse e-mail de l\'association est invalide.'); $this->assert($this->champs_membres instanceof Champs, 'Objet champs membres invalide'); | | > | > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | $this->assert(trim($this->nom_asso) != '', 'Le nom de l\'association ne peut rester vide.'); $this->assert(trim($this->monnaie) != '', 'La monnaie ne peut rester vide.'); $this->assert(trim($this->pays) != '' && Utils::getCountryName($this->pays), 'Le pays ne peut rester vide.'); $this->assert(null === $this->site_asso || filter_var($this->site_asso, FILTER_VALIDATE_URL), 'L\'adresse URL du site web est invalide.'); $this->assert(trim($this->email_asso) != '' && SMTP::checkEmailIsValid($this->email_asso, false), 'L\'adresse e-mail de l\'association est invalide.'); $this->assert($this->champs_membres instanceof Champs, 'Objet champs membres invalide'); // Files $this->assert(count($this->files) == count(self::FILES)); foreach ($this->files as $key => $value) { $this->assert(array_key_exists($key, self::FILES)); $this->assert(is_int($value) || is_null($value)); } $champs = $this->champs_membres; $this->assert(!empty($champs->get($this->champ_identite)), sprintf('Le champ spécifié pour identité, "%s" n\'existe pas', $this->champ_identite)); $this->assert(!empty($champs->get($this->champ_identifiant)), sprintf('Le champ spécifié pour identifiant, "%s" n\'existe pas', $this->champ_identifiant)); $db = DB::getInstance(); // Check that this field is actually unique if (isset($this->_modified['champ_identifiant'])) { $sql = sprintf('SELECT (COUNT(DISTINCT %s COLLATE NOCASE) = COUNT(*)) FROM membres WHERE %1$s IS NOT NULL AND %1$s != \'\';', $this->champ_identifiant); $is_unique = (bool) $db->firstColumn($sql); $this->assert($is_unique, sprintf('Le champ "%s" comporte des doublons et ne peut donc pas servir comme identifiant unique de connexion.', $this->champ_identifiant)); } $this->assert($db->test('users_categories', 'id = ?', $this->categorie_membres), 'Catégorie de membres inconnue'); } public function file(string $key): ?File { if (!isset(self::FILES[$key])) { throw new \InvalidArgumentException('Invalid file key: ' . $key); } if (empty($this->files[$key])) { return null; } return Files::get(self::FILES[$key]); } public function fileURL(string $key, string $params = ''): ?string { if (empty($this->files[$key])) { if ($key == 'favicon') { return ADMIN_URL . 'static/favicon.png'; } elseif ($key == 'icon') { return ADMIN_URL . 'static/icon.png'; } return null; } $params = $params ? $params . '&' : ''; return WWW_URL . self::FILES[$key] . '?' . $params . substr(md5($this->files[$key]), 0, 10); } public function hasFile(string $key): bool { return $this->files[$key] ? true : false; } public function setFile(string $key, ?string $value, bool $upload = false): ?File { $f = Files::get(self::FILES[$key]); $files = $this->files; $type = self::FILES_TYPES[$key]; $path = self::FILES[$key]; // NULL = delete file if (null === $value) { if ($f) { $f->delete(); } $f = null; } elseif ($upload) { $f = File::upload(Utils::dirname($path), $value, Utils::basename($path)); if ($type == 'image' && !$f->image) { $this->setFile($key, null); throw new UserException('Le fichier n\'est pas une image.'); } // Force favicon format if ($key == 'favicon') { $format = 'png'; $i = new Image($f->fullpath()); $i->cropResize(32, 32); $f->setContent($i->output($format, true)); } // Force icon format else if ($key == 'favicon') { $format = 'png'; $i = new Image($f->fullpath()); $i->cropResize(512, 512); $f->setContent($i->output($format, true)); } } elseif ($f) { $f->setContent($value); } else { $f = File::createAndStore(Utils::dirname($path), Utils::basename($path), null, $value); } $files[$key] = $f ? $f->modified->getTimestamp() : null; $this->set('files', $files); return $f; } } |
Modified src/include/lib/Garradin/Entities/Accounting/Transaction.php from [32bd594806] to [ab4675f214].
︙ | ︙ | |||
383 384 385 386 387 388 389 | } return $sum; } public function save(): bool { | | | 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | } return $sum; } public function save(): bool { if ($this->validated && !(isset($this->_modified['validated']) && $this->_modified['validated'] === 0)) { throw new ValidationException('Il n\'est pas possible de modifier une écriture qui a été validée'); } $exists = $this->exists(); $db = DB::getInstance(); |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Accounting/Year.php from [f33e68404f] to [478d431189].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php namespace Garradin\Entities\Accounting; use KD2\DB\EntityManager; use Garradin\DB; use Garradin\Entity; use Garradin\UserException; use Garradin\Accounting\Accounts; use Garradin\Files\Files; use Garradin\Entities\Files\File; class Year extends Entity { const TABLE = 'acc_years'; | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php namespace Garradin\Entities\Accounting; use KD2\DB\EntityManager; use Garradin\DB; use Garradin\Entity; use Garradin\UserException; use Garradin\Utils; use Garradin\Accounting\Accounts; use Garradin\Files\Files; use Garradin\Entities\Files\File; class Year extends Entity { const TABLE = 'acc_years'; |
︙ | ︙ | |||
160 161 162 163 164 165 166 | return EntityManager::findOneById(Chart::class, $this->id_chart); } public function accounts() { return new Accounts($this->id_chart); } | | > > > > > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | return EntityManager::findOneById(Chart::class, $this->id_chart); } public function accounts() { return new Accounts($this->id_chart); } public function label_years() { $start = Utils::date_fr($this->start_date, 'Y'); $end = Utils::date_fr($this->end_date, 'Y'); return $start == $end ? $start : sprintf('%s-%s', $start, substr($end, -2)); } } |
Modified src/include/lib/Garradin/Entities/Files/File.php from [0f34036f86] to [3b1c044956].
︙ | ︙ | |||
62 63 64 65 66 67 68 | const TYPE_DIRECTORY = 2; const TYPE_LINK = 3; /** * Tailles de miniatures autorisées, pour ne pas avoir 500 fichiers générés avec 500 tailles différentes * @var array */ | | > > > > > | | | 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 | const TYPE_DIRECTORY = 2; const TYPE_LINK = 3; /** * Tailles de miniatures autorisées, pour ne pas avoir 500 fichiers générés avec 500 tailles différentes * @var array */ const ALLOWED_THUMB_SIZES = [ '150px' => [['resize', 150]], '200px' => [['resize', 200]], '500px' => [['resize', 500]], 'crop-256px' => [['cropResize', 256, 256]], ]; const THUMB_CACHE_ID = 'file.thumb.%s.%d'; const THUMB_SIZE_TINY = '200px'; const THUMB_SIZE_SMALL = '500px'; const CONTEXT_DOCUMENTS = 'documents'; const CONTEXT_USER = 'user'; const CONTEXT_TRANSACTION = 'transaction'; const CONTEXT_CONFIG = 'config'; const CONTEXT_WEB = 'web'; const CONTEXT_SKELETON = 'skel'; |
︙ | ︙ | |||
170 171 172 173 174 175 176 | // Delete actual file content Files::callStorage('delete', $this); Plugin::fireSignal('files.delete', ['file' => $this]); // clean up thumbnails | | | | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | // Delete actual file content Files::callStorage('delete', $this); Plugin::fireSignal('files.delete', ['file' => $this]); // clean up thumbnails foreach (self::ALLOWED_THUMB_SIZES as $key => $operations) { Static_Cache::remove(sprintf(self::THUMB_CACHE_ID, $this->pathHash(), $key)); } DB::getInstance()->delete('files_search', 'path = ? OR path LIKE ?', $this->path, $this->path . '/%'); if ($this->exists()) { return parent::delete(); } |
︙ | ︙ | |||
225 226 227 228 229 230 231 232 233 234 235 236 237 238 | $return = Files::callStorage('move', $this, $new_path); Plugin::fireSignal('files.move', ['file' => $this, 'new_path' => $new_path]); return $return; } public function setContent(string $content): self { $this->set('modified', new \DateTime); $this->store(null, rtrim($content)); $this->indexForSearch(null, $content); return $this; | > > > > > > > > > > | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | $return = Files::callStorage('move', $this, $new_path); Plugin::fireSignal('files.move', ['file' => $this, 'new_path' => $new_path]); return $return; } /** * Copy the current file to a new location * @param string $target Target path * @return self */ public function copy(string $target): self { return self::createAndStore(Utils::dirname($target), Utils::basename($target), Files::callStorage('getFullPath', $this), null); } public function setContent(string $content): self { $this->set('modified', new \DateTime); $this->store(null, rtrim($content)); $this->indexForSearch(null, $content); return $this; |
︙ | ︙ | |||
313 314 315 316 317 318 319 320 321 322 323 324 325 326 | } Plugin::fireSignal('files.store', ['file' => $this]); if (!$index_search) { $this->indexForSearch($source_path, $source_content); } return $this; } public function indexForSearch(?string $source_path, ?string $source_content, ?string $title = null): void { // Store content in search table | > > > > > > | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | } Plugin::fireSignal('files.store', ['file' => $this]); if (!$index_search) { $this->indexForSearch($source_path, $source_content); } // clean up thumbnails foreach (self::ALLOWED_THUMB_SIZES as $key => $operations) { Static_Cache::remove(sprintf(self::THUMB_CACHE_ID, $this->pathHash(), $key)); } return $this; } public function indexForSearch(?string $source_path, ?string $source_content, ?string $title = null): void { // Store content in search table |
︙ | ︙ | |||
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | $file->set('path', $fullpath); $file->set('parent', $path); $file->set('name', $name); if ($source_path && !$source_content) { $file->set('mime', finfo_file($finfo, $source_path)); $file->set('size', filesize($source_path)); } else { $file->set('mime', finfo_buffer($finfo, $source_content)); $file->set('size', strlen($source_content)); } $file->set('image', (int) in_array($file->mime, self::IMAGE_TYPES)); // Force empty files as text/plain if ($file->mime == 'application/x-empty' && !$file->size) { $file->set('mime', 'text/plain'); } return $file; } | > < < < < < < < < < < < < < < < < < < < < < | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 | $file->set('path', $fullpath); $file->set('parent', $path); $file->set('name', $name); if ($source_path && !$source_content) { $file->set('mime', finfo_file($finfo, $source_path)); $file->set('size', filesize($source_path)); $file->set('modified', new \DateTime('@' . filemtime($source_path))); } else { $file->set('mime', finfo_buffer($finfo, $source_content)); $file->set('size', strlen($source_content)); } $file->set('image', (int) in_array($file->mime, self::IMAGE_TYPES)); // Force empty files as text/plain if ($file->mime == 'application/x-empty' && !$file->size) { $file->set('mime', 'text/plain'); } return $file; } /** * Upload multiple files * @param string $path Target parent directory (eg. 'documents/Logos') * @param string $key The name of the file input in the HTML form (this MUST have a '[]' at the end of the name) * @return array list of File objects created */ static public function uploadMultiple(string $path, string $key): array |
︙ | ︙ | |||
528 529 530 531 532 533 534 | /** * Upload a file using POST from a HTML form * @param string $path Target parent directory (eg. 'documents/Logos') * @param string $key The name of the file input in the HTML form * @return self Created file object */ | | | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 | /** * Upload a file using POST from a HTML form * @param string $path Target parent directory (eg. 'documents/Logos') * @param string $key The name of the file input in the HTML form * @return self Created file object */ static public function upload(string $path, string $key, ?string $name = null): self { if (!isset($_FILES[$key]) || !is_array($_FILES[$key])) { throw new UserException('Aucun fichier reçu'); } $file = $_FILES[$key]; if (!empty($file['error'])) { throw new UserException(self::getErrorMessage($file['error'])); } if (empty($file['size']) || empty($file['name'])) { throw new UserException('Fichier reçu invalide : vide ou sans nom de fichier.'); } if (!is_uploaded_file($file['tmp_name'])) { throw new \RuntimeException('Le fichier n\'a pas été envoyé de manière conventionnelle.'); } $name = self::filterName($name ?? $file['name']); return self::createAndStore($path, $name, $file['tmp_name'], null); } /** * Récupération du message d'erreur |
︙ | ︙ | |||
601 602 603 604 605 606 607 | if ($download) { $url .= '?download'; } return $url; } | | < < < | < < < < < < < < < < < | | < < < | < < < < | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 | if ($download) { $url .= '?download'; } return $url; } public function thumb_url($size = null): string { if (is_int($size)) { $size .= 'px'; } $size = isset(self::ALLOWED_THUMB_SIZES[$size]) ? $size : key(self::ALLOWED_THUMB_SIZES); return sprintf('%s?%dpx', $this->url(), $size); } /** * Envoie le fichier au client HTTP */ public function serve(?Session $session = null, bool $download = false): void { |
︙ | ︙ | |||
658 659 660 661 662 663 664 | $this->_serve($path, $content, $download); } /** * Envoie une miniature à la taille indiquée au client HTTP */ | | < < < < | | < | < | | > > > > > > > > > > > > > > > > | 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | $this->_serve($path, $content, $download); } /** * Envoie une miniature à la taille indiquée au client HTTP */ public function serveThumbnail(?Session $session = null, string $size = null): void { if (!$this->checkReadAccess($session)) { header('HTTP/1.1 403 Forbidden', true, 403); throw new UserException('Accès interdit'); return; } if (!$this->image) { throw new UserException('Il n\'est pas possible de fournir une miniature pour un fichier qui n\'est pas une image.'); } if (!array_key_exists($size, self::ALLOWED_THUMB_SIZES)) { throw new UserException('Cette taille de miniature n\'est pas autorisée.'); } $cache_id = sprintf(self::THUMB_CACHE_ID, $this->pathHash(), $size); $destination = Static_Cache::getPath($cache_id); if (!Static_Cache::exists($cache_id)) { try { if ($path = Files::callStorage('getFullPath', $this)) { $i = new Image($path); } elseif ($content = Files::callStorage('fetch', $this)) { $i = Image::createFromBlob($content); } else { throw new \RuntimeException('Unable to fetch file'); } $operations = self::ALLOWED_THUMB_SIZES[$size]; $allowed_operations = ['resize', 'cropResize', 'flip', 'rotate', 'autoRotate', 'crop']; foreach ($operations as $operation) { $arguments = array_slice($operation, 1); $operation = $operation[0]; if (!in_array($operation, $allowed_operations)) { throw new \InvalidArgumentException('Opération invalide: ' . $operation); } call_user_func_array([$i, $operation], $arguments); } $i->save($destination); } catch (\RuntimeException $e) { throw new UserException('Impossible de créer la miniature'); } } $this->_serve($destination, null); |
︙ | ︙ |
Modified src/include/lib/Garradin/Entities/Web/Page.php from [6b459772f9] to [d8d50d5663].
︙ | ︙ | |||
216 217 218 219 220 221 222 | parent::save(); $this->syncFile($current_path); // Rename/move children if ($change_parent) { $db = DB::getInstance(); $sql = sprintf('UPDATE web_pages | > > | > | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | parent::save(); $this->syncFile($current_path); // Rename/move children if ($change_parent) { $db = DB::getInstance(); $sql = sprintf('UPDATE web_pages SET path = %1$s || substr(path, %2$d), parent = %1$s || substr(parent, %2$d), file_path = \'web/\' || %1$s || substr(file_path, %2$d + 4) WHERE path LIKE %3$s;', $db->quote($this->path), strlen($change_parent) + 1, $db->quote($change_parent . '/%')); $db->exec($sql); } return true; } |
︙ | ︙ |
Modified src/include/lib/Garradin/Install.php from [4757cdcf38] to [a878545951].
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 | $id_membre = $membres->add([ 'id_category' => $cat->id(), 'nom' => $user_name, 'email' => $user_email, 'passe' => $user_password, 'pays' => 'FR', ]); $welcome_text = $welcome_text ?? sprintf("Bienvenue dans l'administration de %s !\n\nUtilisez le menu à gauche pour accéder aux différentes sections.\n\nCe message peut être modifié dans la 'Configuration'.", $name); | > > < < < | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | $id_membre = $membres->add([ 'id_category' => $cat->id(), 'nom' => $user_name, 'email' => $user_email, 'passe' => $user_password, 'pays' => 'FR', ]); $config->set('files', array_map(fn () => null, $config::FILES)); $welcome_text = $welcome_text ?? sprintf("Bienvenue dans l'administration de %s !\n\nUtilisez le menu à gauche pour accéder aux différentes sections.\n\nCe message peut être modifié dans la 'Configuration'.", $name); $config->setFile('admin_homepage', $welcome_text); // Import accounting chart $chart = new Chart; $chart->label = 'Plan comptable associatif 2020 (Règlement ANC n°2018-06)'; $chart->country = 'FR'; $chart->code = 'PCA2018'; $chart->save(); |
︙ | ︙ |
Modified src/include/lib/Garradin/Services/Fees.php from [98acb50ff8] to [f232ea6af4].
︙ | ︙ | |||
41 42 43 44 45 46 47 | * If $user_id is specified, then it will return a column 'user_amount' containing the amount that this specific user should pay */ static public function listAllByService(?int $user_id = null) { $db = DB::getInstance(); $sql = 'SELECT *, CASE WHEN amount THEN amount ELSE NULL END AS user_amount | | < < < < < < < < | 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 | * If $user_id is specified, then it will return a column 'user_amount' containing the amount that this specific user should pay */ static public function listAllByService(?int $user_id = null) { $db = DB::getInstance(); $sql = 'SELECT *, CASE WHEN amount THEN amount ELSE NULL END AS user_amount FROM services_fees ORDER BY id_service, amount IS NULL, label COLLATE NOCASE;'; $result = $db->get($sql); if (!$user_id) { return $result; } foreach ($result as &$row) { if ($row->formula) { $sql = sprintf('SELECT %s FROM membres WHERE id = %d;', $row->formula, $user_id); $row->user_amount = $db->firstColumn($sql); } } return $result; } public function listWithStats() { $db = DB::getInstance(); $hidden_cats = array_keys(Categories::listHidden()); |
︙ | ︙ |
Modified src/include/lib/Garradin/Static_Cache.php from [6c89dae4c6] to [c7f77d4740].
︙ | ︙ | |||
99 100 101 102 103 104 105 | while ($file = $d->read()) { if ($file[0] == '.') { continue; } | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | while ($file = $d->read()) { if ($file[0] == '.') { continue; } if (filemtime($dir . '/' . $file) < $expire) { Utils::safe_unlink($dir . '/' . $file); } } $d->close(); return true; } } |
Modified src/include/lib/Garradin/Template.php from [c72c39239e] to [074688e258].
︙ | ︙ | |||
64 65 66 67 68 69 70 71 72 73 74 75 76 77 | // pour les éléments statiques (genre /admin/static/admin.css?v0.9.0) // car cela dévoilerait la version de Garradin utilisée, posant un souci // en cas de faille, on cache donc la version utilisée, chaque instance // aura sa propre version $this->assign('version_hash', substr(sha1(garradin_version() . garradin_manifest() . ROOT . SECRET_KEY), 0, 10)); $this->assign('www_url', WWW_URL); $this->assign('self_url', Utils::getSelfURI()); $this->assign('self_url_no_qs', Utils::getSelfURI(false)); $this->assign('is_logged', false); $this->assign('dialog', isset($_GET['_dialog'])); $this->assign('password_pattern', sprintf('.{%d,}', Session::MINIMUM_PASSWORD_LENGTH)); | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | // pour les éléments statiques (genre /admin/static/admin.css?v0.9.0) // car cela dévoilerait la version de Garradin utilisée, posant un souci // en cas de faille, on cache donc la version utilisée, chaque instance // aura sa propre version $this->assign('version_hash', substr(sha1(garradin_version() . garradin_manifest() . ROOT . SECRET_KEY), 0, 10)); $this->assign('www_url', WWW_URL); $this->assign('help_url', HELP_URL); $this->assign('self_url', Utils::getSelfURI()); $this->assign('self_url_no_qs', Utils::getSelfURI(false)); $this->assign('is_logged', false); $this->assign('dialog', isset($_GET['_dialog'])); $this->assign('password_pattern', sprintf('.{%d,}', Session::MINIMUM_PASSWORD_LENGTH)); |
︙ | ︙ | |||
573 574 575 576 577 578 579 | { $config = Config::getInstance(); $couleur1 = $config->get('couleur1') ?: ADMIN_COLOR1; $couleur2 = $config->get('couleur2') ?: ADMIN_COLOR2; $admin_background = ADMIN_BACKGROUND_IMAGE; | | | | | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | { $config = Config::getInstance(); $couleur1 = $config->get('couleur1') ?: ADMIN_COLOR1; $couleur2 = $config->get('couleur2') ?: ADMIN_COLOR2; $admin_background = ADMIN_BACKGROUND_IMAGE; if ($url = $config->fileURL('admin_background')) { $admin_background = $url; } // Transformation Hexa vers décimal $couleur1 = implode(', ', sscanf($couleur1, '#%02x%02x%02x')); $couleur2 = implode(', ', sscanf($couleur2, '#%02x%02x%02x')); $out = ' <style type="text/css"> :root { --gMainColor: %s; --gSecondColor: %s; --gBgImage: url("%s"); } </style>'; if ($url = $config->fileURL('admin_css')) { $out .= "\n" . sprintf('<link rel="stylesheet" type="text/css" href="%s" />', $url); } return sprintf($out, $couleur1, $couleur2, $admin_background); } protected function displayChampMembre($v, $config = null) { |
︙ | ︙ |
Modified src/include/lib/Garradin/Upgrade.php from [eb62265c41] to [1ae9756519].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php namespace Garradin; use Garradin\Membres\Session; use Garradin\Membres\Champs; use Garradin\Files\Files; use KD2\HTTP; use KD2\FossilInstaller; class Upgrade { | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php namespace Garradin; use Garradin\Membres\Session; use Garradin\Membres\Champs; use Garradin\Files\Files; use Garradin\Entities\Files\File; use KD2\HTTP; use KD2\FossilInstaller; class Upgrade { |
︙ | ︙ | |||
323 324 325 326 327 328 329 | if (version_compare($v, '1.1.8', '==')) { // Force sync to add missing pages if you had the buggy 1.1.8 version \Garradin\Web\Web::sync(true); } if (version_compare($v, '1.1.10', '<')) { | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | if (version_compare($v, '1.1.8', '==')) { // Force sync to add missing pages if you had the buggy 1.1.8 version \Garradin\Web\Web::sync(true); } if (version_compare($v, '1.1.10', '<')) { \Garradin\Web\Web::sync(); // Force sync of web pages Files::syncVirtualTable('', true); $db->begin(); $db->exec(sprintf('DELETE FROM files_search WHERE path NOT IN (SELECT path FROM %s);', Files::getVirtualTableName())); $db->commit(); } if (version_compare($v, '1.1.15', '<')) { $db->begin(); $db->import(ROOT . '/include/data/1.1.15_migration.sql'); $db->commit(); } if (version_compare($v, '1.1.16', '<')) { $files = Config::FILES; foreach ($files as $key => &$set) { $f = Files::get($set); $set = $f !== null ? $f->modified->getTimestamp() : null; } unset($set); // Migrate files if ($f = Files::get(File::CONTEXT_SKELETON . '/favicon.png')) { $f->copy(Config::FILES['favicon']); $files['favicon'] = $f->modified->getTimestamp(); } if ($f = Files::get(File::CONTEXT_SKELETON . '/logo.png')) { $f->copy(Config::FILES['icon']); $files['icon'] = $f->modified->getTimestamp(); } $db->begin(); $db->exec('DELETE FROM config WHERE key IN (\'admin_background\', \'admin_css\', \'admin_homepage\');'); $db->exec(sprintf('INSERT INTO config (key, value) VALUES (\'files\', %s);', $db->quote(json_encode($files)))); $db->commit(); } // Vérification de la cohérence des clés étrangères $db->foreignKeyCheck(); // Delete local cached files Utils::resetCache(USER_TEMPLATES_CACHE_ROOT); Utils::resetCache(STATIC_CACHE_ROOT); |
︙ | ︙ |
Modified src/include/lib/Garradin/UserTemplate/CommonModifiers.php from [5a74eceef6] to [86761f9ede].
︙ | ︙ | |||
23 24 25 26 27 28 29 | 'typo', ]; const FUNCTIONS_LIST = [ 'pagination', ]; | | > > | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 'typo', ]; const FUNCTIONS_LIST = [ 'pagination', ]; static public function money($number, bool $hide_empty = true, bool $force_sign = false): string { if ($hide_empty && !$number) { return ''; } $sign = ($force_sign && $number > 0) ? '+' : ''; return sprintf('<b class="money">%s</b>', $sign . Utils::money_format($number, ',', ' ', $hide_empty)); } static public function money_currency($number, bool $hide_empty = true): string { $out = self::money($number, $hide_empty); if ($out !== '') { |
︙ | ︙ |
Modified src/include/lib/Garradin/UserTemplate/Modifiers.php from [98d18e40cb] to [418f637151].
︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 'truncate', 'excerpt', 'protect_contact', 'atom_date', 'xml_escape', 'replace', 'regexp_replace', ]; static public function replace($str, $find, $replace): string { return str_replace($find, $replace, $str); } static public function regexp_replace($str, $pattern, $replace) | > > > > | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 'truncate', 'excerpt', 'protect_contact', 'atom_date', 'xml_escape', 'replace', 'regexp_replace', 'remove_leading_number', 'get_leading_number', ]; const LEADING_NUMBER_REGEXP = '/^([\d.]+)\s*[.\)]\s*/'; static public function replace($str, $find, $replace): string { return str_replace($find, $replace, $str); } static public function regexp_replace($str, $pattern, $replace) |
︙ | ︙ | |||
119 120 121 122 123 124 125 | return Utils::date_fr($date, DATE_ATOM); } static public function xml_escape($str) { return htmlspecialchars($str, ENT_XML1 | ENT_QUOTES); } | | > > > > > > > > > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | return Utils::date_fr($date, DATE_ATOM); } static public function xml_escape($str) { return htmlspecialchars($str, ENT_XML1 | ENT_QUOTES); } static public function remove_leading_number($str): string { return preg_replace(self::LEADING_NUMBER_REGEXP, '', trim($str)); } static public function get_leading_number($str): ?string { $match = preg_match(self::LEADING_NUMBER_REGEXP, $str); return $match[1] ?? null; } } |
Modified src/include/lib/Garradin/UserTemplate/UserTemplate.php from [008bd1b9aa] to [9c5fedaedd].
︙ | ︙ | |||
28 29 30 31 32 33 34 | static public function getRootVariables() { if (null !== self::$root_variables) { return self::$root_variables; } | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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']; if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { if (function_exists('locale_accept_from_http')) { $lang = locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE']); } |
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | } else { $lang = ''; } $config = Config::getInstance(); $config = array_intersect_key($config->asArray(), array_flip($keys)); self::$root_variables = [ 'root_url' => WWW_URL, 'request_url' => Utils::getRequestURI(), 'admin_url' => ADMIN_URL, '_GET' => &$_GET, '_POST' => &$_POST, | > > > > > > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | } else { $lang = ''; } $config = Config::getInstance(); $files = $config::FILES; // Put URL in files array array_walk($files, function (&$v, $k) use ($config) { $v = $config->fileURL($k); }); $config = array_intersect_key($config->asArray(), array_flip($keys)); $config['files'] = $files; self::$root_variables = [ 'root_url' => WWW_URL, 'request_url' => Utils::getRequestURI(), 'admin_url' => ADMIN_URL, '_GET' => &$_GET, '_POST' => &$_POST, |
︙ | ︙ |
Modified src/include/lib/Garradin/Utils.php from [920f71c5e9] to [04aa6a6cb3].
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 | return sprintf('%s%s%s%s', $sign, number_format($number, 0, $dec_point, $thousands_sep), $dec_point, $decimals); } static public function getLocalURL(string $url = '', ?string $default_prefix = null): string { if ($url[0] == '!') { return ADMIN_URL . substr($url, 1); } elseif ($url[0] == '/' && ($pos = strpos($url, WWW_URI)) === 0) { return WWW_URL . substr($url, strlen(WWW_URI)); } elseif (substr($url, 0, 5) == 'http:' || substr($url, 0, 6) == 'https:') { return $url; } | > > > | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | return sprintf('%s%s%s%s', $sign, number_format($number, 0, $dec_point, $thousands_sep), $dec_point, $decimals); } static public function getLocalURL(string $url = '', ?string $default_prefix = null): string { if ($url[0] == '!') { return ADMIN_URL . substr($url, 1); } elseif (substr($url, 0, 7) == '/admin/') { return ADMIN_URL . substr($url, 7); } elseif ($url[0] == '/' && ($pos = strpos($url, WWW_URI)) === 0) { return WWW_URL . substr($url, strlen(WWW_URI)); } elseif (substr($url, 0, 5) == 'http:' || substr($url, 0, 6) == 'https:') { return $url; } |
︙ | ︙ | |||
832 833 834 835 836 837 838 | $g /= 255; $b /= 255; $max = max($r, $g, $b); $min = min($r, $g, $b); $h = $s = $v = $max; $d = $max - $min; | | > > | | 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 | $g /= 255; $b /= 255; $max = max($r, $g, $b); $min = min($r, $g, $b); $h = $s = $v = $max; $d = $max - $min; //$s = ($max == 0) ? 0 : $d / $max; $l = ($max + $min) / 2; $s = $l > 0.5 ? $d / ((2 - $max - $min) ?: 1) : $d / (($max + $min) ?: 1); if($max == $min) { $h = 0; // achromatic } else { switch($max) { case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break; case $g: $h = ($b - $r) / $d + 2; break; case $b: $h = ($r - $g) / $d + 4; break; } $h /= 6; } return array($h * 360, $s, $l); } static public function HTTPCache(?string $hash, int $last_change): bool { $etag = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : null; $last_modified = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : null; |
︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 | $cmd = PDF_COMMAND; if (!$cmd) { // Try to see if there's a plugin $in = ['source' => $source, 'target' => $target]; if (Plugin::fireSignal('pdf.create', $in)) { return $target; } unset($in); // Try to find a local executable $list = ['prince', 'chromium', 'wkhtmltopdf', 'weasyprint']; | > | 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 | $cmd = PDF_COMMAND; if (!$cmd) { // Try to see if there's a plugin $in = ['source' => $source, 'target' => $target]; if (Plugin::fireSignal('pdf.create', $in)) { Utils::safe_unlink($source); return $target; } unset($in); // Try to find a local executable $list = ['prince', 'chromium', 'wkhtmltopdf', 'weasyprint']; |
︙ | ︙ | |||
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 | $cmd = 'weasyprint %1$s %2$s'; break; default: break; } exec(sprintf($cmd, escapeshellarg($source), escapeshellarg($target))); if (!file_exists($target)) { throw new \RuntimeException('PDF command failed'); } | > < < | 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | $cmd = 'weasyprint %1$s %2$s'; break; default: break; } exec(sprintf($cmd, escapeshellarg($source), escapeshellarg($target))); Utils::safe_unlink($source); if (!file_exists($target)) { throw new \RuntimeException('PDF command failed'); } return $target; } /** * Integer to A-Z, AA-ZZ, AAA-ZZZ, etc. * @see https://www.php.net/manual/fr/function.base-convert.php#94874 */ |
︙ | ︙ |
Modified src/include/lib/Garradin/Web/Render/Skriv.php from [ce5e528272] to [02e93f8ff3].
︙ | ︙ | |||
104 105 106 107 108 109 110 | if (!$name || null === $this->current_path) { return $skriv->parseError('/!\ Tag image : aucun nom de fichier indiqué.'); } $url = $this->resolveAttachment($name); | | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | if (!$name || null === $this->current_path) { return $skriv->parseError('/!\ Tag image : aucun nom de fichier indiqué.'); } $url = $this->resolveAttachment($name); $thumb_url = sprintf('%s?%s', $url, $align == 'center' ? '500px' : '200px'); $out = sprintf('<a href="%s" class="internal-image" target="_image"><img src="%s" alt="%s" loading="lazy" /></a>', htmlspecialchars($url), htmlspecialchars($thumb_url), htmlspecialchars($caption ?? $name) ); |
︙ | ︙ |
Modified src/include/lib/Garradin/Web/Web.php from [2a719367c6] to [a0e6ad5801].
︙ | ︙ | |||
13 14 15 16 17 18 19 | use Garradin\Utils; use Garradin\UserException; use Garradin\ValidationException; use Garradin\Membres\Session; use KD2\DB\EntityManager as EM; | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | use Garradin\Utils; use Garradin\UserException; use Garradin\ValidationException; use Garradin\Membres\Session; use KD2\DB\EntityManager as EM; use const Garradin\{WWW_URI, ADMIN_URL, FILE_STORAGE_BACKEND, ROOT}; class Web { static public function search(string $search): array { $results = Files::search($search, File::CONTEXT_WEB . '%'); |
︙ | ︙ | |||
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | // Redirect old URLs (pre-1.1) if ($uri == 'feed/atom/') { Utils::redirect('/atom.xml'); } elseif (substr($uri, 0, 4) == 'api/') { API::dispatchURI(substr($uri, 4)); exit; } elseif (substr($uri, 0, 6) === 'admin/') { http_response_code(404); throw new UserException('Cette page n\'existe pas.'); } elseif (($file = Files::getFromURI($uri)) || ($file = self::getAttachmentFromURI($uri))) { $size = null; | > > > > > | | | | > | 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 213 | // Redirect old URLs (pre-1.1) if ($uri == 'feed/atom/') { Utils::redirect('/atom.xml'); } elseif (substr($uri, 0, 4) == 'api/') { API::dispatchURI(substr($uri, 4)); exit; } elseif ($uri == 'favicon.ico') { header('Location: ' . Config::getInstance()->fileURL('favicon'), true); return; } elseif (substr($uri, 0, 6) === 'admin/') { http_response_code(404); throw new UserException('Cette page n\'existe pas.'); } elseif (($file = Files::getFromURI($uri)) || ($file = self::getAttachmentFromURI($uri))) { $size = null; if ($file->image) { foreach ($_GET as $key => $v) { if (array_key_exists($key, File::ALLOWED_THUMB_SIZES)) { $size = $key; break; } } } $session = Session::getInstance(); if (Plugin::fireSignal('http.request.file.before', compact('file', 'uri', 'session'))) { // If a plugin handled the request, let's stop here |
︙ | ︙ |
Modified src/include/lib/dependencies.list from [b3a90c3add] to [e9b8ffa0d9].
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | KD2/Form.php KD2/FossilInstaller.php KD2/HTTP.php KD2/Graphics/Image.php KD2/Graphics/QRCode.php KD2/Graphics/SVG/Pie.php KD2/Graphics/SVG/Plot.php KD2/Office/Calc/Writer.php KD2/Security.php KD2/Security_OTP.php KD2/SimpleDiff.php KD2/SkrivLite.php KD2/Smartyer.php KD2/SMTP.php KD2/Translate.php KD2/UserSession.php KD2/ZipWriter.php Parsedown.php | > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | KD2/Form.php KD2/FossilInstaller.php KD2/HTTP.php KD2/Graphics/Image.php KD2/Graphics/QRCode.php KD2/Graphics/SVG/Pie.php KD2/Graphics/SVG/Plot.php KD2/Graphics/SVG/Bar.php KD2/Office/Calc/Writer.php KD2/Security.php KD2/Security_OTP.php KD2/SimpleDiff.php KD2/SkrivLite.php KD2/Smartyer.php KD2/SMTP.php KD2/Translate.php KD2/UserSession.php KD2/ZipWriter.php Parsedown.php |
Modified src/templates/acc/accounts/journal.tpl from [0463c7f428] to [92b1619c26].
︙ | ︙ | |||
23 24 25 26 27 28 29 | <p class="alert block">Ce tiers vous doit <strong>{$sum|abs|raw|money_currency}</strong>.</p> {else} <p class="confirm block">Vous ne devez pas d'argent à ce tiers, et il ne vous en doit pas non plus.</p> {/if} {elseif $account.type == $account::TYPE_BANK} {if $sum < 0} <p class="error block">Ce compte est à découvert de <strong>{$sum|abs|raw|money_currency}</strong> à la banque.</p> | | | | | 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 | <p class="alert block">Ce tiers vous doit <strong>{$sum|abs|raw|money_currency}</strong>.</p> {else} <p class="confirm block">Vous ne devez pas d'argent à ce tiers, et il ne vous en doit pas non plus.</p> {/if} {elseif $account.type == $account::TYPE_BANK} {if $sum < 0} <p class="error block">Ce compte est à découvert de <strong>{$sum|abs|raw|money_currency}</strong> à la banque.</p> {elseif $sum > 0} <p class="confirm block">Ce compte est créditeur de <strong>{$sum|abs|raw|money_currency}</strong> à la banque.</p> {/if} {elseif $account.type == $account::TYPE_CASH} {if $sum < 0} <p class="error block">Cette caisse est débiteur de <strong>{$sum|abs|raw|money_currency}</strong>. Est-ce normal ? Une vérification est peut-être nécessaire ?</p> {elseif $sum > 0} <p class="confirm block">Cette caisse est créditrice de <strong>{$sum|abs|raw|money_currency}</strong>.</p> {/if} {elseif $account.type == $account::TYPE_OUTSTANDING} {if $sum < 0} <p class="error block">Ce compte est débiteur <strong>{$sum|abs|raw|money_currency}</strong>. Est-ce normal ? Une vérification est peut-être nécessaire ?</p> {elseif $sum > 0} <p class="confirm block">Ce compte d'attente est créditeur de <strong>{$sum|abs|raw|money_currency}</strong>. {if $sum > 200}Un dépôt à la banque serait peut-être une bonne idée ?{/if}</p> {/if} {elseif $account.type == $account::TYPE_REVENUE && $sum < 0} <p class="alert block">Ce compte présente un solde négatif de <strong>{$sum|raw|money_currency}</strong>. Est-ce normal ? Cette situation ne devrait se produire que si vous avez dû procéder à des remboursements par exemple, et que ceux-ci couvrent des recettes perçues sur un exercice précédent.</p> {elseif $account.type == $account::TYPE_EXPENSE && $sum < 0} <p class="alert block">Ce compte présente un solde négatif de <strong>{$sum|raw|money_currency}</strong>. Est-ce normal ? Cette situation ne devrait se produire que si vous avez reçu des remboursements par exemple, et que ceux-ci couvrent des dépenses réglées sur un exercice précédent.</p> {/if} |
︙ | ︙ |
Modified src/templates/acc/reports/_header.tpl from [b23bfd50d9] to [2032e3497e].
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 | {if isset($analytical)} <h3>Projet : {$analytical.label}</h3> {/if} {if isset($year)} <p>Exercice : {$year.label} ({if $year.closed}clôturé{else}en cours{/if}, du {$year.start_date|date_short} au {$year.end_date|date_short}, généré le {$close_date|date_short})</p> {/if} <p class="noprint print-btn"> <button onclick="window.print(); return false;" class="icn-btn" data-icon="⎙">Imprimer</button> {linkbutton shape="download" href="%s&_pdf"|args:$self_url label="Télécharger en PDF"} </p> </div> | > > > > > > > > > > > > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | {if isset($analytical)} <h3>Projet : {$analytical.label}</h3> {/if} {if isset($year)} <p>Exercice : {$year.label} ({if $year.closed}clôturé{else}en cours{/if}, du {$year.start_date|date_short} au {$year.end_date|date_short}, généré le {$close_date|date_short})</p> {/if} {if !empty($allow_compare) && !empty($other_years)} <form method="get" action="" class="noprint"> <fieldset> <legend>Comparer avec un autre exercice</legend> <p> {input type="select" name="compare_year" options=$other_years default=$criterias.compare_year} {button type="submit" label="Comparer" shape="right"} </p> <input type="hidden" name="year" value="{$year.id}" /> </fieldset> </form> {/if} <p class="noprint print-btn"> <button onclick="window.print(); return false;" class="icn-btn" data-icon="⎙">Imprimer</button> {linkbutton shape="download" href="%s&_pdf"|args:$self_url label="Télécharger en PDF"} </p> </div> |
Modified src/templates/acc/reports/_statement.tpl from [674c33b3c6] to [9f0303b5cc].
︙ | ︙ | |||
16 17 18 19 20 21 22 | <tfoot> <tr> <td> <table> <tfoot> <tr> <th>Total</th> | | > > > > | > > > > | > > > > | > > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | <tfoot> <tr> <td> <table> <tfoot> <tr> <th>Total</th> <td class="money" width="10%">{$statement.expense_sum|raw|money:false}</td> {if $statement.expense_sum2} <td class="money" width="10%">{$statement.expense_sum2|raw|money:false}</td> <td class="money" width="10%">{$statement.expense_change|raw|money:true:true}</td> {/if} </tr> </tfoot> </table> </td> <td> <table> <tfoot> <tr> <th>Total</th> <td class="money" width="10%">{$statement.revenue_sum|raw|money:false}</td> {if $statement.revenue_sum2} <td class="money" width="10%">{$statement.revenue_sum2|raw|money:false}</td> <td class="money" width="10%">{$statement.revenue_change|raw|money:true:true}</td> {/if} </tr> </tfoot> </table> </td> </tr> {if $statement.result} <tr> <td> {if ($statement.result < 0)} <table> <tfoot> <tr> <th>Résultat (perte)</th> <td class="money" width="10%">{$statement.result|raw|money:false}</td> {if $statement.result2} <td class="money" width="10%">{$statement.result2|raw|money:false}</td> <td class="money" width="10%">{$statement.result_change|raw|money:true:true}</td> {/if} </tr> </tfoot> </table> {/if} </td> <td> {if ($statement.result >= 0)} <table> <tfoot> <tr> <th>Résultat (excédent)</th> <td class="money" width="10%">{$statement.result|raw|money:false}</td> {if $statement.result2} <td class="money" width="10%">{$statement.result2|raw|money:false}</td> <td class="money" width="10%">{$statement.result_change|raw|money:true:true}</td> {/if} </tr> </tfoot> </table> {/if} </td> </tr> {/if} </tfoot> </table> |
Modified src/templates/acc/reports/_statement_table.tpl from [cb39c0e999] to [29eca66656].
1 2 3 4 5 6 | <table class="list"> {if !empty($caption)} <caption><h3>{$caption}</h3></caption> {/if} <tbody> {foreach from=$accounts item="account"} | > > > > > > > > > > > | | > > > > | 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 | <table class="list"> {if !empty($caption)} <caption><h3>{$caption}</h3></caption> {/if} {if !empty($year2)} <thead> <tr> <td></td> <th></th> <td class="money" width="10%">{$year->label_years()}</td> <td class="money" width="10%">{$year2->label_years()}</td> <td class="money" width="10%">Écart</td> </tr> </thead> {/if} <tbody> {foreach from=$accounts item="account"} <tr class="compte{if isset($year2) && !$account.sum} disabled{/if}"> <td class="num"> {if !empty($year)}<a href="{$admin_url}acc/accounts/journal.php?id={$account.id}&year={$year.id}">{$account.code}</a> {else}{$account.code} {/if} </td> <th>{$account.label}</th> <td class="money">{$account.sum|raw|money:false}</td> {if isset($year2)} <td class="money">{$account.sum2|raw|money:false}</td> <td class="money">{$account.change|raw|money:true:true}</td> {/if} </tr> {/foreach} </tbody> </table> |
Modified src/templates/acc/reports/balance_sheet.tpl from [dc88e4cf11] to [b458502c4d].
1 2 | {include file="admin/_head.tpl" title="Bilan" current="acc/years"} | | | | | | > > > > | > > > > | 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 | {include file="admin/_head.tpl" title="Bilan" current="acc/years"} {include file="acc/reports/_header.tpl" current="balance_sheet" title="Bilan" allow_compare=true} {if $balance.sums.asset != $balance.sums.liability} <p class="alert block"> <strong>Le bilan n'est pas équilibré !</strong><br /> Vérifiez que vous n'avez pas oublié de reporter des soldes depuis le précédent exercice. </p> {/if} <table class="statement"> <colgroup> <col width="50%" /> <col width="50%" /> </colgroup> <tbody> <tr> <td> {include file="acc/reports/_statement_table.tpl" accounts=$balance.accounts.asset caption="Actif"} </td> <td> {include file="acc/reports/_statement_table.tpl" accounts=$balance.accounts.liability caption="Passif"} </td> </tr> </tbody> <tfoot> <tr> <td> <table> <tfoot> <tr> <th>Total actif</th> <td class="money" width="10%">{$balance.sums.asset|raw|money:false}</td> {if isset($year2)} <td class="money" width="10%">{$balance.sums2.asset|raw|money:false}</td> <td class="money" width="10%">{$balance.change.asset|raw|money:true:true}</td> {/if} </tr> </tfoot> </table> </td> <td> <table> <tfoot> <tr> <th>Total passif</th> <td class="money" width="10%">{$balance.sums.liability|raw|money:false}</td> {if isset($year2)} <td class="money" width="10%">{$balance.sums2.liability|raw|money:false}</td> <td class="money" width="10%">{$balance.change.liability|raw|money:true:true}</td> {/if} </tr> </tfoot> </table> </td> </tr> </tfoot> </table> <p class="help">Toutes les écritures sont libellées en {$config.monnaie}.</p> {include file="admin/_foot.tpl"} |
Modified src/templates/acc/reports/statement.tpl from [176cb70b6d] to [b37ad29a93].
1 2 | {include file="admin/_head.tpl" title="Compte de résultat" current="acc/years"} | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | {include file="admin/_head.tpl" title="Compte de résultat" current="acc/years"} {include file="acc/reports/_header.tpl" current="statement" title="Compte de résultat" allow_compare=true} {include file="acc/reports/_statement.tpl" statement=$general caption1="Charges" caption2="Produits"} {if !empty($volunteering.expense_sum) || !empty($volunteering.revenue_sum)} <h2 class="ruler">Contributions en nature</h2> {include file="acc/reports/_statement.tpl" statement=$volunteering header=false caption1="Emplois des contributions volontaires en nature" caption2="Contributions volontaires en nature"} {/if} <p class="help">Toutes les écritures sont libellées en {$config.monnaie}.</p> {include file="admin/_foot.tpl"} |
Modified src/templates/acc/years/index.tpl from [fbfa64b72c] to [3c1158f29c].
︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | {if !empty($list)} {if count($list) > 1} <section class="year-infos"> <section class="graphs"> <figure> <img src="{$admin_url}acc/reports/graph_plot_all.php?type=assets" alt="" /> </figure> <figure> <img src="{$admin_url}acc/reports/graph_plot_all.php?type=result" alt="" /> </figure> </section> </section> {/if} <table class="list"> {foreach from=$list item="year"} | > > | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | {if !empty($list)} {if count($list) > 1} <section class="year-infos"> <section class="graphs"> <figure> <img src="{$admin_url}acc/reports/graph_plot_all.php?type=assets" alt="" /> <figcaption>Soldes des banques et caisses par exercice</figcaption> </figure> <figure> <img src="{$admin_url}acc/reports/graph_plot_all.php?type=result" alt="" /> <figcaption>Recettes et dépenses par exercice</figcaption> </figure> </section> </section> {/if} <table class="list"> {foreach from=$list item="year"} |
︙ | ︙ |
Modified src/templates/admin/_head.tpl from [384cd3607b] to [0c365a5631].
1 | <!DOCTYPE html> | | > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr"{if array_key_exists('_dialog', $_GET)} class="dialog"{/if} data-version="{$version_hash}"> <head> <meta charset="utf-8" /> <meta name="v" content="{$version_hash}" /> <title>{$title}</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="{$admin_url}static/admin.css?{$version_hash}" media="all" /> <script type="text/javascript" src="{$admin_url}static/scripts/global.js?{$version_hash}"></script> {if isset($custom_js)} {foreach from=$custom_js item="js"} <script type="text/javascript" src="{$admin_url}static/scripts/{$js}?{$version_hash}"></script> {/foreach} |
︙ | ︙ | |||
25 26 27 28 29 30 31 | {if isset($plugin_js)} {foreach from=$plugin_js item="js"} <script type="text/javascript" src="{plugin_url file=$js}?{$version_hash}"></script> {/foreach} {/if} <link rel="stylesheet" type="text/css" href="{$admin_url}static/print.css?{$version_hash}" media="print" /> <link rel="stylesheet" type="text/css" href="{$admin_url}static/handheld.css?{$version_hash}" media="handheld,screen and (max-width:981px)" /> | < | < > > > > > > > > | 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 | {if isset($plugin_js)} {foreach from=$plugin_js item="js"} <script type="text/javascript" src="{plugin_url file=$js}?{$version_hash}"></script> {/foreach} {/if} <link rel="stylesheet" type="text/css" href="{$admin_url}static/print.css?{$version_hash}" media="print" /> <link rel="stylesheet" type="text/css" href="{$admin_url}static/handheld.css?{$version_hash}" media="handheld,screen and (max-width:981px)" /> <link rel="manifest" href="{$admin_url}manifest.php" /> {if isset($config)} <link rel="icon" type="image/png" href="{$config->fileURL('favicon')}" /> {custom_colors config=$config} {/if} </head> <body{if isset($transparent)} class="transparent"{/if}> {if !array_key_exists('_dialog', $_GET) && !isset($transparent)} <header class="header"> <nav class="menu"> {if isset($config)} <figure class="logo"> {if $url = $config->fileURL('logo', '150px')} <a href="{$admin_url}"><img src="{$url}" alt="" /></a> {/if} </figure> {/if} <ul> {if $is_logged} <?php $current_parent = substr($current, 0, strpos($current, '/')); ?> <li class="home{if $current == 'home'} current{elseif $current_parent == 'home'} current_parent{/if}"> <a href="{$admin_url}"><b class="icn">⌂</b><i> Accueil</i></a> |
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 | <li{if $current == 'me/services'} class="current"{/if}><a href="{$admin_url}me/services.php">Mes activités & cotisations</a></li> </ul> </li> {if !defined('Garradin\LOCAL_LOGIN') || !LOCAL_LOGIN} <li class="logout"><a href="{$admin_url}logout.php"><b class="icn">⤝</b><i> Déconnexion</i></a></li> {/if} {elseif !defined('Garradin\INSTALL_PROCESS')} <li><a href="{if $config.site_asso}{$config.site_asso}{else}{$www_url}{/if}">← Retour au site</a></li> <li><a href="{$admin_url}">Connexion</a> <ul> <li><a href="{$admin_url}password.php">Mot de passe perdu</a> </ul> </li> | > > > > > > > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | <li{if $current == 'me/services'} class="current"{/if}><a href="{$admin_url}me/services.php">Mes activités & cotisations</a></li> </ul> </li> {if !defined('Garradin\LOCAL_LOGIN') || !LOCAL_LOGIN} <li class="logout"><a href="{$admin_url}logout.php"><b class="icn">⤝</b><i> Déconnexion</i></a></li> {/if} {if $help_url} <li> <a href="{$help_url}" target="_blank"><b class="icn">❓</b><i> Aide</i></a> </li> {/if} {elseif !defined('Garradin\INSTALL_PROCESS')} <li><a href="{if $config.site_asso}{$config.site_asso}{else}{$www_url}{/if}">← Retour au site</a></li> <li><a href="{$admin_url}">Connexion</a> <ul> <li><a href="{$admin_url}password.php">Mot de passe perdu</a> </ul> </li> |
︙ | ︙ |
Modified src/templates/admin/config/_menu.tpl from [91dfe799a4] to [3e2e12b62f].
1 2 3 4 5 6 7 8 9 10 | <nav class="tabs"> <ul> <li{if $current == 'index'} class="current"{/if}><a href="{$admin_url}config/">Général</a></li> <li{if $current == 'categories'} class="current"{/if}><a href="{$admin_url}config/categories/">Catégories de membres</a></li> <li{if $current == 'fiches_membres'} class="current"{/if}><a href="{$admin_url}config/membres.php">Fiche des membres</a></li> <li{if $current == 'backup'} class="current"{/if}><a href="{$admin_url}config/backup/">Sauvegardes</a></li> <li{if $current == 'plugins'} class="current"{/if}><a href="{$admin_url}config/plugins.php">Extensions</a></li> <li{if $current == 'advanced'} class="current"{/if}><a href="{$admin_url}config/advanced/">Fonctions avancées</a></li> </ul> | > | 1 2 3 4 5 6 7 8 9 10 11 | <nav class="tabs"> <ul> <li{if $current == 'index'} class="current"{/if}><a href="{$admin_url}config/">Général</a></li> <li{if $current == 'custom'} class="current"{/if}><a href="{$admin_url}config/custom.php">Personnalisation</a></li> <li{if $current == 'categories'} class="current"{/if}><a href="{$admin_url}config/categories/">Catégories de membres</a></li> <li{if $current == 'fiches_membres'} class="current"{/if}><a href="{$admin_url}config/membres.php">Fiche des membres</a></li> <li{if $current == 'backup'} class="current"{/if}><a href="{$admin_url}config/backup/">Sauvegardes</a></li> <li{if $current == 'plugins'} class="current"{/if}><a href="{$admin_url}config/plugins.php">Extensions</a></li> <li{if $current == 'advanced'} class="current"{/if}><a href="{$admin_url}config/advanced/">Fonctions avancées</a></li> </ul> |
︙ | ︙ |
Added src/templates/admin/config/custom.tpl version [f0931e6c67].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | {include file="admin/_head.tpl" title="Personnalisation" current="config" custom_css=['config.css']} {include file="admin/config/_menu.tpl" current="custom"} {if isset($_GET['ok']) && !$form->hasErrors()} <p class="block confirm"> La configuration a bien été enregistrée. </p> {/if} {form_errors} <fieldset> <legend>Association et site web</legend> <dl> <dt>Logo</dt> {if $url = $config->fileURL('logo', '150px')} <dd> <img src="{$url}" alt="" /> </dd> {/if} <dd> {linkbutton href="!config/edit_file.php?k=%s"|args:'logo' label="Modifier" shape="edit" target="_dialog"} </dd> <dd class="help"> Ce logo sera affiché en haut du menu de l'administration, sur le site web et sur les documents imprimés. </dd> <dt>Petite icône</dt> {if $url = $config->fileURL('favicon')} <dd> <img src="{$url}" alt="" /> </dd> {/if} <dd> {linkbutton href="!config/edit_file.php?k=%s"|args:'favicon' label="Modifier" shape="edit" target="_dialog"} </dd> <dd class="help"> Cette image sera affichée dans l'onglet du navigateur (favicon). </dd> <dt>Grande icône</dt> {if $url = $config->fileURL('icon', '150px')} <dd class="image-preview"> <img src="{$url}" alt="" /> <figure class="masked-icon" title="Aperçu de l'icône sur téléphone"> <span class="icon"><img src="{$url}" alt="" /></span> <figcaption>{$config.nom_asso|truncate:12:'…':true}</figcaption> </figure> </dd> {/if} <dd> {linkbutton href="!config/edit_file.php?k=%s"|args:'icon' label="Modifier" shape="edit" target="_dialog"} </dd> <dd class="help"> Cette image sera utilisée comme icône de l'application mobile (à installer depuis {link href="!" label="la page d'accueil"} et le bouton « Installer comme application sur l'écran d'accueil »). </dd> </dl> </fieldset> <form method="post" action="{$self_url}"> <fieldset> <legend>Interface d'administration</legend> <dl> {input type="color" pattern="#[a-f0-9]{6}" title="Couleur au format hexadécimal" default=$color1 source=$config name="couleur1" label="Couleur primaire" placeholder=$color1} {input type="color" pattern="#[a-f0-9]{6}" title="Couleur au format hexadécimal" default=$color2 source=$config name="couleur2" label="Couleur secondaire" placeholder=$color2} {input type="file" label="Image de fond" name="background" help="Il est conseillé d'utiliser une image en noir et blanc avec un fond blanc pour un meilleur rendu. Dimensions recommandées : 380x200" accept="image/*,*.jpeg,*.jpg,*.png,*.gif"} <dt>Texte de la page d'accueil</dt> <dd> {linkbutton href="!config/edit_file.php?k=%s"|args:'admin_homepage' label="Modifier" shape="edit" target="_dialog" data-dialog-height="90%"} </dd> <dd class="help"> Ce contenu sera affiché à la connexion d'un membre, ou en cliquant sur l'onglet 'Accueil' du menu de gauche. </dd> <dt>Personnalisation CSS de l'administration</dt> <dd> {linkbutton href="!config/edit_file.php?k=%s"|args:'admin_css' label="Modifier" shape="edit" target="_dialog" data-dialog-height="90%"} </dd> <dd class="help"> Permet de rajouter des <a href="https://developer.mozilla.org/fr/docs/Learn/CSS/First_steps" target="_blank">règles CSS</a> qui modifieront l'apparence de l'interface d'administration. </dd> </dl> <input type="hidden" name="admin_background" id="f_admin_background" data-current="{$background_image_current}" data-default="{$background_image_default}" value="{$_POST.admin_background}" /> <p class="submit"> {csrf_field key="config_custom"} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} </p> </fieldset> </form> {include file="admin/_foot.tpl"} |
Added src/templates/admin/config/edit_image.tpl version [ecffc7fa59].
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | {include file="admin/_head.tpl" title="Envoi d'image"} {form_errors} <form method="post" action="{$self_url}" enctype="multipart/form-data" data-focus="1"> <fieldset> <legend>Téléverser un fichier</legend> <dl> {input type="file" name="file" label="Fichier à envoyer" data-enhanced=1} </dl> <p class="submit"> {csrf_field key=$csrf_key} {button type="submit" name="upload" label="Envoyer" shape="upload" class="main"} {button type="submit" name="reset" label="Supprimer" shape="delete"} </p> </fieldset> </form> {include file="admin/_foot.tpl"} |
Modified src/templates/admin/config/index.tpl from [b8e165fd8b] to [6e42e917da].
︙ | ︙ | |||
63 64 65 66 67 68 69 | <dl> {input type="select" name="categorie_membres" source=$config options=$membres_cats required=true label="Catégorie par défaut des nouveaux membres"} {input type="select" name="champ_identite" source=$config options=$champs required=true label="Champ utilisé pour définir l'identité des membres" help="Ce champ des fiches membres sera utilisé comme identité du membre dans les emails, les fiches, les pages, etc."} {input type="select" name="champ_identifiant" source=$config options=$champs required=true label="Champ utilisé comme identifiant de connexion" help="Ce champ des fiches membres sera utilisé comme identifiant pour se connecter à Garradin. Ce champ doit être unique (il ne peut pas contenir deux membres ayant la même valeur dans ce champ)."} </dl> </fieldset> | < < < < < < < < < < < < < < < < < < < < < < < < | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <dl> {input type="select" name="categorie_membres" source=$config options=$membres_cats required=true label="Catégorie par défaut des nouveaux membres"} {input type="select" name="champ_identite" source=$config options=$champs required=true label="Champ utilisé pour définir l'identité des membres" help="Ce champ des fiches membres sera utilisé comme identité du membre dans les emails, les fiches, les pages, etc."} {input type="select" name="champ_identifiant" source=$config options=$champs required=true label="Champ utilisé comme identifiant de connexion" help="Ce champ des fiches membres sera utilisé comme identifiant pour se connecter à Garradin. Ce champ doit être unique (il ne peut pas contenir deux membres ayant la même valeur dans ce champ)."} </dl> </fieldset> <p class="submit"> {csrf_field key="config"} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} </p> </form> {include file="admin/_foot.tpl"} |
Modified src/templates/admin/config/upgrade.tpl from [cbbd2e2031] to [f0a3b1ba46].
︙ | ︙ | |||
23 24 25 26 27 28 29 | {if !$can_verify} <br />(Cela est probablement dû au fait que votre installation ne dispose pas du module <em>GnuPG</em>.) {/if} </p> {/if} <details> <summary><h3>{$diff.delete|count} fichiers seront supprimés</h3></summary> | | | | | | | | | | | 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 | {if !$can_verify} <br />(Cela est probablement dû au fait que votre installation ne dispose pas du module <em>GnuPG</em>.) {/if} </p> {/if} <details> <summary><h3>{$diff.delete|count} fichiers seront supprimés</h3></summary> <dl> {foreach from=$diff.delete key="file" item="path"} <dd>{$file}</dd> {/foreach} </dl> </details> <details> <summary><h3>{$diff.create|count} fichiers seront rajoutés</h3></summary> <dl> {foreach from=$diff.create key="file" item="path"} <dd>{$file}</dd> {/foreach} </dl> </details> <details> <summary><h3>{$diff.update|count} fichiers seront modifiés</h3></summary> <p class="alert block"> Si vous aviez bidouillé ces fichiers, les modifications seront écrasées. </p> <dl> {foreach from=$diff.update key="file" item="path"} <dd>{$file}</dd> {/foreach} </dl> </details> <dl class="block error"> {input type="checkbox" name="upgrade" value=$version label="Je confirme vouloir procéder à la mise à jour" help="Cette action peut casser votre installation !"} </dl> </fieldset> <p class="alert block">N'oubliez pas d'aller {link href="%swiki/?name=Changelog"|args:WEBSITE target="_blank" label="lire le journal des changements"} avant d'effectuer la mise à jour !</p> |
︙ | ︙ |
Modified src/templates/admin/index.tpl from [6f63e0375d] to [03e37c3c4d].
1 2 3 4 5 6 7 8 9 10 11 12 | {include file="admin/_head.tpl" title="Bonjour %s !"|args:$user.identite current="home"} {$banner|raw} <nav class="tabs"> <ul> <li><a href="{$admin_url}me/">Mes informations personnelles</a></li> <li><a href="{$admin_url}me/services.php">Suivi de mes activités et cotisations</a></li> </ul> </nav> <nav class="home-buttons"> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | {include file="admin/_head.tpl" title="Bonjour %s !"|args:$user.identite current="home"} {$banner|raw} <nav class="tabs"> <ul> <li><a href="{$admin_url}me/">Mes informations personnelles</a></li> <li><a href="{$admin_url}me/services.php">Suivi de mes activités et cotisations</a></li> </ul> </nav> <nav class="home-buttons"> {button id="homescreen-btn" label="Installer comme application sur l'écran d'accueil" class="hidden" shape="plus"} </nav> <aside class="describe"> <h3>{$config.nom_asso}</h3> {if !empty($config.adresse_asso)} <p> {$config.adresse_asso|escape|nl2br} |
︙ | ︙ |
Modified src/templates/common/files/edit_code.tpl from [bf7571f93a] to [7057de99c4].
︙ | ︙ | |||
8 9 10 11 12 13 14 | <p class="submit"> {csrf_field key=$csrf_key} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} </p> </form> | | > > | 8 9 10 11 12 13 14 15 16 17 18 19 | <p class="submit"> {csrf_field key=$csrf_key} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} </p> </form> <script type="text/javascript" src="{$admin_url}static/scripts/code_editor.js?{$version_hash}"></script> <script type="text/javascript"> {include file="admin/_foot.tpl"} |
Modified src/templates/common/files/edit_web.tpl from [c721ff089b] to [bf6e4a6a9c].
1 2 3 4 | {include file="admin/_head.tpl" title="Édition de fichier" custom_js=['wiki_editor.js']} <form method="post" action="{$self_url}"> <p class="textEditor"> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | {include file="admin/_head.tpl" title="Édition de fichier" custom_js=['wiki_editor.js']} <form method="post" action="{$self_url}"> <p class="textEditor"> {input type="textarea" name="content" cols="70" rows="30" default=$content data-preview-url="!common/files/_preview.php?f=%s"|local_url|args:$path data-fullscreen="1" data-attachments="0" data-savebtn="1" data-format=$format} </p> <p class="submit"> {csrf_field key=$csrf_key} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} </p> </form> {include file="admin/_foot.tpl"} |
Modified src/templates/services/user/add.tpl from [f7d8e48aef] to [e38a83f3e8].
1 2 3 4 5 6 7 | {include file="admin/_head.tpl" title="Inscrire à une activité" current="membres/services"} {include file="services/_nav.tpl" current="save" fee=null service=null} {form_errors} {if !$user_id} | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | {include file="admin/_head.tpl" title="Inscrire à une activité" current="membres/services"} {include file="services/_nav.tpl" current="save" fee=null service=null} {form_errors} {if !$user_id} <form method="post" action="{$self_url}"> <fieldset> <legend>Inscrire un membre à une activité</legend> <dl> {input type="list" name="user" required=1 label="Sélectionner un membre" default=$selected_user target="membres/selector.php"} </dl> </fieldset> |
︙ | ︙ |
Modified src/templates/web/page.tpl from [3803091252] to [618d6c6d33].
︙ | ︙ | |||
8 9 10 11 12 13 14 | </aside> {else} <aside> {linkbutton href="?p=%s&toggle_type"|args:$page.path label="Transformer en catégorie" shape="reset"} </aside> {/if} <ul> | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | </aside> {else} <aside> {linkbutton href="?p=%s&toggle_type"|args:$page.path label="Transformer en catégorie" shape="reset"} </aside> {/if} <ul> <li><a href="{$admin_url}web/?p={if $page.type == $page::TYPE_CATEGORY}{$page.path}{else}{$page.parent}{/if}">Retour à la liste</a></li> {if $session->canAccess($session::SECTION_WEB, $session::ACCESS_WRITE)} <li><a href="{$admin_url}web/edit.php?p={$page.path}">Modifier</a></li> {/if} {if $page.status == $page::STATUS_ONLINE && !$config.site_disabled} <li><a href="{$page->url()}" target="_blank">Voir sur le site</a></li> {/if} {if $session->canAccess($session::SECTION_WEB, $session::ACCESS_WRITE)} |
︙ | ︙ |
Modified src/www/.htaccess from [aeeaf470c2] to [477ff985ec].
|
| | | 1 2 3 4 5 6 7 8 | Options -Indexes -Multiviews DirectoryIndex disabled DirectoryIndex index.php # Rediriger les adresses dynamiques vers le routeur FallbackResource /_route.php # Si FallbackResource ne fonctionne pas, utiliser ceci : |
︙ | ︙ |
Modified src/www/_route.php from [9acabfe9b8] to [f77d1dc351].
︙ | ︙ | |||
12 13 14 15 16 17 18 | if ('_route.php' === basename($uri)) { http_response_code(403); die('Appel interdit'); } http_response_code(200); | < < < < | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | if ('_route.php' === basename($uri)) { http_response_code(403); die('Appel interdit'); } http_response_code(200); if (($pos = strpos($uri, '?')) !== false) { $uri = substr($uri, 0, $pos); } if (file_exists(__DIR__ . $uri)) { |
︙ | ︙ |
Modified src/www/admin/acc/charts/edit.php from [012860281d] to [1189423941].
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | } if (f('save') && $form->check('acc_charts_edit_' . $chart->id())) { try { $chart->importForm(); $chart->save(); Utils::redirect(sprintf('%sacc/charts/', ADMIN_URL)); } catch (UserException $e) { $form->addError($e->getMessage()); | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | } if (f('save') && $form->check('acc_charts_edit_' . $chart->id())) { try { $chart->importForm(); $chart->set('archived', (int) f('archived')); $chart->save(); Utils::redirect(sprintf('%sacc/charts/', ADMIN_URL)); } catch (UserException $e) { $form->addError($e->getMessage()); |
︙ | ︙ |
Modified src/www/admin/acc/reports/_inc.php from [c7c07f3287] to [d05357fbdb].
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 | $criterias['analytical_only'] = true; } if (!count($criterias)) { throw new UserException('Critère de rapport inconnu.'); } $tpl->assign('criterias', $criterias); $tpl->assign('criterias_query', http_build_query($criterias)); | > > > > > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | $criterias['analytical_only'] = true; } if (!count($criterias)) { throw new UserException('Critère de rapport inconnu.'); } if ($y2 = Years::get((int)qg('compare_year'))) { $tpl->assign('year2', $y2); $criterias['compare_year'] = $y2->id; } $tpl->assign('criterias', $criterias); $tpl->assign('criterias_query', http_build_query($criterias)); |
Modified src/www/admin/acc/reports/balance_sheet.php from [8ddee02813] to [f3e2995539].
1 2 3 4 5 6 7 8 9 | <?php namespace Garradin; use Garradin\Accounting\Reports; use Garradin\Entities\Accounting\Account; require_once __DIR__ . '/_inc.php'; | > | | | | < | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php namespace Garradin; use Garradin\Accounting\Reports; use Garradin\Accounting\Years; use Garradin\Entities\Accounting\Account; require_once __DIR__ . '/_inc.php'; $tpl->assign('balance', Reports::getBalanceSheet($criterias)); if (!empty($criterias['year'])) { $years = Years::listClosedAssocExcept($criterias['year']); $tpl->assign('other_years', count($years) ? [null => '-- Ne pas comparer'] + $years : $years); } $tpl->display('acc/reports/balance_sheet.tpl'); |
Modified src/www/admin/acc/reports/graph_plot_all.php from [de8f97a287] to [eb0fafc20b].
︙ | ︙ | |||
9 10 11 12 13 14 15 | header('Content-Type: image/svg+xml'); $expiry = time() - 600; $hash = sha1('plot_all'); if (!Utils::HTTPCache($hash, $expiry)) { | | | 9 10 11 12 13 14 15 16 17 | header('Content-Type: image/svg+xml'); $expiry = time() - 600; $hash = sha1('plot_all'); if (!Utils::HTTPCache($hash, $expiry)) { echo Graph::bar(qg('type'), []); } |
Modified src/www/admin/acc/reports/statement.php from [3be4e9af46] to [45a0377453].
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin; use Garradin\Accounting\Reports; use Garradin\Entities\Accounting\Account; require_once __DIR__ . '/_inc.php'; $tpl->assign('general', Reports::getStatement($criterias + ['exclude_type' => Account::TYPE_VOLUNTEERING])); $tpl->assign('volunteering', Reports::getStatement($criterias + ['type' => Account::TYPE_VOLUNTEERING])); $tpl->display('acc/reports/statement.tpl'); | > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php namespace Garradin; use Garradin\Accounting\Reports; use Garradin\Accounting\Years; use Garradin\Entities\Accounting\Account; require_once __DIR__ . '/_inc.php'; $tpl->assign('general', Reports::getStatement($criterias + ['exclude_type' => Account::TYPE_VOLUNTEERING])); $tpl->assign('volunteering', Reports::getStatement($criterias + ['type' => Account::TYPE_VOLUNTEERING])); if (!empty($criterias['year'])) { $years = Years::listClosedAssocExcept($criterias['year']); $tpl->assign('other_years', count($years) ? [null => '-- Ne pas comparer'] + $years : $years); } $tpl->display('acc/reports/statement.tpl'); |
Modified src/www/admin/common/files/edit.php from [5a8ed5bb74] to [1c9c88333c].
︙ | ︙ | |||
21 22 23 24 25 26 27 | $form->runIf('content', function () use ($file) { $file->setContent(f('content')); if (qg('js') !== null) { die('{"success":true}'); } | < < > > | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | $form->runIf('content', function () use ($file) { $file->setContent(f('content')); if (qg('js') !== null) { die('{"success":true}'); } }, $csrf_key, Utils::getSelfURI()); $tpl->assign('file', $file); if (!$editor) { $tpl->display('common/file_upload.tpl'); } else { $content = $file->fetch(); $path = $file->path; $format = $file->renderFormat(); $tpl->assign(compact('csrf_key', 'content', 'path', 'format')); $tpl->display(sprintf('common/files/edit_%s.tpl', $editor)); } |
Added src/www/admin/config/custom.php version [650b04f08c].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?php namespace Garradin; use Garradin\Users\Categories; use Garradin\Files\Files; use Garradin\Entities\Files\File; require_once __DIR__ . '/_inc.php'; $config = Config::getInstance(); $form->runIf('save', function () use ($config) { $config->importForm(); if (f('admin_background') == 'RESET') { $config->setFile('admin_background', null); } elseif (f('admin_background')) { $config->setFile('admin_background', base64_decode(f('admin_background'))); } $config->save(); }, 'config_custom', Utils::getSelfURI(['ok' => ''])); $tpl->assign([ 'color1' => ADMIN_COLOR1, 'color2' => ADMIN_COLOR2, ]); $tpl->assign('background_image_current', $config->fileURL('admin_background')); $tpl->assign('background_image_default', ADMIN_BACKGROUND_IMAGE); $tpl->assign('custom_js', ['color_helper.js']); $tpl->display('admin/config/custom.tpl'); |
Modified src/www/admin/config/edit_file.php from [8a746033ac] to [15de1c6be2].
1 2 3 4 5 6 7 8 9 10 | <?php namespace Garradin; use Garradin\Entities\Files\File; use Garradin\Files\Files; require __DIR__ . '/_inc.php'; $key = qg('k'); | | < | | < | < | < < < | < | | | < | | | | > < < > | | > | | | | | < | > > > > > > > | | > | 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 | <?php namespace Garradin; use Garradin\Entities\Files\File; use Garradin\Files\Files; require __DIR__ . '/_inc.php'; $key = qg('k'); $config = Config::getInstance(); if (!isset(Config::FILES[$key])) { throw new UserException('Fichier invalide'); } $file = $config->file($key); $type = Config::FILES_TYPES[$key]; $csrf_key = 'edit_file_' . $key; $form->runIf('upload', function () use ($key, $config) { $config->setFile($key, 'file', true); $config->save(); }, $csrf_key, Utils::getSelfURI()); $form->runIf('reset', function () use ($key, $config) { $config->setFile($key, null); $config->save(); }, $csrf_key, Utils::getSelfURI()); $form->runIf('save', function () use ($key, $config) { $content = trim(f('content')); $config->setFile($key, $content === '' ? null : $content); $config->save(); if (qg('js') !== null) { die('{"success":true}'); } }, $csrf_key, Utils::getSelfURI()); $tpl->assign(compact('csrf_key', 'file')); if ($type == 'image') { $tpl->display('admin/config/edit_image.tpl'); } else { $content = $file ? $file->fetch() : ''; $path = Config::FILES[$key]; $format = $file ? $file->renderFormat() : 'skriv'; $tpl->assign(compact('content', 'path', 'format')); $tpl->display(sprintf('common/files/edit_%s.tpl', $type)); } |
Modified src/www/admin/config/index.php from [00108c3e5f] to [a6385e7542].
︙ | ︙ | |||
26 27 28 29 30 31 32 | 'php_version' => phpversion(), 'has_gpg_support' => \KD2\Security::canUseEncryption(), 'server_time' => time(), 'sqlite_version' => \SQLite3::version()['versionString'], 'countries' => Utils::getCountryList(), 'membres_cats' => Categories::listSimple(), 'champs' => $config->get('champs_membres')->listAssocNames(), | < < < < < < < < < < < < < < | 26 27 28 29 30 31 32 33 34 35 36 | 'php_version' => phpversion(), 'has_gpg_support' => \KD2\Security::canUseEncryption(), 'server_time' => time(), 'sqlite_version' => \SQLite3::version()['versionString'], 'countries' => Utils::getCountryList(), 'membres_cats' => Categories::listSimple(), 'champs' => $config->get('champs_membres')->listAssocNames(), 'garradin_website' => WEBSITE, ]); $tpl->display('admin/config/index.tpl'); |
Modified src/www/admin/docs/new_dir.php from [d2c27619fc] to [d8847a425e].
︙ | ︙ | |||
14 15 16 17 18 19 20 | } $csrf_key = 'create_dir'; $form->runIf('create', function () use ($parent) { $name = trim(f('name')); File::validatePath($parent . '/' . $name); | | > > > > > > > > | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | } $csrf_key = 'create_dir'; $form->runIf('create', function () use ($parent) { $name = trim(f('name')); File::validatePath($parent . '/' . $name); $f = File::createDirectory($parent, $name); $url = '!docs/?path=' . $f->path; if (null !== qg('_dialog')) { Utils::reloadParentFrame($url); } Utils::redirect($url); }, $csrf_key); $tpl->assign(compact('csrf_key')); $tpl->display('docs/new_dir.tpl'); |
Modified src/www/admin/docs/new_file.php from [b548528de7] to [4505ff7d98].
︙ | ︙ | |||
22 23 24 25 26 27 28 | $name .= '.skriv'; } File::validatePath($parent . '/' . $name); $name = File::filterName($name); $file = File::createAndStore($parent, $name, null, ''); | > > | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | $name .= '.skriv'; } File::validatePath($parent . '/' . $name); $name = File::filterName($name); $file = File::createAndStore($parent, $name, null, ''); Utils::redirect('!common/files/edit.php?p=' . rawurlencode($file->path)); }, $csrf_key); $tpl->assign(compact('csrf_key')); $tpl->display('docs/new_file.tpl'); |
Modified src/www/admin/index.php from [025ac1c529] to [bb797c6a32].
1 2 3 4 5 6 7 8 9 10 | <?php namespace Garradin; use Garradin\Web\Web; use Garradin\Files\Files; use Garradin\Entities\Files\File; require_once __DIR__ . '/_inc.php'; | < < > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php namespace Garradin; use Garradin\Web\Web; use Garradin\Files\Files; use Garradin\Entities\Files\File; require_once __DIR__ . '/_inc.php'; $banner = null; Plugin::fireSignal('accueil.banniere', ['user' => $user, 'session' => $session], $banner); $homepage = Config::getInstance()->file('admin_homepage'); if ($homepage) { $homepage = $homepage->render(ADMIN_URL . 'common/files/preview.php?p=' . File::CONTEXT_DOCUMENTS . '/'); } else { $homepage = null; } $tpl->assign(compact('homepage', 'banner')); |
︙ | ︙ |
Modified src/www/admin/manifest.php from [4bf7b7e222] to [97a146fba4].
1 2 3 4 5 6 7 | <?php namespace Garradin; const LOGIN_PROCESS = true; require_once __DIR__ . '/_inc.php'; $manifest = [ | | > | | | > > > > | | 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 | <?php namespace Garradin; const LOGIN_PROCESS = true; require_once __DIR__ . '/_inc.php'; $manifest = [ 'background_color' => $config->couleur2, 'theme_color' => $config->couleur1, 'description' => 'Gestion de l\'association', 'display' => 'fullscreen', 'name' => $config->nom_asso, 'start_url' => ADMIN_URL, 'icons' => [ [ 'sizes' => '32x32', 'src' => $config->fileURL('favicon'), 'type' => 'image/png', 'purpose' => 'any maskable', ], [ 'sizes' => '256x256', 'src' => $config->fileURL('icon', 'crop-256px'), 'type' => 'image/png', 'purpose' => 'any maskable', ], ], ]; $body = json_encode($manifest, JSON_PRETTY_PRINT); Utils::HTTPCache(md5($body), max($config->files['icon'], $config->files['favicon'], strtotime('2011-11-11'))); header('Content-Type: text/json; charset=utf-8'); echo $body; |
Name change from src/www/skel-dist/favicon.png to src/www/admin/static/favicon.png.
cannot compute difference between binary files
Modified src/www/admin/static/handheld.css from [460c6b5a67] to [bb9608a6af].
︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | z-index: 10000; margin: 0; margin-bottom: 3em; width: 100%; padding: 0; display: block; } .header .menu *, .header .menu a { margin: 0; padding: 0; } .header .menu > ul { | > > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | z-index: 10000; margin: 0; margin-bottom: 3em; width: 100%; padding: 0; display: block; } .header .menu .logo { display: none; } .header .menu *, .header .menu a { margin: 0; padding: 0; } .header .menu > ul { |
︙ | ︙ |
Modified src/www/admin/static/icon.png from [5f4be49914] to [1387768a8b].
cannot compute difference between binary files
Modified src/www/admin/static/print.css from [6937e4b226] to [cf1afc3c77].
1 2 | @page { size: A4 landscape; | | | 1 2 3 4 5 6 7 8 9 10 | @page { size: A4 landscape; margin: 1cm; } html { height: auto; } body { |
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | color: #000 !important; border-right: 1px solid #999; } table.list tfoot { border-top: double .3rem #000; } table.list tfoot tr td, table.list tfoot th { background: #fff; color: #000; } table.list tr { | > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | color: #000 !important; border-right: 1px solid #999; } table.list tfoot { border-top: double .3rem #000; } table.statement tfoot tr { color: #000; } table.list tfoot tr td, table.list tfoot th { background: #fff; color: #000; } table.list tr { |
︙ | ︙ |
Modified src/www/admin/static/scripts/code_editor.js from [81fbe437c7] to [9902ec3c83].
︙ | ︙ | |||
85 86 87 88 89 90 91 92 93 | return false; }; } else { appendButton('fullscreen', 'Plein écran', code.toggleFullscreen); } }); }()); | > > | 85 86 87 88 89 90 91 92 93 94 95 | return false; }; } else { appendButton('fullscreen', 'Plein écran', code.toggleFullscreen); } g.setParentDialogHeight('90%'); }); }()); |
Modified src/www/admin/static/scripts/color_helper.js from [6151df33de] to [65ca233b3d].
︙ | ︙ | |||
22 23 24 25 26 27 28 | return '#' + color.split(/,/).map(function (el) { return ('0' + parseInt(el, 10).toString(16)).substr(-2); }).join(''); } function changeColor(element, color) { | | > > > > > > > > > | | > > > > > > > > > > > > > > > | 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 | return '#' + color.split(/,/).map(function (el) { return ('0' + parseInt(el, 10).toString(16)).substr(-2); }).join(''); } function changeColor(element, color) { let new_color = colorToRGB(color, element); let text_color = element == 'gMainColor' ? [255, 255, 255] : [0, 0, 0]; let change = element == 'gMainColor' ? -5 : 5; while (!checkContrast(new_color, text_color)) { new_color[0] += change; new_color[1] += change; new_color[2] += change; } // Mise à jour variable CSS document.documentElement.style.setProperty('--' + element, new_color.join(',')); applyColors(); return new_color.join(','); } /** * Return true if contrast is OK (W3C AA-level), false if not * @see https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o */ function checkContrast(color1, color2) { let l1 = 0.2126 * color1[0] + 0.7152 * color1[1] + 0.0722 * color1[2]; let l2 = 0.2126 * color2[0] + 0.7152 * color2[1] + 0.0722 * color2[2]; let ratio = l1 > l2 ? ((l2 + 0.05) / (l1 + 0.05)) : ((l1 + 0.05) / (l2 + 0.05)); return ratio < 1/3 ? true : false; } function applyColors() { let input = $('#f_couleur2'); let color = colorToRGB(input.value, 'gSecondColor'); let color1 = $('#f_couleur1'), color2 = $('#f_couleur2'); |
︙ | ︙ |
Modified src/www/admin/static/scripts/datepicker2.js from [6f612d2aa2] to [317a9279e3].
︙ | ︙ | |||
14 15 16 17 18 19 20 | this.button = button; this.input = input; this.date = null; Object.assign(this, { format: 0, // 0 = Y-m-d, 1 = d/m/Y lang: 'fr', | | < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | this.button = button; this.input = input; this.date = null; Object.assign(this, { format: 0, // 0 = Y-m-d, 1 = d/m/Y lang: 'fr', class: 'datepicker' }, config); var c = document.createElement('dialog'); c.className = this.class; this.container = button.parentNode.insertBefore(c, button.nextSibling); button.onclick = () => { this.container.hasAttribute('open') ? this.close() : this.open() }; |
︙ | ︙ | |||
183 184 185 186 187 188 189 | if (this.input) { this.input.value = v; } this.close(); | > | | < > | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | if (this.input) { this.input.value = v; } this.close(); event = document.createEvent('HTMLEvents'); event.initEvent('change', true, true); event.eventName = 'change'; this.input.dispatchEvent(event); } focus() { this.container.querySelectorAll('tbody td').forEach((cell) => { var v = parseInt(cell.innerHTML, 10); |
︙ | ︙ |
Modified src/www/admin/static/scripts/global.js from [8f3eacf771] to [b111eaadcd].
1 | (function () { | < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | (function () { window.g = window.garradin = { url: window.location.href.replace(/\/admin\/.*?$/, ''), admin_url: window.location.href.replace(/\/admin\/.*?$/, '/admin/'), static_url: window.location.href.replace(/\/admin\/.*?$/, '/admin/static/'), version: document.documentElement.getAttribute('data-version'), loaded: {} }; window.$ = function(selector) { if (!selector.match(/^[.#]?[a-z0-9_-]+$/i)) { return document.querySelectorAll(selector); |
︙ | ︙ | |||
160 161 162 163 164 165 166 167 168 169 170 | var iframe = document.createElement('iframe'); iframe.src = url; iframe.name = 'dialog'; iframe.id = 'frameDialog'; iframe.frameborder = '0'; iframe.scrolling = 'yes'; iframe.width = iframe.height = 0; iframe.addEventListener('load', () => { iframe.contentWindow.onkeyup = (e) => { if (e.key == 'Escape') g.closeDialog(); }; // We need to wait a bit for the height to be correct, not sure why window.setTimeout(() => { | > | > > > > > > > > > | > > > > > > | | | | | | | > | 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 213 214 215 216 217 218 219 220 221 | var iframe = document.createElement('iframe'); iframe.src = url; iframe.name = 'dialog'; iframe.id = 'frameDialog'; iframe.frameborder = '0'; iframe.scrolling = 'yes'; iframe.width = iframe.height = 0; iframe.setAttribute('data-height', height); iframe.addEventListener('load', () => { iframe.contentWindow.onkeyup = (e) => { if (e.key == 'Escape') g.closeDialog(); }; // We need to wait a bit for the height to be correct, not sure why window.setTimeout(() => { iframe.style.height = iframe.dataset.height == 'auto' ? iframe.contentWindow.document.body.offsetHeight + 'px' : iframe.dataset.height; }, 100); }); g.openDialog(iframe, callback); return iframe; }; g.reloadParentDialog = () => { if (!window.parent.g.dialog) { return; } location.href = window.parent.g.dialog.querySelector('iframe').getAttribute('src'); }; g.setParentDialogHeight = (height) => { if (!window.parent.g.dialog) { return; } window.parent.g.dialog.querySelector('iframe').setAttribute('data-height', height); g.resizeParentDialog(height); }; g.resizeParentDialog = (forced_height) => { if (!window.parent.g.dialog) { return; } let height; if (forced_height) { height = forced_height; } else { let body_height = document.body.offsetHeight; let parent_height = window.parent.innerHeight; if (body_height > parent_height * 0.9) { height = '90%'; } else { height = body_height + 'px'; } } window.parent.g.dialog.childNodes[1].style.height = height; }; g.closeDialog = function () { if (null === g.dialog) { |
︙ | ︙ |
Modified src/www/admin/static/scripts/wiki_editor.js from [7a505bf40b] to [7df1c9bd9b].
︙ | ︙ | |||
248 249 250 251 252 253 254 | fetch(t.textarea.form.action + '&js', { method: 'post', body: data, }).then((response) => response.json()) .then(data => { showSaved(); t.textarea.defaultValue = t.textarea.value; | > | > > > > > | | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | fetch(t.textarea.form.action + '&js', { method: 'post', body: data, }).then((response) => response.json()) .then(data => { showSaved(); t.textarea.defaultValue = t.textarea.value; let e = t.textarea.form.querySelector('input[name=editing_started]'); if (e) { e.value = data.modified; } }).catch(e => { console.log(e); t.textarea.form.querySelector('[type=submit]').click(); } ); return true; }; let createToolbar = () => { appendButton('title', "Titre", applyHeader ); appendButton('bold', 'Gras', applyBold ); appendButton('italic', "Italique", applyItalic ); |
︙ | ︙ | |||
310 311 312 313 314 315 316 317 318 | t.shortcuts.push({ctrl: true, key: 'g', callback: applyBold }); t.shortcuts.push({ctrl: true, key: 'i', callback: applyItalic }); t.shortcuts.push({ctrl: true, key: 't', callback: applyHeader }); t.shortcuts.push({ctrl: true, key: 'l', callback: insertURL}); t.shortcuts.push({ctrl: true, key: 's', callback: save}); t.shortcuts.push({ctrl: true, shift: true, key: 'p', callback: openPreview}); t.shortcuts.push({key: 'F1', callback: openSyntaxHelp}); }); }()); | > > | 316 317 318 319 320 321 322 323 324 325 326 | t.shortcuts.push({ctrl: true, key: 'g', callback: applyBold }); t.shortcuts.push({ctrl: true, key: 'i', callback: applyItalic }); t.shortcuts.push({ctrl: true, key: 't', callback: applyHeader }); t.shortcuts.push({ctrl: true, key: 'l', callback: insertURL}); t.shortcuts.push({ctrl: true, key: 's', callback: save}); t.shortcuts.push({ctrl: true, shift: true, key: 'p', callback: openPreview}); t.shortcuts.push({key: 'F1', callback: openSyntaxHelp}); g.setParentDialogHeight('90%'); }); }()); |
Modified src/www/admin/static/styles/01-layout.css from [f34fa8a8e0] to [9046a79977].
︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | .header h1 { color: rgb(var(--gMainColor)); margin-left: 180px; margin-bottom: 0.4em; } .header .menu { position: fixed; overflow: auto; z-index: 1000; width: 170px; top: 0; bottom: 0; | > > > > > > > > > > > > > > > > > > < | 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 | .header h1 { color: rgb(var(--gMainColor)); margin-left: 180px; margin-bottom: 0.4em; } .header .menu .logo { padding: 0; min-height: 100px; } .header .menu .logo img { transition: opacity .2s; } .header .menu .logo a { padding: 10px; padding-bottom: 0; display: inline-block; text-align: center; color: inherit; width: 150px; } .header .menu { position: fixed; overflow: auto; z-index: 1000; width: 170px; top: 0; bottom: 0; background: rgb(var(--gMainColor)) var(--gBgImage) no-repeat 0px 0px; } .header .menu::-webkit-scrollbar { width: 8px; background: rgba(255, 255, 255, 0.25); box-shadow: inset 0px 0px 10px #666; |
︙ | ︙ |
Modified src/www/admin/static/styles/02-common.css from [b08c6421a8] to [72a91c6a02].
︙ | ︙ | |||
360 361 362 363 364 365 366 | float: right; padding: .5em; border: .5em solid #000; background: #fff; } details { | < < > | | | | > > | | > > > | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | float: right; padding: .5em; border: .5em solid #000; background: #fff; } details { margin-bottom: 1em; } details summary { list-style: none; padding: 0.2em 0.5em; transition: background-color .2s; position: relative; padding-left: 4em; } details summary:hover { cursor: pointer; background-color: rgba(var(--gMainColor), 0.1); } details summary::-webkit-details-marker { display: none; } details summary::after { content: "↓"; position: absolute; left: 0; top: 0; bottom: 0; /* From .icn-btn */ cursor: pointer; color: #003; border: 1px solid rgba(var(--gMainColor), 0.5); background-color: rgba(var(--gSecondColor), 0.1); user-select: none; display: inline-block; font-size: inherit; border-radius: .2em; padding: .2em .4em; margin: auto .5em; height: 1em; white-space: pre; transition: color .3s, background-color .3s; font-family: "gicon", sans-serif; text-shadow: 1px 1px 1px #999; font-size: 1.2em; } details[open] summary::after { content: "↑"; } details summary:hover::after { |
︙ | ︙ |
Modified src/www/admin/static/styles/03-forms.css from [9d02310ab4] to [48a1aece0a].
︙ | ︙ | |||
449 450 451 452 453 454 455 | border-radius: .5em; } .datepicker input { font-family: gicon; } | | | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 | border-radius: .5em; } .datepicker input { font-family: gicon; } [data-icon]:before, .main[data-icon]:after { display: inline-block; font-family: "gicon", sans-serif; text-shadow: 1px 1px 1px #ccc; padding-right: .5em; font-size: 1.2em; line-height: .8em; vertical-align: middle; |
︙ | ︙ |
Modified src/www/admin/static/styles/10-accounting.css from [d4efb6cfa3] to [1b7cf590bb].
︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 85 86 87 88 | padding-bottom: .5em; border-bottom: 1pt solid #999; } .year-header .print-btn button { font-size: 1.3rem; } .year-infos { text-align: center; } .year-infos .graphs { display: flex; | > > > > > | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | padding-bottom: .5em; border-bottom: 1pt solid #999; } .year-header .print-btn button { font-size: 1.3rem; } .year-header form { max-width: 30em; margin: 1em auto; } .year-infos { text-align: center; } .year-infos .graphs { display: flex; |
︙ | ︙ |
Modified src/www/admin/static/styles/config.css from [bc72f7c8a4] to [33fa4a39f3].
︙ | ︙ | |||
97 98 99 100 101 102 103 | cursor: pointer; } #orderFields fieldset .interactive:hover { cursor: pointer; text-decoration: underline; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | cursor: pointer; } #orderFields fieldset .interactive:hover { cursor: pointer; text-decoration: underline; } .image-preview img, .image-preview figure { vertical-align: middle; } .image-preview img { max-width: 150px; } .masked-icon { background: linear-gradient(rgba(var(--gMainColor), 0.9), rgba(0, 0, 0, 0.5)); border-radius: .5em; padding: 10px; display: inline-flex; flex-direction: column; text-align: center; margin-left: 2em; } .masked-icon .icon { filter: drop-shadow(0px 2px 2px rgba(0, 0, 0, 0.5)); display: inline-block; } .masked-icon figcaption { font-size: .9em; font-family: "Droid Sans", sans-serif; text-shadow: 0px 0px 5px #000; color: #fff; } .masked-icon img { clip-path: circle(40px at center); background: #fff; width: 100px; height: 100px; box-shadow: 1px 10px 5px #000; display: block; } |
Modified src/www/skel-dist/_head.html from [a4ef48e61c] to [f9b2a89dba].
1 2 3 4 5 | <!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8" /> <title>{{if $title}}{{$title}} — {{/if}}{{$config.nom_asso}}</title> | < > | | 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 | <!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8" /> <title>{{if $title}}{{$title}} — {{/if}}{{$config.nom_asso}}</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, target-densitydpi=device-dpi" /> <link rel="stylesheet" type="text/css" href="{{$root_url}}default.css" media="screen,projection,handheld" /> <link rel="stylesheet" type="text/css" href="{{$root_url}}content.css" media="all" /> <link rel="alternate" type="application/atom+xml" title="{{$config.nom_asso}}" href="{{$root_url}}atom.xml" /> <link rel="icon" type="image/png" href="{{$config.files.favicon}}" /> </head> <body> <header class="nav"> <nav> <ul> <li class="current"><a href="{{$root_url}}">Accueil</a></li> <li><a href="{{$admin_url}}">Administration</a></li> </ul> </nav> </header> <header class="main"> <h1><a href="{{$root_url}}">{{if $config.files.logo}}<img src="{{$config.files.logo}}&150px" alt="" class="logo" />{{/if}} <span>{{$config.nom_asso}}</span></a></h1> {{if $config.adresse_asso || $config.telephone_asso || $config.email_asso}} <article class="contacts"> {{if $config.adresse_asso}} <h4>{{$config.adresse_asso|escape}}</h4> {{/if}} {{if $config.telephone_asso}} |
︙ | ︙ |
Modified src/www/skel-dist/content.css from [520eaea40b] to [36b56b4140].
︙ | ︙ | |||
90 91 92 93 94 95 96 | } .web-content .toc { margin: 1rem 0; border: 1px solid rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.1); padding: .3rem; | | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | } .web-content .toc { margin: 1rem 0; border: 1px solid rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.1); padding: .3rem; display: inline-block; } .web-content .toc ol { list-style: none; counter-reset: item; margin: .5rem 0 .5rem .5rem; } |
︙ | ︙ |
Modified src/www/skel-dist/default.css from [2d1b805a22] to [aa7580d3cb].
︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | padding: .2em 0 .1em 0; font-size: 4em; font-family: Georgia, "Times New Roman", Times, serif; font-weight: normal; } header.main h1 a { color: #9c4f15; text-decoration: none; } header.main { margin-bottom: 1em; background: url("data:image/png;base64,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") no-repeat top right; } /* LISTE DES CATÉGORIES EN DESSOUS DU NOM DE L'ASSOCIATION */ | > > > > > > > > | 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 | padding: .2em 0 .1em 0; font-size: 4em; font-family: Georgia, "Times New Roman", Times, serif; font-weight: normal; } header.main h1 a { display: flex; flex-direction: row-reverse; align-items: center; justify-content: space-between; color: #9c4f15; text-decoration: none; } header.main h1 a span { margin-right: auto; } header.main { margin-bottom: 1em; background: url("data:image/png;base64,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") no-repeat top right; } /* LISTE DES CATÉGORIES EN DESSOUS DU NOM DE L'ASSOCIATION */ |
︙ | ︙ | |||
152 153 154 155 156 157 158 159 160 161 162 163 164 165 | } .breadcrumbs ul li::before { content: "»"; color: #ccc; margin: .5em; } .breadcrumbs a { color: #999; } /* MESSAGES ALERTE ET ERREUR (par exemple : page non trouvée) */ | > > > > | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | } .breadcrumbs ul li::before { content: "»"; color: #ccc; margin: .5em; } .breadcrumbs ul li:nth-child(1)::before { content: ""; } .breadcrumbs a { color: #999; } /* MESSAGES ALERTE ET ERREUR (par exemple : page non trouvée) */ |
︙ | ︙ |
Added tools/fossil-verify.sh version [abbb39b794].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | #!/bin/bash REPO="$1" if [ ! -f "$1/manifest" ] then echo "Missing manifest, maybe you didn't specify a repository path," echo "or you didn't enable the manifest? (fossil settings manifest on)" echo "Usage: $0 FOSSIL_REPOSITORY_PATH" exit 1 fi gpg --verify "$1/manifest" 2> /dev/null if [ $? != 0 ] then echo "Manifest signature failed to verify" exit 2 fi TMPFILE=$(mktemp) while IFS= read -r LINE do if [ "${LINE:0:2}" != "F " ] then echo "$LINE" >> $TMPFILE continue fi # Split string by spaces PARTS=($LINE) FILE_ENCODED="${PARTS[1]}" FILE="${PARTS[1]//\\s/ }" HASH="${PARTS[2]}" if [ "${#HASH}" = 40 ] then NEW_HASH=$(sha1sum "$1/$FILE" | awk '{print $1}') else NEW_HASH=$(openssl dgst -sha3-256 -binary "$1/$FILE" | xxd -p -c 100) fi if [ "$HASH" != "$NEW_HASH" ] then echo "Local file has changed" echo "$FILE" echo "Manifest hash: $HASH" echo "Local file hash: $NEW_HASH" exit 2 fi PARTS[2]="$HASH" # join parts in a new string NEW_LINE="$(printf " %s" "${PARTS[@]}")" NEW_LINE="${NEW_LINE:1}" echo "$NEW_LINE" >> $TMPFILE done < "$1/manifest" gpg --verify $TMPFILE 2>/dev/null if [ $? != 0 ] then echo "Something has changed between manifest and check?!" diff "$1/manifest" $TMPFILE rm -f $TMPFILE exit 2 fi rm -f $TMPFILE exit 0 |