"GALETTE_DEBUG",
"GALETTE_NIGHTLY",
"GALETTE_PHOTOS_PATH",
+ "GALETTE_DOCUMENTS_PATH",
"GALETTE_PLUGINS_PATH",
"GALETTE_ROOT",
"GALETTE_TELEMETRY_URI",
!/galette/data/files/readme.txt
!/galette/data/files/.htaccess
+# /galette/data/documents/
+/galette/data/documents/*
+!/galette/data/documents/readme.txt
+!/galette/data/documents/.htaccess
+
# Test stuff
tests/coverage/
tests/mageekguy.atoum.phar
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use Twig\TwigFunction;
+use Twig\Extra\String\StringExtension;
if ( !class_exists('\Twig\Loader\FilesystemLoader')) {
require_once __DIR__ . '/../galette/vendor/autoload.php';
]
);
+$twig->addExtension(new StringExtension());
+
$twig_functions = [
'__',
'_T',
"slim/twig-view": "^3.3",
"slim/psr7": "^1.6",
"symfony/yaml": "^6.2",
- "guzzlehttp/guzzle": "^7.8"
+ "guzzlehttp/guzzle": "^7.8",
+ "twig/string-extra": "^3.8"
},
"require-dev": {
"squizlabs/php_codesniffer": "^3.7",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "dbbf63121881c53f2c16c57ee40027ed",
+ "content-hash": "49aee4bfa35737b80d4e7c19726e83c8",
"packages": [
{
"name": "akrabat/rka-slim-session-middleware",
],
"time": "2023-01-26T09:26:14+00:00"
},
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "875e90aeea2777b6f135677f618529449334a612"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
+ "reference": "875e90aeea2777b6f135677f618529449334a612",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-26T09:26:14+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-26T09:26:14+00:00"
+ },
{
"name": "symfony/polyfill-mbstring",
"version": "v1.28.0",
],
"time": "2023-01-26T09:26:14+00:00"
},
+ {
+ "name": "symfony/string",
+ "version": "v6.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339",
+ "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/intl": "^6.2",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v6.3.5"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-09-18T10:38:32+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v3.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "06450585bf65e978026bda220cdebca3f867fde7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7",
+ "reference": "06450585bf65e978026bda220cdebca3f867fde7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.4-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-26T14:02:43+00:00"
+ },
{
"name": "symfony/yaml",
"version": "v6.3.7",
},
"funding": [
{
- "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project",
- "type": "custom"
+ "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project",
+ "type": "custom"
+ }
+ ],
+ "time": "2023-09-06T15:09:26+00:00"
+ },
+ {
+ "name": "twig/string-extra",
+ "version": "v3.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/twigphp/string-extra.git",
+ "reference": "b0c9037d96baff79abe368dc092a59b726517548"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/twigphp/string-extra/zipball/b0c9037d96baff79abe368dc092a59b726517548",
+ "reference": "b0c9037d96baff79abe368dc092a59b726517548",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/string": "^5.4|^6.0|^7.0",
+ "symfony/translation-contracts": "^1.1|^2|^3",
+ "twig/twig": "^3.0"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Twig\\Extra\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com",
+ "homepage": "http://fabien.potencier.org",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "A Twig extension for Symfony String",
+ "homepage": "https://twig.symfony.com",
+ "keywords": [
+ "html",
+ "string",
+ "twig",
+ "unicode"
+ ],
+ "support": {
+ "source": "https://github.com/twigphp/string-extra/tree/v3.8.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/twig/twig",
+ "type": "tidelift"
}
],
- "time": "2023-09-06T15:09:26+00:00"
+ "time": "2023-11-21T14:02:01+00:00"
},
{
"name": "twig/twig",
],
"time": "2023-09-26T12:56:25+00:00"
},
- {
- "name": "symfony/polyfill-intl-grapheme",
- "version": "v1.28.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "875e90aeea2777b6f135677f618529449334a612"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
- "reference": "875e90aeea2777b6f135677f618529449334a612",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "suggest": {
- "ext-intl": "For best performance"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for intl's grapheme_* functions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "grapheme",
- "intl",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2023-01-26T09:26:14+00:00"
- },
- {
- "name": "symfony/polyfill-intl-normalizer",
- "version": "v1.28.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
- "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "suggest": {
- "ext-intl": "For best performance"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
- },
- "classmap": [
- "Resources/stubs"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for intl's Normalizer class and related functions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "intl",
- "normalizer",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2023-01-26T09:26:14+00:00"
- },
{
"name": "symfony/service-contracts",
"version": "v2.5.2",
],
"time": "2022-05-30T19:17:29+00:00"
},
- {
- "name": "symfony/string",
- "version": "v6.3.5",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/string.git",
- "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339",
- "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1",
- "symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-intl-grapheme": "~1.0",
- "symfony/polyfill-intl-normalizer": "~1.0",
- "symfony/polyfill-mbstring": "~1.0"
- },
- "conflict": {
- "symfony/translation-contracts": "<2.5"
- },
- "require-dev": {
- "symfony/error-handler": "^5.4|^6.0",
- "symfony/http-client": "^5.4|^6.0",
- "symfony/intl": "^6.2",
- "symfony/translation-contracts": "^2.5|^3.0",
- "symfony/var-exporter": "^5.4|^6.0"
- },
- "type": "library",
- "autoload": {
- "files": [
- "Resources/functions.php"
- ],
- "psr-4": {
- "Symfony\\Component\\String\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
- "homepage": "https://symfony.com",
- "keywords": [
- "grapheme",
- "i18n",
- "string",
- "unicode",
- "utf-8",
- "utf8"
- ],
- "support": {
- "source": "https://github.com/symfony/string/tree/v6.3.5"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2023-09-18T10:38:32+00:00"
- },
{
"name": "theseer/tokenizer",
"version": "1.2.1",
if (!defined('GALETTE_PHOTOS_PATH')) {
define('GALETTE_PHOTOS_PATH', GALETTE_DATA_PATH . 'photos/');
}
+if (!defined('GALETTE_DOCUMENTS_PATH')) {
+ define('GALETTE_DOCUMENTS_PATH', GALETTE_DATA_PATH . 'documents/');
+}
if (!defined('GALETTE_ATTACHMENTS_PATH')) {
define('GALETTE_ATTACHMENTS_PATH', GALETTE_DATA_PATH . 'attachments/');
}
--- /dev/null
+
+This file is here because most ftp-clients don't upload empty directory.
+
+You can delete this file after your upload.
+
'pdfModels' => 'staff',
'attendance_sheet_details' => 'groupmanager',
'attendance_sheet' => 'groupmanager',
+ '/(.+)?document(.+)?/i' => 'staff'
];
use Slim\Routing\RouteContext;
use Slim\Routing\RouteParser;
use Slim\Views\Twig;
+use Twig\Extra\String\StringExtension;
$container = $app->getContainer();
//Twig extensions
$view->addExtension(new \Galette\Twig\CsrfExtension($c->get('csrf')));
+ $view->addExtension(new StringExtension());
if (\Galette\Core\Galette::isDebugEnabled()) {
$view->addExtension(new \Twig\Extension\DebugExtension());
}
'/{form_name:adh|contrib|trans}/{id:\d+}/file/{fid:\d+}/{pos:\d+}/{name}',
[Crud\DynamicFieldsController::class, 'getDynamicFile']
)->setName('getDynamicFile')->add($authenticate);
+
+$app->get(
+ '/documents[/{option:page|order}/{value}]',
+ [Crud\DocumentsController::class, 'list']
+)->setName('documentsList')->add($authenticate);
+
+$app->post(
+ '/documents/filter',
+ [Crud\DocumentsController::class, 'filter']
+)->setName('documentsFilter')->add($authenticate);
+
+$app->get(
+ '/document/remove/{id:\d+}',
+ [Crud\DocumentsController::class, 'confirmDelete']
+)->setName('removeDocument')->add($authenticate);
+
+$app->post(
+ '/document/remove/{id:\d+}',
+ [Crud\DocumentsController::class, 'delete']
+)->setName('doRemoveDocument')->add($authenticate);
+
+$app->get(
+ '/document/add',
+ [Crud\DocumentsController::class, 'add']
+)->setName('addDocument')->add($authenticate);
+
+$app->get(
+ '/document/edit/{id:\d+}',
+ [Crud\DocumentsController::class, 'edit']
+)->setName('editDocument')->add($authenticate);
+
+$app->post(
+ '/document/add',
+ [Crud\DocumentsController::class, 'doAdd']
+)->setName('doAddDocument')->add($authenticate);
+
+$app->post(
+ '/document/edit/{id:\d+}',
+ [Crud\DocumentsController::class, 'doEdit']
+)->setName('doEditDocument')->add($authenticate);
+
+$app->get(
+ '/document/get/{id:\d+}',
+ [Crud\DocumentsController::class, 'getDocument']
+)->setName('getDocumentFile');
->withHeader('Location', $routeparser->urlFor('publicList', $args));
}
);
+
+ $app->get(
+ '/documents[/{option:page|order}/{value:\d+|\w+}]',
+ [Crud\DocumentsController::class, 'publicList']
+ )->setName('documentsPublicList');
})->add(\Galette\Middleware\PublicPages::class);
FOREIGN KEY (id_adh) REFERENCES galette_adherents (id_adh) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
+-- table for documents
+DROP TABLE IF EXISTS galette_documents;
+CREATE TABLE galette_documents (
+ id_document int(10) unsigned NOT NULL auto_increment,
+ type varchar(250) NOT NULL,
+ visible tinyint(1) NOT NULL,
+ filename varchar(255) DEFAULT NULL,
+ comment text,
+ creation_date datetime NOT NULL,
+ PRIMARY KEY (id_document),
+ KEY (type)
+) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
+
-- table for database version
DROP TABLE IF EXISTS galette_database;
CREATE TABLE galette_database (
MINVALUE 1
CACHE 1;
+-- sequence for documents
+DROP SEQUENCE IF EXISTS galette_documents_id_seq;
+CREATE SEQUENCE galette_documents_id_seq
+ START 1
+ INCREMENT 1
+ MAXVALUE 2147483647
+ MINVALUE 1
+ CACHE 1;
+
-- Schema
-- REMINDER: Create order IS important, dependencies first !!
DROP TABLE IF EXISTS galette_paymenttypes CASCADE;
-- add index on table to look for type
CREATE INDEX galette_socials_idx ON galette_socials (type);
+-- table for documents
+DROP TABLE IF EXISTS galette_documents CASCADE;
+CREATE TABLE galette_documents (
+ id_document integer DEFAULT nextval('galette_documents_id_seq'::text) NOT NULL,
+ type character varying(250) NOT NULL,
+ visible integer NOT NULL,
+ filename character varying(255) DEFAULT NULL,
+ comment text,
+ creation_date timestamp NOT NULL,
+ PRIMARY KEY (id_document)
+);
+-- add index on table to look for type
+CREATE INDEX galette_documents_idx ON galette_documents (type);
+
-- table for database version
DROP TABLE IF EXISTS galette_database CASCADE;
CREATE TABLE galette_database (
-- change dynamic fields permissions
ALTER TABLE galette_field_types CHANGE field_perm field_perm INT(10) NOT NULL DEFAULT 1;
+
+-- table for documents
+DROP TABLE IF EXISTS galette_documents;
+CREATE TABLE galette_documents (
+ id_document int(10) unsigned NOT NULL auto_increment,
+ type varchar(250) NOT NULL,
+ visible tinyint(1) NOT NULL,
+ filename varchar(255) DEFAULT NULL,
+ comment text,
+ creation_date datetime NOT NULL,
+ PRIMARY KEY (id_document),
+ KEY (type)
+) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
-- change dynamic fields permissions
ALTER TABLE galette_field_types ALTER COLUMN field_perm SET DEFAULT 1;
+
+-- sequence for documents
+DROP SEQUENCE IF EXISTS galette_documents_id_seq;
+CREATE SEQUENCE galette_documents_id_seq
+ START 1
+ INCREMENT 1
+ MAXVALUE 2147483647
+ MINVALUE 1
+ CACHE 1;
+
+-- table for documents
+DROP TABLE IF EXISTS galette_documents CASCADE;
+CREATE TABLE galette_documents (
+ id_document integer DEFAULT nextval('galette_documents_id_seq'::text) NOT NULL,
+ type character varying(250) NOT NULL,
+ visible integer NOT NULL,
+ filename character varying(255) DEFAULT NULL,
+ comment text,
+ creation_date timestamp NOT NULL,
+ PRIMARY KEY (id_document)
+);
+-- add index on table to look for type
+CREATE INDEX galette_documents_idx ON galette_documents (type);
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-17 19:30+0100\n"
+"POT-Creation-Date: 2024-03-20 18:45+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
msgstr ""
#: ../lib/Galette/Controllers/Crud/GroupsController.php:184
-#: ../lib/Galette/Core/Galette.php:548
+#: ../lib/Galette/Core/Galette.php:570
msgid "Groups"
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/Crud/MailingsController.php:477
-#: ../lib/Galette/Core/Galette.php:564
+#: ../lib/Galette/Core/Galette.php:586
msgid "Mailings"
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/Crud/ContributionsTypesController.php:89
-#: ../lib/Galette/Core/Install.php:1166 ../lib/Galette/Core/Galette.php:380
+#: ../lib/Galette/Core/Install.php:1166 ../lib/Galette/Core/Galette.php:387
msgid "Contributions types"
msgstr ""
#: ../lib/Galette/Controllers/Crud/DynamicFieldsController.php:308
#: ../lib/Galette/Controllers/Crud/MembersController.php:238
#: ../lib/Galette/Controllers/Crud/MembersController.php:935
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:276
#: ../lib/Galette/Controllers/PdfController.php:97
#: ../lib/Galette/Controllers/PdfController.php:264
#: ../lib/Galette/Middleware/Authenticate.php:169
msgstr ""
#: ../lib/Galette/Controllers/Crud/DynamicFieldsController.php:364
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:315
msgid "The file does not exists or cannot be read :("
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/Crud/MembersController.php:353
-#: ../lib/Galette/Core/Galette.php:486
+#: ../lib/Galette/Core/Galette.php:493
msgid "Members list"
msgstr ""
#: ../lib/Galette/Controllers/Crud/MembersController.php:353
-#: ../lib/Galette/Core/Galette.php:494
+#: ../lib/Galette/Core/Galette.php:501
msgid "Trombinoscope"
msgstr ""
msgid "transaction"
msgstr ""
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:64
+msgid "Add document"
+msgstr ""
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:120
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:406
+msgid "An error occurred adding document :("
+msgstr ""
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:134
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:420
+msgid "Document has been successfully stored!"
+msgstr ""
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:195
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:230
+msgid "Documents list"
+msgstr ""
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:350
+#: ../../tempcache/pages/documents_list.html.twig:203
+msgid "Edit document"
+msgstr ""
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:492
+#: ../../tempcache/pages/documents_list.html.twig:216
+msgid "Delete document"
+msgstr ""
+
#: ../lib/Galette/Controllers/Crud/SavedSearchesController.php:102
msgid "This search is already saved."
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/GaletteController.php:222
-#: ../lib/Galette/Core/Install.php:1162 ../lib/Galette/Core/Galette.php:328
-#: ../lib/Galette/Core/Galette.php:606
+#: ../lib/Galette/Core/Install.php:1162 ../lib/Galette/Core/Galette.php:335
+#: ../lib/Galette/Core/Galette.php:647
msgid "Settings"
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/GaletteController.php:664
-#: ../lib/Galette/Core/Galette.php:256 ../lib/Galette/Core/Galette.php:590
+#: ../lib/Galette/Core/Galette.php:256 ../lib/Galette/Core/Galette.php:612
msgid "Reminders"
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/PdfController.php:384
-#: ../lib/Galette/Core/Galette.php:898
+#: ../lib/Galette/Core/Galette.php:938
#: ../lib/Galette/IO/PdfAttendanceSheet.php:120
#: ../../tempcache/modals/members_attendance_sheet.html.twig:104
msgid "Attendance sheet"
msgstr ""
#: ../lib/Galette/Controllers/PdfController.php:525
-#: ../lib/Galette/Core/Install.php:1186 ../lib/Galette/Core/Galette.php:402
+#: ../lib/Galette/Core/Install.php:1186 ../lib/Galette/Core/Galette.php:409
msgid "PDF models"
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/PluginsController.php:61
-#: ../lib/Galette/Core/Galette.php:335 ../lib/Galette/Core/Galette.php:614
+#: ../lib/Galette/Core/Galette.php:342 ../lib/Galette/Core/Galette.php:655
msgid "Plugins"
msgstr ""
msgstr ""
#: ../lib/Galette/Controllers/DynamicTranslationsController.php:54
-#: ../lib/Galette/Core/Galette.php:365
+#: ../lib/Galette/Core/Galette.php:372
#: ../../tempcache/pages/configuration_payment_types.html.twig:293
#: ../../tempcache/pages/configuration_dynamic_fields.html.twig:250
msgid "Translate labels"
msgid "Mails texts"
msgstr ""
-#: ../lib/Galette/Core/Install.php:1182 ../lib/Galette/Core/Galette.php:394
+#: ../lib/Galette/Core/Install.php:1182 ../lib/Galette/Core/Galette.php:401
msgid "Titles"
msgstr ""
msgid "My Account"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:125 ../lib/Galette/Core/Galette.php:639
+#: ../lib/Galette/Core/Galette.php:125 ../lib/Galette/Core/Galette.php:680
msgid "My contributions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:126 ../lib/Galette/Core/Galette.php:640
+#: ../lib/Galette/Core/Galette.php:126 ../lib/Galette/Core/Galette.php:681
msgid "View and filter all my contributions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:133 ../lib/Galette/Core/Galette.php:648
+#: ../lib/Galette/Core/Galette.php:133 ../lib/Galette/Core/Galette.php:689
msgid "My transactions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:134 ../lib/Galette/Core/Galette.php:649
+#: ../lib/Galette/Core/Galette.php:134 ../lib/Galette/Core/Galette.php:690
msgid "View and filter all my transactions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:141 ../lib/Galette/Core/Galette.php:631
+#: ../lib/Galette/Core/Galette.php:141 ../lib/Galette/Core/Galette.php:672
msgid "My information"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:142 ../lib/Galette/Core/Galette.php:632
+#: ../lib/Galette/Core/Galette.php:142 ../lib/Galette/Core/Galette.php:673
msgid "View my member card"
msgstr ""
msgid "Add new child member in database"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:164 ../lib/Galette/Core/Galette.php:540
+#: ../lib/Galette/Core/Galette.php:164 ../lib/Galette/Core/Galette.php:562
#: ../lib/Galette/DynamicFields/DynamicField.php:533
#: ../../tempcache/elements/group.html.twig:95
msgid "Members"
msgid "List of members"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:173 ../lib/Galette/Core/Galette.php:541
+#: ../lib/Galette/Core/Galette.php:173 ../lib/Galette/Core/Galette.php:563
msgid "View, search into and filter member's list"
msgstr ""
msgid "Add new member in database"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:211 ../lib/Galette/Core/Galette.php:572
+#: ../lib/Galette/Core/Galette.php:211 ../lib/Galette/Core/Galette.php:594
#: ../lib/Galette/DynamicFields/DynamicField.php:534
#: ../../tempcache/pages/members_list.html.twig:561
msgid "Contributions"
msgid "List of contributions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:216 ../lib/Galette/Core/Galette.php:573
+#: ../lib/Galette/Core/Galette.php:216 ../lib/Galette/Core/Galette.php:595
msgid "View and filter contributions"
msgstr ""
msgid "List of transactions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:225 ../lib/Galette/Core/Galette.php:582
+#: ../lib/Galette/Core/Galette.php:225 ../lib/Galette/Core/Galette.php:604
msgid "View and filter transactions"
msgstr ""
msgid "Add new transaction in database"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:257 ../lib/Galette/Core/Galette.php:591
+#: ../lib/Galette/Core/Galette.php:257 ../lib/Galette/Core/Galette.php:613
msgid "Send reminders to late members"
msgstr ""
msgid "Manage groups"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:273 ../lib/Galette/Core/Galette.php:549
+#: ../lib/Galette/Core/Galette.php:273 ../lib/Galette/Core/Galette.php:571
msgid "View and manage groups"
msgstr ""
msgid "Manage mailings"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:292 ../lib/Galette/Core/Galette.php:565
+#: ../lib/Galette/Core/Galette.php:292 ../lib/Galette/Core/Galette.php:587
msgid "Manage mailings that has been sent"
msgstr ""
msgid "Various charts"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:324 ../install/steps/check.php:53
+#: ../lib/Galette/Core/Galette.php:319 ../lib/Galette/Core/Galette.php:516
+#: ../lib/Galette/Core/Galette.php:631
+msgid "Documents"
+msgstr ""
+
+#: ../lib/Galette/Core/Galette.php:320
+msgid ""
+"Add documents to share related to your association (status, rules of "
+"procedure, ...)"
+msgstr ""
+
+#: ../lib/Galette/Core/Galette.php:331 ../install/steps/check.php:53
msgid "Configuration"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:329 ../lib/Galette/Core/Galette.php:607
+#: ../lib/Galette/Core/Galette.php:336 ../lib/Galette/Core/Galette.php:648
msgid ""
"Set applications preferences (address, website, member's cards "
"configuration, ...)"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:336 ../lib/Galette/Core/Galette.php:615
+#: ../lib/Galette/Core/Galette.php:343 ../lib/Galette/Core/Galette.php:656
msgid "Information about available plugins"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:342
+#: ../lib/Galette/Core/Galette.php:349
msgid "Core lists"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:343
+#: ../lib/Galette/Core/Galette.php:350
msgid "Customize lists fields and order"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:350
+#: ../lib/Galette/Core/Galette.php:357
msgid "Core fields"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:351
+#: ../lib/Galette/Core/Galette.php:358
msgid ""
"Customize fields order, set which are required, and for who they're visibles"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:357
+#: ../lib/Galette/Core/Galette.php:364
msgid "Dynamic fields"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:358
+#: ../lib/Galette/Core/Galette.php:365
msgid "Manage additional fields for various forms"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:366
+#: ../lib/Galette/Core/Galette.php:373
msgid "Translate additional fields labels"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:372
+#: ../lib/Galette/Core/Galette.php:379
msgid "Manage statuses"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:381
+#: ../lib/Galette/Core/Galette.php:388
msgid "Manage contributions types"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:387
+#: ../lib/Galette/Core/Galette.php:394
msgid "Emails content"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:388
+#: ../lib/Galette/Core/Galette.php:395
msgid "Manage emails texts and subjects"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:395
+#: ../lib/Galette/Core/Galette.php:402
msgid "Manage titles"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:403
+#: ../lib/Galette/Core/Galette.php:410
msgid "Manage PDF models"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:409
+#: ../lib/Galette/Core/Galette.php:416
msgid "Payment types"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:410
+#: ../lib/Galette/Core/Galette.php:417
msgid "Manage payment types"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:417
+#: ../lib/Galette/Core/Galette.php:424
msgid "Empty adhesion form"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:418
+#: ../lib/Galette/Core/Galette.php:425
msgid "Download empty adhesion form"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:428
+#: ../lib/Galette/Core/Galette.php:435
msgid "Admin tools"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:429
+#: ../lib/Galette/Core/Galette.php:436
msgid "Various administrative tools"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:482
+#: ../lib/Galette/Core/Galette.php:489
msgid "Public pages"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:581
+#: ../lib/Galette/Core/Galette.php:517 ../lib/Galette/Core/Galette.php:632
+msgid "View documents related to your association"
+msgstr ""
+
+#: ../lib/Galette/Core/Galette.php:603
#: ../lib/Galette/DynamicFields/DynamicField.php:535
msgid "Transactions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:699 ../lib/Galette/Core/Galette.php:704
+#: ../lib/Galette/Core/Galette.php:739 ../lib/Galette/Core/Galette.php:744
msgid "%membername: edit information"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:720 ../lib/Galette/Core/Galette.php:725
+#: ../lib/Galette/Core/Galette.php:760 ../lib/Galette/Core/Galette.php:765
msgid "%membername: contributions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:741 ../lib/Galette/Core/Galette.php:746
+#: ../lib/Galette/Core/Galette.php:781 ../lib/Galette/Core/Galette.php:786
msgid "%membername: remove from database"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:765 ../lib/Galette/Core/Galette.php:770
+#: ../lib/Galette/Core/Galette.php:805 ../lib/Galette/Core/Galette.php:810
#: ../../tempcache/elements/group_persons.html.twig:182
msgid "Log in in as %membername"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:856
+#: ../lib/Galette/Core/Galette.php:896
msgid "Mass change"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:861
+#: ../lib/Galette/Core/Galette.php:901
msgid "Mass add contributions"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:866
+#: ../lib/Galette/Core/Galette.php:906
#: ../../tempcache/elements/group.html.twig:374
#: ../../tempcache/pages/contributions_list.html.twig:56
msgid "Delete"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:882
+#: ../lib/Galette/Core/Galette.php:922
#: ../../tempcache/pages/members_list.html.twig:740
msgid "Mail"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:903
+#: ../lib/Galette/Core/Galette.php:943
#: ../../tempcache/elements/mailing_recipients.html.twig:106
msgid "Generate labels"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:908
+#: ../lib/Galette/Core/Galette.php:948
msgid "Generate Member Cards"
msgstr ""
-#: ../lib/Galette/Core/Galette.php:913
+#: ../lib/Galette/Core/Galette.php:953
#: ../../tempcache/pages/contributions_list.html.twig:56
msgid "Export as CSV"
msgstr ""
#: ../lib/Galette/Core/Preferences.php:1136
#: ../lib/Galette/Core/Preferences.php:1152 ../lib/Galette/IO/Csv.php:97
-#: ../lib/Galette/Entity/Texts.php:193
+#: ../lib/Galette/Entity/Document.php:448 ../lib/Galette/Entity/Texts.php:193
#: ../../tempcache/pages/history.html.twig:323
#: ../../tempcache/pages/mailings_list.html.twig:296
msgid "Y-m-d H:i:s"
msgid "invoice"
msgstr ""
-#: ../lib/Galette/Features/Permissions.php:59
+#: ../lib/Galette/Features/Permissions.php:60
msgid "Inaccessible"
msgstr ""
-#: ../lib/Galette/Features/Permissions.php:60
+#: ../lib/Galette/Features/Permissions.php:64
+#: ../lib/Galette/Entity/ListsConfig.php:202
+msgid "Public"
+msgstr ""
+
+#: ../lib/Galette/Features/Permissions.php:68
msgid "User, read only"
msgstr ""
-#: ../lib/Galette/Features/Permissions.php:61
+#: ../lib/Galette/Features/Permissions.php:69
msgid "User, read/write"
msgstr ""
-#: ../lib/Galette/Features/Permissions.php:62
+#: ../lib/Galette/Features/Permissions.php:70
#: ../../tempcache/elements/ajax_members.html.twig:152
#: ../../tempcache/elements/group_persons.html.twig:148
#: ../../tempcache/pages/members_list.html.twig:626
msgid "Group manager"
msgstr ""
-#: ../lib/Galette/Features/Permissions.php:63
+#: ../lib/Galette/Features/Permissions.php:71
#: ../../tempcache/elements/ajax_members.html.twig:141
#: ../../tempcache/elements/group_persons.html.twig:137
#: ../../tempcache/pages/members_list.html.twig:619
msgid "Staff member"
msgstr ""
-#: ../lib/Galette/Features/Permissions.php:64
+#: ../lib/Galette/Features/Permissions.php:72
#: ../../tempcache/elements/ajax_members.html.twig:130
#: ../../tempcache/elements/group_persons.html.twig:126
msgid "Administrator"
msgstr ""
-#: ../lib/Galette/Features/Permissions.php:68
-#: ../lib/Galette/Entity/ListsConfig.php:202
-msgid "Public"
-msgstr ""
-
#: ../lib/Galette/Features/I18n.php:115
#, php-format
msgid "Unable to add dynamic translation for %field :("
msgstr ""
#: ../lib/Galette/Features/Replacements.php:402
+#: ../../tempcache/pages/document_form.html.twig:176
msgid "Comment"
msgstr ""
msgid "Membership"
msgstr ""
+#: ../lib/Galette/Entity/Document.php:464
+msgid "Association status"
+msgstr ""
+
+#: ../lib/Galette/Entity/Document.php:465
+msgid "Rules of procedure"
+msgstr ""
+
+#: ../lib/Galette/Entity/Document.php:466
+#: ../../tempcache/elements/edit_pdf_models.html.twig:189
+#: ../../tempcache/pages/member_show.html.twig:152
+msgid "Adhesion form"
+msgstr ""
+
+#: ../lib/Galette/Entity/Document.php:467
+msgid "Meeting minutes"
+msgstr ""
+
+#: ../lib/Galette/Entity/Document.php:468
+msgid "Votes results"
+msgstr ""
+
#: ../lib/Galette/Entity/Title.php:169
msgid "You cannot delete Mr. or Mrs. titles!"
msgstr ""
msgstr ""
#. TRANS: see https://fomantic-ui.com/modules/calendar.html#custom-format - must be the same as Y-m-d for PHP https://www.php.net/manual/datetime.format.php
-#: ../includes/dependencies.php:172
+#: ../includes/dependencies.php:174
msgid "YYYY-MM-DD"
msgstr ""
-#: ../includes/dependencies.php:443
+#: ../includes/dependencies.php:445
msgid "Failed CSRF check!"
msgstr ""
#: ../../tempcache/pages/configuration_title_form.html.twig:94
#: ../../tempcache/pages/preferences.html.twig:2662
#: ../../tempcache/pages/configuration_payment_type_form.html.twig:94
+#: ../../tempcache/pages/document_form.html.twig:201
msgid "Cancel"
msgstr ""
#: ../../tempcache/pages/configuration_core_lists.html.twig:74
#: ../../tempcache/pages/configuration_core_lists.html.twig:186
#: ../../tempcache/pages/configuration_core_lists.html.twig:305
+#: ../../tempcache/pages/document_form.html.twig:147
msgid "Permissions"
msgstr ""
#: ../../tempcache/pages/transaction_form.html.twig:285
#: ../../tempcache/pages/contributions_list.html.twig:408
#: ../../tempcache/pages/contributions_list.html.twig:662
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:154
#: ../../tempcache/pages/advanced_search.html.twig:819
#: ../../tempcache/pages/advanced_search.html.twig:824
msgid "Type"
#: ../../tempcache/pages/member_form.html.twig:468
#: ../../tempcache/pages/configuration_payment_type_form.html.twig:89
#: ../../tempcache/pages/configuration_dynamic_translations.html.twig:185
+#: ../../tempcache/pages/document_form.html.twig:196
msgid "Save"
msgstr ""
msgid "Receipt"
msgstr ""
-#: ../../tempcache/elements/edit_pdf_models.html.twig:189
-#: ../../tempcache/pages/member_show.html.twig:152
-msgid "Adhesion form"
-msgstr ""
-
#: ../../tempcache/elements/display_dynamic_fields.html.twig:45
#: ../../tempcache/components/dynamic_fields.html.twig:60
msgid "Additionnal fields:"
msgstr ""
#: ../../tempcache/elements/edit_socials.html.twig:151
+#: ../../tempcache/pages/document_form.html.twig:121
msgid "Choose or enter your own..."
msgstr ""
msgstr ""
#: ../../tempcache/pages/configuration_dynamic_field_form.html.twig:423
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:160
msgid "Visibility"
msgstr ""
+#: ../../tempcache/pages/documents_public_list.html.twig:86
+#: ../../tempcache/pages/documents_list.html.twig:57
+#, php-format
+msgid "%count document"
+msgid_plural "%count documents"
+msgstr[0] ""
+msgstr[1] ""
+
#: ../../tempcache/pages/transaction_form.html.twig:63
msgid "Transaction details"
msgstr ""
msgid "Click on a row to select a member"
msgstr ""
+#: ../../tempcache/pages/documents_list.html.twig:84
+msgid "Add a document"
+msgstr ""
+
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:166
+msgid "Filename"
+msgstr ""
+
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:180
+#: ../../tempcache/pages/saved_searches_list.html.twig:69
+#: ../../tempcache/pages/saved_searches_list.html.twig:128
+#: ../../tempcache/pages/advanced_search.html.twig:308
+#: ../../tempcache/pages/advanced_search.html.twig:661
+msgid "Creation date"
+msgstr ""
+
+#: ../../tempcache/pages/documents_list.html.twig:247
+msgid "No document"
+msgstr ""
+
#: ../../tempcache/pages/index.html.twig:122
msgid "Lost your password?"
msgstr ""
#: ../../tempcache/pages/import.html.twig:259
#: ../../tempcache/pages/preferences.html.twig:245
#: ../../tempcache/pages/preferences.html.twig:2017
+#: ../../tempcache/pages/document_form.html.twig:101
#: ../../tempcache/components/forms/picture.html.twig:75
#: ../../tempcache/components/dynamic_fields.html.twig:758
msgid "Choose a file"
#: ../../tempcache/pages/preferences.html.twig:243
#: ../../tempcache/pages/preferences.html.twig:2015
+#: ../../tempcache/pages/document_form.html.twig:99
#: ../../tempcache/components/forms/picture.html.twig:73
#: ../../tempcache/components/dynamic_fields.html.twig:756
msgid "Choose another file"
msgstr[0] ""
msgstr[1] ""
-#: ../../tempcache/pages/saved_searches_list.html.twig:69
-#: ../../tempcache/pages/saved_searches_list.html.twig:128
-#: ../../tempcache/pages/advanced_search.html.twig:308
-#: ../../tempcache/pages/advanced_search.html.twig:661
-msgid "Creation date"
-msgstr ""
-
#: ../../tempcache/pages/saved_searches_list.html.twig:140
msgid "Search parameters"
msgstr ""
msgstr ""
#: ../../tempcache/pages/500.html.twig:121
+#: ../../tempcache/pages/document_form.html.twig:68
msgid "File:"
msgstr ""
msgid "Trace"
msgstr ""
+#: ../../tempcache/pages/document_form.html.twig:111
+msgid "Document type"
+msgstr ""
+
+#: ../../tempcache/pages/document_form.html.twig:184
+msgid "Extra information displayed along with document."
+msgstr ""
+
#: ../../tempcache/pages/export.html.twig:56
msgid ""
"Each selected export will be stored into a separate file in the exports "
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-17 19:30+0100\n"
-"PO-Revision-Date: 2024-03-17 19:30+0100\n"
+"POT-Creation-Date: 2024-03-20 18:45+0100\n"
+"PO-Revision-Date: 2024-03-20 18:45+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: en_US\n"
msgstr "Group name is missing!"
#: ../lib/Galette/Controllers/Crud/GroupsController.php:184
-#: ../lib/Galette/Core/Galette.php:548
+#: ../lib/Galette/Core/Galette.php:570
msgid "Groups"
msgstr "Groups"
msgstr "Mailing has been successfully saved."
#: ../lib/Galette/Controllers/Crud/MailingsController.php:477
-#: ../lib/Galette/Core/Galette.php:564
+#: ../lib/Galette/Core/Galette.php:586
msgid "Mailings"
msgstr "Mailings"
msgstr "Mailing preview"
#: ../lib/Galette/Controllers/Crud/ContributionsTypesController.php:89
-#: ../lib/Galette/Core/Install.php:1166 ../lib/Galette/Core/Galette.php:380
+#: ../lib/Galette/Core/Install.php:1166 ../lib/Galette/Core/Galette.php:387
msgid "Contributions types"
msgstr "Contributions types"
#: ../lib/Galette/Controllers/Crud/DynamicFieldsController.php:308
#: ../lib/Galette/Controllers/Crud/MembersController.php:238
#: ../lib/Galette/Controllers/Crud/MembersController.php:935
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:276
#: ../lib/Galette/Controllers/PdfController.php:97
#: ../lib/Galette/Controllers/PdfController.php:264
#: ../lib/Galette/Middleware/Authenticate.php:169
msgstr "You do not have permission for requested URL."
#: ../lib/Galette/Controllers/Crud/DynamicFieldsController.php:364
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:315
msgid "The file does not exists or cannot be read :("
msgstr "The file does not exists or cannot be read :("
msgstr "Member Profile"
#: ../lib/Galette/Controllers/Crud/MembersController.php:353
-#: ../lib/Galette/Core/Galette.php:486
+#: ../lib/Galette/Core/Galette.php:493
msgid "Members list"
msgstr "Members list"
#: ../lib/Galette/Controllers/Crud/MembersController.php:353
-#: ../lib/Galette/Core/Galette.php:494
+#: ../lib/Galette/Core/Galette.php:501
msgid "Trombinoscope"
msgstr "Trombinoscope"
msgid "transaction"
msgstr "transaction"
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:64
+msgid "Add document"
+msgstr "Add document"
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:120
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:406
+msgid "An error occurred adding document :("
+msgstr "An error occurred adding document :("
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:134
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:420
+msgid "Document has been successfully stored!"
+msgstr "Document has been successfully stored!"
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:195
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:230
+msgid "Documents list"
+msgstr "Documents list"
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:350
+#: ../../tempcache/pages/documents_list.html.twig:203
+msgid "Edit document"
+msgstr "Edit document"
+
+#: ../lib/Galette/Controllers/Crud/DocumentsController.php:492
+#: ../../tempcache/pages/documents_list.html.twig:216
+msgid "Delete document"
+msgstr "Delete document"
+
#: ../lib/Galette/Controllers/Crud/SavedSearchesController.php:102
msgid "This search is already saved."
msgstr "This search is already saved."
msgstr "Dashboard"
#: ../lib/Galette/Controllers/GaletteController.php:222
-#: ../lib/Galette/Core/Install.php:1162 ../lib/Galette/Core/Galette.php:328
-#: ../lib/Galette/Core/Galette.php:606
+#: ../lib/Galette/Core/Install.php:1162 ../lib/Galette/Core/Galette.php:335
+#: ../lib/Galette/Core/Galette.php:647
msgid "Settings"
msgstr "Settings"
msgstr "An error occurred while storing list configuration :("
#: ../lib/Galette/Controllers/GaletteController.php:664
-#: ../lib/Galette/Core/Galette.php:256 ../lib/Galette/Core/Galette.php:590
+#: ../lib/Galette/Core/Galette.php:256 ../lib/Galette/Core/Galette.php:612
msgid "Reminders"
msgstr "Reminders"
msgstr "No member selected to generate attendance sheet"
#: ../lib/Galette/Controllers/PdfController.php:384
-#: ../lib/Galette/Core/Galette.php:898
+#: ../lib/Galette/Core/Galette.php:938
#: ../lib/Galette/IO/PdfAttendanceSheet.php:120
#: ../../tempcache/modals/members_attendance_sheet.html.twig:104
msgid "Attendance sheet"
msgstr "Unable to get groups list."
#: ../lib/Galette/Controllers/PdfController.php:525
-#: ../lib/Galette/Core/Install.php:1186 ../lib/Galette/Core/Galette.php:402
+#: ../lib/Galette/Core/Install.php:1186 ../lib/Galette/Core/Galette.php:409
msgid "PDF models"
msgstr "PDF models"
msgstr "Flush the logs"
#: ../lib/Galette/Controllers/PluginsController.php:61
-#: ../lib/Galette/Core/Galette.php:335 ../lib/Galette/Core/Galette.php:614
+#: ../lib/Galette/Core/Galette.php:342 ../lib/Galette/Core/Galette.php:655
msgid "Plugins"
msgstr "Plugins"
msgstr "Plugin %name has been disabled"
#: ../lib/Galette/Controllers/DynamicTranslationsController.php:54
-#: ../lib/Galette/Core/Galette.php:365
+#: ../lib/Galette/Core/Galette.php:372
#: ../../tempcache/pages/configuration_payment_types.html.twig:293
#: ../../tempcache/pages/configuration_dynamic_fields.html.twig:250
msgid "Translate labels"
msgid "Mails texts"
msgstr "Mails texts"
-#: ../lib/Galette/Core/Install.php:1182 ../lib/Galette/Core/Galette.php:394
+#: ../lib/Galette/Core/Install.php:1182 ../lib/Galette/Core/Galette.php:401
msgid "Titles"
msgstr "Titles"
msgid "My Account"
msgstr "My Account"
-#: ../lib/Galette/Core/Galette.php:125 ../lib/Galette/Core/Galette.php:639
+#: ../lib/Galette/Core/Galette.php:125 ../lib/Galette/Core/Galette.php:680
msgid "My contributions"
msgstr "My contributions"
-#: ../lib/Galette/Core/Galette.php:126 ../lib/Galette/Core/Galette.php:640
+#: ../lib/Galette/Core/Galette.php:126 ../lib/Galette/Core/Galette.php:681
msgid "View and filter all my contributions"
msgstr "View and filter all my contributions"
-#: ../lib/Galette/Core/Galette.php:133 ../lib/Galette/Core/Galette.php:648
+#: ../lib/Galette/Core/Galette.php:133 ../lib/Galette/Core/Galette.php:689
msgid "My transactions"
msgstr "My transactions"
-#: ../lib/Galette/Core/Galette.php:134 ../lib/Galette/Core/Galette.php:649
+#: ../lib/Galette/Core/Galette.php:134 ../lib/Galette/Core/Galette.php:690
msgid "View and filter all my transactions"
msgstr "View and filter all my transactions"
-#: ../lib/Galette/Core/Galette.php:141 ../lib/Galette/Core/Galette.php:631
+#: ../lib/Galette/Core/Galette.php:141 ../lib/Galette/Core/Galette.php:672
msgid "My information"
msgstr "My information"
-#: ../lib/Galette/Core/Galette.php:142 ../lib/Galette/Core/Galette.php:632
+#: ../lib/Galette/Core/Galette.php:142 ../lib/Galette/Core/Galette.php:673
msgid "View my member card"
msgstr "View my member card"
msgid "Add new child member in database"
msgstr "Add new child member in database"
-#: ../lib/Galette/Core/Galette.php:164 ../lib/Galette/Core/Galette.php:540
+#: ../lib/Galette/Core/Galette.php:164 ../lib/Galette/Core/Galette.php:562
#: ../lib/Galette/DynamicFields/DynamicField.php:533
#: ../../tempcache/elements/group.html.twig:95
msgid "Members"
msgid "List of members"
msgstr "List of members"
-#: ../lib/Galette/Core/Galette.php:173 ../lib/Galette/Core/Galette.php:541
+#: ../lib/Galette/Core/Galette.php:173 ../lib/Galette/Core/Galette.php:563
msgid "View, search into and filter member's list"
msgstr "View, search into and filter member's list"
msgid "Add new member in database"
msgstr "Add new member in database"
-#: ../lib/Galette/Core/Galette.php:211 ../lib/Galette/Core/Galette.php:572
+#: ../lib/Galette/Core/Galette.php:211 ../lib/Galette/Core/Galette.php:594
#: ../lib/Galette/DynamicFields/DynamicField.php:534
#: ../../tempcache/pages/members_list.html.twig:561
msgid "Contributions"
msgid "List of contributions"
msgstr "List of contributions"
-#: ../lib/Galette/Core/Galette.php:216 ../lib/Galette/Core/Galette.php:573
+#: ../lib/Galette/Core/Galette.php:216 ../lib/Galette/Core/Galette.php:595
msgid "View and filter contributions"
msgstr "View and filter contributions"
msgid "List of transactions"
msgstr "List of transactions"
-#: ../lib/Galette/Core/Galette.php:225 ../lib/Galette/Core/Galette.php:582
+#: ../lib/Galette/Core/Galette.php:225 ../lib/Galette/Core/Galette.php:604
msgid "View and filter transactions"
msgstr "View and filter transactions"
msgid "Add new transaction in database"
msgstr "Add new transaction in database"
-#: ../lib/Galette/Core/Galette.php:257 ../lib/Galette/Core/Galette.php:591
+#: ../lib/Galette/Core/Galette.php:257 ../lib/Galette/Core/Galette.php:613
msgid "Send reminders to late members"
msgstr "Send reminders to late members"
msgid "Manage groups"
msgstr "Manage groups"
-#: ../lib/Galette/Core/Galette.php:273 ../lib/Galette/Core/Galette.php:549
+#: ../lib/Galette/Core/Galette.php:273 ../lib/Galette/Core/Galette.php:571
msgid "View and manage groups"
msgstr "View and manage groups"
msgid "Manage mailings"
msgstr "Manage mailings"
-#: ../lib/Galette/Core/Galette.php:292 ../lib/Galette/Core/Galette.php:565
+#: ../lib/Galette/Core/Galette.php:292 ../lib/Galette/Core/Galette.php:587
msgid "Manage mailings that has been sent"
msgstr "Manage mailings that has been sent"
msgid "Various charts"
msgstr "Various charts"
-#: ../lib/Galette/Core/Galette.php:324 ../install/steps/check.php:53
+#: ../lib/Galette/Core/Galette.php:319 ../lib/Galette/Core/Galette.php:516
+#: ../lib/Galette/Core/Galette.php:631
+msgid "Documents"
+msgstr "Documents"
+
+#: ../lib/Galette/Core/Galette.php:320
+msgid ""
+"Add documents to share related to your association (status, rules of "
+"procedure, ...)"
+msgstr ""
+"Add documents to share related to your association (status, rules of "
+"procedure, ...)"
+
+#: ../lib/Galette/Core/Galette.php:331 ../install/steps/check.php:53
msgid "Configuration"
msgstr "Configuration"
-#: ../lib/Galette/Core/Galette.php:329 ../lib/Galette/Core/Galette.php:607
+#: ../lib/Galette/Core/Galette.php:336 ../lib/Galette/Core/Galette.php:648
msgid ""
"Set applications preferences (address, website, member's cards "
"configuration, ...)"
"Set applications preferences (address, website, member's cards "
"configuration, ...)"
-#: ../lib/Galette/Core/Galette.php:336 ../lib/Galette/Core/Galette.php:615
+#: ../lib/Galette/Core/Galette.php:343 ../lib/Galette/Core/Galette.php:656
msgid "Information about available plugins"
msgstr "Information about available plugins"
-#: ../lib/Galette/Core/Galette.php:342
+#: ../lib/Galette/Core/Galette.php:349
msgid "Core lists"
msgstr "Core lists"
-#: ../lib/Galette/Core/Galette.php:343
+#: ../lib/Galette/Core/Galette.php:350
msgid "Customize lists fields and order"
msgstr "Customize lists fields and order"
-#: ../lib/Galette/Core/Galette.php:350
+#: ../lib/Galette/Core/Galette.php:357
msgid "Core fields"
msgstr "Core fields"
-#: ../lib/Galette/Core/Galette.php:351
+#: ../lib/Galette/Core/Galette.php:358
msgid ""
"Customize fields order, set which are required, and for who they're visibles"
msgstr ""
"Customize fields order, set which are required, and for who they're visibles"
-#: ../lib/Galette/Core/Galette.php:357
+#: ../lib/Galette/Core/Galette.php:364
msgid "Dynamic fields"
msgstr "Dynamic fields"
-#: ../lib/Galette/Core/Galette.php:358
+#: ../lib/Galette/Core/Galette.php:365
msgid "Manage additional fields for various forms"
msgstr "Manage additional fields for various forms"
-#: ../lib/Galette/Core/Galette.php:366
+#: ../lib/Galette/Core/Galette.php:373
msgid "Translate additional fields labels"
msgstr "Translate additional fields labels"
-#: ../lib/Galette/Core/Galette.php:372
+#: ../lib/Galette/Core/Galette.php:379
msgid "Manage statuses"
msgstr "Manage statuses"
-#: ../lib/Galette/Core/Galette.php:381
+#: ../lib/Galette/Core/Galette.php:388
msgid "Manage contributions types"
msgstr "Manage contributions types"
-#: ../lib/Galette/Core/Galette.php:387
+#: ../lib/Galette/Core/Galette.php:394
msgid "Emails content"
msgstr "Emails content"
-#: ../lib/Galette/Core/Galette.php:388
+#: ../lib/Galette/Core/Galette.php:395
msgid "Manage emails texts and subjects"
msgstr "Manage emails texts and subjects"
-#: ../lib/Galette/Core/Galette.php:395
+#: ../lib/Galette/Core/Galette.php:402
msgid "Manage titles"
msgstr "Manage titles"
-#: ../lib/Galette/Core/Galette.php:403
+#: ../lib/Galette/Core/Galette.php:410
msgid "Manage PDF models"
msgstr "Manage PDF models"
-#: ../lib/Galette/Core/Galette.php:409
+#: ../lib/Galette/Core/Galette.php:416
msgid "Payment types"
msgstr "Payment types"
-#: ../lib/Galette/Core/Galette.php:410
+#: ../lib/Galette/Core/Galette.php:417
msgid "Manage payment types"
msgstr "Manage payment types"
-#: ../lib/Galette/Core/Galette.php:417
+#: ../lib/Galette/Core/Galette.php:424
msgid "Empty adhesion form"
msgstr "Empty adhesion form"
-#: ../lib/Galette/Core/Galette.php:418
+#: ../lib/Galette/Core/Galette.php:425
msgid "Download empty adhesion form"
msgstr "Download empty adhesion form"
-#: ../lib/Galette/Core/Galette.php:428
+#: ../lib/Galette/Core/Galette.php:435
msgid "Admin tools"
msgstr "Admin tools"
-#: ../lib/Galette/Core/Galette.php:429
+#: ../lib/Galette/Core/Galette.php:436
msgid "Various administrative tools"
msgstr "Various administrative tools"
-#: ../lib/Galette/Core/Galette.php:482
+#: ../lib/Galette/Core/Galette.php:489
msgid "Public pages"
msgstr "Public pages"
-#: ../lib/Galette/Core/Galette.php:581
+#: ../lib/Galette/Core/Galette.php:517 ../lib/Galette/Core/Galette.php:632
+msgid "View documents related to your association"
+msgstr "View documents related to your association"
+
+#: ../lib/Galette/Core/Galette.php:603
#: ../lib/Galette/DynamicFields/DynamicField.php:535
msgid "Transactions"
msgstr "Transactions"
-#: ../lib/Galette/Core/Galette.php:699 ../lib/Galette/Core/Galette.php:704
+#: ../lib/Galette/Core/Galette.php:739 ../lib/Galette/Core/Galette.php:744
msgid "%membername: edit information"
msgstr "%membername: edit information"
-#: ../lib/Galette/Core/Galette.php:720 ../lib/Galette/Core/Galette.php:725
+#: ../lib/Galette/Core/Galette.php:760 ../lib/Galette/Core/Galette.php:765
msgid "%membername: contributions"
msgstr "%membername: contributions"
-#: ../lib/Galette/Core/Galette.php:741 ../lib/Galette/Core/Galette.php:746
+#: ../lib/Galette/Core/Galette.php:781 ../lib/Galette/Core/Galette.php:786
msgid "%membername: remove from database"
msgstr "%membername: remove from database"
-#: ../lib/Galette/Core/Galette.php:765 ../lib/Galette/Core/Galette.php:770
+#: ../lib/Galette/Core/Galette.php:805 ../lib/Galette/Core/Galette.php:810
#: ../../tempcache/elements/group_persons.html.twig:182
msgid "Log in in as %membername"
msgstr "Log in in as %membername"
-#: ../lib/Galette/Core/Galette.php:856
+#: ../lib/Galette/Core/Galette.php:896
msgid "Mass change"
msgstr "Mass change"
-#: ../lib/Galette/Core/Galette.php:861
+#: ../lib/Galette/Core/Galette.php:901
msgid "Mass add contributions"
msgstr "Mass add contributions"
-#: ../lib/Galette/Core/Galette.php:866
+#: ../lib/Galette/Core/Galette.php:906
#: ../../tempcache/elements/group.html.twig:374
#: ../../tempcache/pages/contributions_list.html.twig:56
msgid "Delete"
msgstr "Delete"
-#: ../lib/Galette/Core/Galette.php:882
+#: ../lib/Galette/Core/Galette.php:922
#: ../../tempcache/pages/members_list.html.twig:740
msgid "Mail"
msgstr "Mail"
-#: ../lib/Galette/Core/Galette.php:903
+#: ../lib/Galette/Core/Galette.php:943
#: ../../tempcache/elements/mailing_recipients.html.twig:106
msgid "Generate labels"
msgstr "Generate labels"
-#: ../lib/Galette/Core/Galette.php:908
+#: ../lib/Galette/Core/Galette.php:948
msgid "Generate Member Cards"
msgstr "Generate Member Cards"
-#: ../lib/Galette/Core/Galette.php:913
+#: ../lib/Galette/Core/Galette.php:953
#: ../../tempcache/pages/contributions_list.html.twig:56
msgid "Export as CSV"
msgstr "Export as CSV"
#: ../lib/Galette/Core/Preferences.php:1136
#: ../lib/Galette/Core/Preferences.php:1152 ../lib/Galette/IO/Csv.php:97
-#: ../lib/Galette/Entity/Texts.php:193
+#: ../lib/Galette/Entity/Document.php:448 ../lib/Galette/Entity/Texts.php:193
#: ../../tempcache/pages/history.html.twig:323
#: ../../tempcache/pages/mailings_list.html.twig:296
msgid "Y-m-d H:i:s"
msgid "invoice"
msgstr "invoice"
-#: ../lib/Galette/Features/Permissions.php:59
+#: ../lib/Galette/Features/Permissions.php:60
msgid "Inaccessible"
msgstr "Inaccessible"
-#: ../lib/Galette/Features/Permissions.php:60
+#: ../lib/Galette/Features/Permissions.php:64
+#: ../lib/Galette/Entity/ListsConfig.php:202
+msgid "Public"
+msgstr "Public"
+
+#: ../lib/Galette/Features/Permissions.php:68
msgid "User, read only"
msgstr "User, read only"
-#: ../lib/Galette/Features/Permissions.php:61
+#: ../lib/Galette/Features/Permissions.php:69
msgid "User, read/write"
msgstr "User, read/write"
-#: ../lib/Galette/Features/Permissions.php:62
+#: ../lib/Galette/Features/Permissions.php:70
#: ../../tempcache/elements/ajax_members.html.twig:152
#: ../../tempcache/elements/group_persons.html.twig:148
#: ../../tempcache/pages/members_list.html.twig:626
msgid "Group manager"
msgstr "Group manager"
-#: ../lib/Galette/Features/Permissions.php:63
+#: ../lib/Galette/Features/Permissions.php:71
#: ../../tempcache/elements/ajax_members.html.twig:141
#: ../../tempcache/elements/group_persons.html.twig:137
#: ../../tempcache/pages/members_list.html.twig:619
msgid "Staff member"
msgstr "Staff member"
-#: ../lib/Galette/Features/Permissions.php:64
+#: ../lib/Galette/Features/Permissions.php:72
#: ../../tempcache/elements/ajax_members.html.twig:130
#: ../../tempcache/elements/group_persons.html.twig:126
msgid "Administrator"
msgstr "Administrator"
-#: ../lib/Galette/Features/Permissions.php:68
-#: ../lib/Galette/Entity/ListsConfig.php:202
-msgid "Public"
-msgstr "Public"
-
#: ../lib/Galette/Features/I18n.php:115
#, php-format
msgid "Unable to add dynamic translation for %field :("
msgstr "Contribution year"
#: ../lib/Galette/Features/Replacements.php:402
+#: ../../tempcache/pages/document_form.html.twig:176
msgid "Comment"
msgstr "Comment"
msgid "Membership"
msgstr "Membership"
+#: ../lib/Galette/Entity/Document.php:464
+msgid "Association status"
+msgstr "Association status"
+
+#: ../lib/Galette/Entity/Document.php:465
+msgid "Rules of procedure"
+msgstr "Rules of procedure"
+
+#: ../lib/Galette/Entity/Document.php:466
+#: ../../tempcache/elements/edit_pdf_models.html.twig:189
+#: ../../tempcache/pages/member_show.html.twig:152
+msgid "Adhesion form"
+msgstr "Adhesion form"
+
+#: ../lib/Galette/Entity/Document.php:467
+msgid "Meeting minutes"
+msgstr "Meeting minutes"
+
+#: ../lib/Galette/Entity/Document.php:468
+msgid "Votes results"
+msgstr "Votes results"
+
#: ../lib/Galette/Entity/Title.php:169
msgid "You cannot delete Mr. or Mrs. titles!"
msgstr "You cannot delete Mr. or Mrs. titles!"
msgstr "Association"
#. TRANS: see https://fomantic-ui.com/modules/calendar.html#custom-format - must be the same as Y-m-d for PHP https://www.php.net/manual/datetime.format.php
-#: ../includes/dependencies.php:172
+#: ../includes/dependencies.php:174
msgid "YYYY-MM-DD"
msgstr "YYYY-MM-DD"
-#: ../includes/dependencies.php:443
+#: ../includes/dependencies.php:445
msgid "Failed CSRF check!"
msgstr "Failed CSRF check!"
#: ../../tempcache/pages/configuration_title_form.html.twig:94
#: ../../tempcache/pages/preferences.html.twig:2662
#: ../../tempcache/pages/configuration_payment_type_form.html.twig:94
+#: ../../tempcache/pages/document_form.html.twig:201
msgid "Cancel"
msgstr "Cancel"
#: ../../tempcache/pages/configuration_core_lists.html.twig:74
#: ../../tempcache/pages/configuration_core_lists.html.twig:186
#: ../../tempcache/pages/configuration_core_lists.html.twig:305
+#: ../../tempcache/pages/document_form.html.twig:147
msgid "Permissions"
msgstr "Permissions"
#: ../../tempcache/pages/transaction_form.html.twig:285
#: ../../tempcache/pages/contributions_list.html.twig:408
#: ../../tempcache/pages/contributions_list.html.twig:662
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:154
#: ../../tempcache/pages/advanced_search.html.twig:819
#: ../../tempcache/pages/advanced_search.html.twig:824
msgid "Type"
#: ../../tempcache/pages/member_form.html.twig:468
#: ../../tempcache/pages/configuration_payment_type_form.html.twig:89
#: ../../tempcache/pages/configuration_dynamic_translations.html.twig:185
+#: ../../tempcache/pages/document_form.html.twig:196
msgid "Save"
msgstr "Save"
msgid "Receipt"
msgstr "Receipt"
-#: ../../tempcache/elements/edit_pdf_models.html.twig:189
-#: ../../tempcache/pages/member_show.html.twig:152
-msgid "Adhesion form"
-msgstr "Adhesion form"
-
#: ../../tempcache/elements/display_dynamic_fields.html.twig:45
#: ../../tempcache/components/dynamic_fields.html.twig:60
msgid "Additionnal fields:"
msgstr "Add new social network"
#: ../../tempcache/elements/edit_socials.html.twig:151
+#: ../../tempcache/pages/document_form.html.twig:121
msgid "Choose or enter your own..."
msgstr "Choose or enter your own..."
msgstr "Field name"
#: ../../tempcache/pages/configuration_dynamic_field_form.html.twig:423
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:160
msgid "Visibility"
msgstr "Visibility"
+#: ../../tempcache/pages/documents_public_list.html.twig:86
+#: ../../tempcache/pages/documents_list.html.twig:57
+#, php-format
+msgid "%count document"
+msgid_plural "%count documents"
+msgstr[0] "%count document"
+msgstr[1] "%count documents"
+
#: ../../tempcache/pages/transaction_form.html.twig:63
msgid "Transaction details"
msgstr "Transaction details"
msgid "Click on a row to select a member"
msgstr "Click on a row to select a member"
+#: ../../tempcache/pages/documents_list.html.twig:84
+msgid "Add a document"
+msgstr "Add a document"
+
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:166
+msgid "Filename"
+msgstr "Filename"
+
+#: ../../tempcache/pages/documents_list.html.twig:97
+#: ../../tempcache/pages/documents_list.html.twig:180
+#: ../../tempcache/pages/saved_searches_list.html.twig:69
+#: ../../tempcache/pages/saved_searches_list.html.twig:128
+#: ../../tempcache/pages/advanced_search.html.twig:308
+#: ../../tempcache/pages/advanced_search.html.twig:661
+msgid "Creation date"
+msgstr "Creation date"
+
+#: ../../tempcache/pages/documents_list.html.twig:247
+msgid "No document"
+msgstr "No document"
+
#: ../../tempcache/pages/index.html.twig:122
msgid "Lost your password?"
msgstr "Lost your password?"
#: ../../tempcache/pages/import.html.twig:259
#: ../../tempcache/pages/preferences.html.twig:245
#: ../../tempcache/pages/preferences.html.twig:2017
+#: ../../tempcache/pages/document_form.html.twig:101
#: ../../tempcache/components/forms/picture.html.twig:75
#: ../../tempcache/components/dynamic_fields.html.twig:758
msgid "Choose a file"
#: ../../tempcache/pages/preferences.html.twig:243
#: ../../tempcache/pages/preferences.html.twig:2015
+#: ../../tempcache/pages/document_form.html.twig:99
#: ../../tempcache/components/forms/picture.html.twig:73
#: ../../tempcache/components/dynamic_fields.html.twig:756
msgid "Choose another file"
msgstr[0] "%count search"
msgstr[1] "%count searches"
-#: ../../tempcache/pages/saved_searches_list.html.twig:69
-#: ../../tempcache/pages/saved_searches_list.html.twig:128
-#: ../../tempcache/pages/advanced_search.html.twig:308
-#: ../../tempcache/pages/advanced_search.html.twig:661
-msgid "Creation date"
-msgstr "Creation date"
-
#: ../../tempcache/pages/saved_searches_list.html.twig:140
msgid "Search parameters"
msgstr "Search parameters"
msgstr "Code:"
#: ../../tempcache/pages/500.html.twig:121
+#: ../../tempcache/pages/document_form.html.twig:68
msgid "File:"
msgstr "File:"
msgid "Trace"
msgstr "Trace"
+#: ../../tempcache/pages/document_form.html.twig:111
+msgid "Document type"
+msgstr "Document type"
+
+#: ../../tempcache/pages/document_form.html.twig:184
+msgid "Extra information displayed along with document."
+msgstr "Extra information displayed along with document."
+
#: ../../tempcache/pages/export.html.twig:56
msgid ""
"Each selected export will be stored into a separate file in the exports "
--- /dev/null
+<?php
+
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Galette\Controllers\Crud;
+
+use Galette\Core\Galette;
+use Galette\Entity\Document;
+use Galette\Filters\DocumentsList;
+use Galette\IO\File;
+use Galette\Repository\DynamicFieldsSet;
+use Throwable;
+use Galette\Controllers\CrudController;
+use Slim\Psr7\Request;
+use Slim\Psr7\Response;
+use Galette\DynamicFields\DynamicField;
+use Analog\Analog;
+
+/**
+ * Galette documents controller
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ */
+
+class DocumentsController extends CrudController
+{
+ // CRUD - Create
+
+ /**
+ * Add page
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param ?string $form_name Form name
+ *
+ * @return Response
+ */
+ public function add(Request $request, Response $response, string $form_name = null): Response
+ {
+ if (isset($this->session->document)) {
+ $document = $this->session->document;
+ unset($this->session->document);
+ } else {
+ $document = new Document($this->zdb);
+ }
+ $params = [
+ 'page_title' => _T("Add document"),
+ 'action' => 'add',
+ 'mode' => (($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') ? 'ajax' : ''),
+ 'document' => $document,
+ 'types' => $document->getSystemTypes(),
+ 'perm_names' => $document::getPermissionsList(true),
+ 'html_editor' => true,
+ ];
+
+ // display page
+ $this->view->render(
+ $response,
+ 'pages/document_form.html.twig',
+ $params
+ );
+ return $response;
+ }
+
+ /**
+ * Add action
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param ?string $form_name Form name
+ *
+ * @return Response
+ */
+ public function doAdd(Request $request, Response $response, string $form_name = null): Response
+ {
+ $post = $request->getParsedBody();
+
+ $error_detected = [];
+ $warning_detected = [];
+
+ if (isset($post['cancel'])) {
+ return $response
+ ->withStatus(301)
+ ->withHeader('Location', $this->cancelUri($this->getArgs($request)));
+ }
+
+ $document = new Document($this->zdb);
+
+ try {
+ $document->store($post, $_FILES);
+ $error_detected = $document->getErrors();
+ $warning_detected = $document->getWarnings();
+ } catch (Throwable $e) {
+ $msg = 'An error occurred adding new document.';
+ Analog::log(
+ $msg . ' | ' .
+ $e->getMessage(),
+ Analog::ERROR
+ );
+ if (Galette::isDebugEnabled()) {
+ throw $e;
+ }
+ $error_detected[] = _T('An error occurred adding document :(');
+ }
+
+ //flash messages
+ if (count($error_detected) > 0) {
+ foreach ($error_detected as $error) {
+ $this->flash->addMessage(
+ 'error_detected',
+ $error
+ );
+ }
+ } else {
+ $this->flash->addMessage(
+ 'success_detected',
+ _T('Document has been successfully stored!')
+ );
+ }
+
+ if (count($warning_detected) > 0) {
+ foreach ($warning_detected as $warning) {
+ $this->flash->addMessage(
+ 'warning_detected',
+ $warning
+ );
+ }
+ }
+
+ //handle redirections
+ if (count($error_detected) > 0) {
+ //something went wrong :'(
+ $this->session->document = $document;
+ return $response
+ ->withStatus(301)
+ ->withHeader(
+ 'Location',
+ $this->routeparser->urlFor('addDocument')
+ );
+ } else {
+ return $response
+ ->withStatus(301)
+ ->withHeader(
+ 'Location',
+ $this->routeparser->urlFor('documentsList')
+ );
+ }
+ }
+
+ // /CRUD - Create
+ // CRUD - Read
+
+ /**
+ * List page
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param string|null $option One of 'page' or 'order'
+ * @param integer|string|null $value Value of the option
+ *
+ * @return Response
+ */
+ public function list(
+ Request $request,
+ Response $response,
+ string $option = null,
+ int|string $value = null,
+ ): Response {
+ $filters = new DocumentsList();
+
+ $document = new Document($this->zdb);
+ $documents = $document->getList();
+
+ //assign pagination variables to the template and add pagination links
+ $filters->setViewPagination($this->routeparser, $this->view);
+
+ $params = [
+ 'page_title' => _T("Documents list"),
+ 'nb' => count($documents),
+ 'documents' => $documents,
+ 'filters' => $filters
+ ];
+
+ // display page
+ $this->view->render(
+ $response,
+ 'pages/documents_list.html.twig',
+ $params
+ );
+ return $response;
+ }
+
+ /**
+ * List page
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param string|null $option One of 'page' or 'order'
+ * @param integer|string|null $value Value of the option
+ *
+ * @return Response
+ */
+ public function publicList(
+ Request $request,
+ Response $response,
+ string $option = null,
+ int|string $value = null,
+ ): Response {
+ $document = new Document($this->zdb);
+ $documents = $document->getTypedList();
+
+ $params = [
+ 'page_title' => _T("Documents list"),
+ 'typed_documents' => $documents
+ ];
+
+ // display page
+ $this->view->render(
+ $response,
+ 'pages/documents_public_list.html.twig',
+ $params
+ );
+ return $response;
+ }
+
+ /**
+ * Filtering
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ *
+ * @return Response
+ */
+ public function filter(Request $request, Response $response): Response
+ {
+ //no filtering
+ return $response;
+ }
+
+ /**
+ * Get a document
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param integer $id Document ID
+ *
+ * @return Response
+ */
+ public function getDocument(
+ Request $request,
+ Response $response,
+ int $id
+ ): Response {
+ $document = new Document($this->zdb, $id);
+
+ if (!$document->canShow($this->login)) {
+ $this->flash->addMessage(
+ 'error_detected',
+ _T("You do not have permission for requested URL.")
+ );
+
+ return $response
+ ->withStatus(301)
+ ->withHeader(
+ 'Location',
+ $this->routeparser->urlFor(
+ 'slash'
+ )
+ );
+ }
+
+ if (file_exists($document->getDestDir() . $document->getDocumentFilename())) {
+ $type = File::getMimeType($document->getDestDir() . $document->getDocumentFilename());
+
+ $response = $response->withHeader('Content-Description', 'File Transfer')
+ ->withHeader('Content-Type', $type)
+ ->withHeader('Content-Disposition', 'attachment;filename="' . $document->getDocumentFilename() . '"')
+ ->withHeader('Pragma', 'no-cache')
+ ->withHeader('Content-Transfer-Encoding', 'binary')
+ ->withHeader('Expires', '0')
+ ->withHeader('Cache-Control', 'must-revalidate')
+ ->withHeader('Pragma', 'public');
+
+ $stream = fopen('php://memory', 'r+');
+ fwrite($stream, file_get_contents($document->getDestDir() . $document->getDocumentFilename()));
+ rewind($stream);
+
+ return $response->withBody(new \Slim\Psr7\Stream($stream));
+ } else {
+ Analog::log(
+ 'A request has been made to get a document file named `' .
+ $document->getDocumentFilename() . '` that does not exists.',
+ Analog::WARNING
+ );
+
+ $this->flash->addMessage(
+ 'error_detected',
+ _T("The file does not exists or cannot be read :(")
+ );
+
+ return $response
+ ->withStatus(301)
+ ->withHeader(
+ 'Location',
+ $this->routeparser->urlFor(
+ 'slash'
+ )
+ );
+ }
+ }
+
+ // /CRUD - Read
+ // CRUD - Update
+
+ /**
+ * Edit page
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param integer $id Document id
+ *
+ * @return Response
+ */
+ public function edit(Request $request, Response $response, int $id): Response
+ {
+ if (isset($this->session->document)) {
+ $document = $this->session->document;
+ unset($this->session->document);
+ } else {
+ $document = new Document($this->zdb, $id);
+ }
+ $params = [
+ 'page_title' => _T("Edit document"),
+ 'action' => 'edit',
+ 'mode' => (($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') ? 'ajax' : ''),
+ 'document' => $document,
+ 'types' => $document->getSystemTypes(),
+ 'perm_names' => $document::getPermissionsList(true),
+ 'html_editor' => true,
+ ];
+
+ // display page
+ $this->view->render(
+ $response,
+ 'pages/document_form.html.twig',
+ $params
+ );
+ return $response;
+ }
+
+ /**
+ * Edit action
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param integer $id Document id
+ *
+ * @return Response
+ */
+ public function doEdit(Request $request, Response $response, int $id): Response
+ {
+ $post = $request->getParsedBody();
+
+ $error_detected = [];
+ $warning_detected = [];
+
+ if (isset($post['cancel'])) {
+ return $response
+ ->withStatus(301)
+ ->withHeader('Location', $this->cancelUri($this->getArgs($request)));
+ }
+
+ $document = new Document($this->zdb, $id);
+
+ try {
+ $document->store($post, $_FILES);
+ $error_detected = $document->getErrors();
+ $warning_detected = $document->getWarnings();
+ } catch (Throwable $e) {
+ $msg = 'An error occurred adding new document.';
+ Analog::log(
+ $msg . ' | ' .
+ $e->getMessage(),
+ Analog::ERROR
+ );
+ if (Galette::isDebugEnabled()) {
+ throw $e;
+ }
+ $error_detected[] = _T('An error occurred adding document :(');
+ }
+
+ //flash messages
+ if (count($error_detected) > 0) {
+ foreach ($error_detected as $error) {
+ $this->flash->addMessage(
+ 'error_detected',
+ $error
+ );
+ }
+ } else {
+ $this->flash->addMessage(
+ 'success_detected',
+ _T('Document has been successfully stored!')
+ );
+ }
+
+ if (count($warning_detected) > 0) {
+ foreach ($warning_detected as $warning) {
+ $this->flash->addMessage(
+ 'warning_detected',
+ $warning
+ );
+ }
+ }
+
+ //handle redirections
+ if (count($error_detected) > 0) {
+ //something went wrong :'(
+ $this->session->document = $document;
+ return $response
+ ->withStatus(301)
+ ->withHeader(
+ 'Location',
+ $this->routeparser->urlFor('addDocument')
+ );
+ } else {
+ return $response
+ ->withStatus(301)
+ ->withHeader(
+ 'Location',
+ $this->routeparser->urlFor('documentsList')
+ );
+ }
+ }
+
+ // /CRUD - Update
+ // CRUD - Delete
+
+ /**
+ * Get redirection URI
+ *
+ * @param array<string,mixed> $args Route arguments
+ *
+ * @return string
+ */
+ public function redirectUri(array $args): string
+ {
+ return $this->routeparser->urlFor('documentsList');
+ }
+
+ /**
+ * Get form URI
+ *
+ * @param array<string,mixed> $args Route arguments
+ *
+ * @return string
+ */
+ public function formUri(array $args): string
+ {
+ return $this->routeparser->urlFor(
+ 'doRemoveDocument',
+ ['id' => $args['id']]
+ );
+ }
+
+ /**
+ * Get confirmation removal page title
+ *
+ * @param array<string,mixed> $args Route arguments
+ *
+ * @return string
+ */
+ public function confirmRemoveTitle(array $args): string
+ {
+ return _T('Delete document');
+ }
+
+ /**
+ * Remove object
+ *
+ * @param array<string,mixed> $args Route arguments
+ * @param array<string,mixed> $post POST values
+ *
+ * @return boolean
+ */
+ protected function doDelete(array $args, array $post): bool
+ {
+ $document = new Document($this->zdb, (int)$post['id']);
+ return $document->remove();
+ }
+
+ // /CRUD - Delete
+ // /CRUD
+}
'route' => [
'name' => 'charts'
]
+ ], [
+ 'label' => _T("Documents"),
+ 'title' => _T("Add documents to share related to your association (status, rules of procedure, ...)"),
+ 'route' => [
+ 'name' => 'documentsList',
+ 'aliases' => ['editDocument', 'addDocument']
+ ]
]
]);
}//admin or staff
* @var Login $login
* @var Plugins $plugins
*/
- global $preferences, $login, $plugins;
+ global $preferences, $login, $plugins, $zdb;
$menus = [];
if ($preferences->showPublicPages($login)) {
]
];
+ //display documents menu if at least one document is present with current ACLs
+ $document = new \Galette\Entity\Document($zdb);
+ $documents = $document->getList();
+ if ($login->isSuperAdmin() || count($documents)) {
+ $menus['public']['items'][] = [
+ 'label' => _T("Documents"),
+ 'title' => _T("View documents related to your association"),
+ 'route' => [
+ 'name' => 'documentsPublicList'
+ ],
+ 'icon' => 'file alternate'
+ ];
+ }
+
foreach (array_keys($plugins->getModules()) as $module_id) {
//get plugins public menus entries
$plugin_class = $plugins->getClassName($module_id, true);
/**
* @var Login $login
* @var Plugins $plugins
+ * @var Db $zdb
*/
- global $login, $plugins;
+ global $login, $plugins, $zdb;
$dashboards = [];
);
}
+ //display documents menu if at least one document is present with current ACLs
+ $document = new \Galette\Entity\Document($zdb);
+ $documents = $document->getList();
+ if ($login->isSuperAdmin() || count($documents)) {
+ $dashboards = array_merge(
+ $dashboards,
+ [
+ [
+ 'label' => _T("Documents"),
+ 'title' => _T("View documents related to your association"),
+ 'route' => [
+ 'name' => 'documentsPublicList'
+ ],
+ 'icon' => 'dividers'
+ ]
+ ]
+ );
+ }
+
if ($login->isAdmin()) {
$dashboards = array_merge(
$dashboards,
'args' => ['type' => 'transactions']
],
'icon' => 'book'
- ],
-
+ ]
]
);
}
--- /dev/null
+<?php
+
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Galette\Entity;
+
+use ArrayObject;
+use DateTime;
+use Galette\Core\Authentication;
+use Galette\Core\Login;
+use Galette\Features\I18n;
+use Galette\Features\Permissions;
+use Galette\IO\FileInterface;
+use Galette\IO\FileTrait;
+use Throwable;
+use Galette\Core\Db;
+use Analog\Analog;
+
+/**
+ * Documents
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ */
+
+class Document implements FileInterface
+{
+ use I18n;
+ use Permissions;
+ use FileTrait {
+ store as protected trait_store;
+ writeOnDisk as protected trait_writeOnDisk;
+ }
+
+ public const TABLE = 'documents';
+ public const PK = 'id_document';
+
+ public const STATUS = 'status';
+ public const RULES = 'rules';
+ public const ADHESION = 'adhesion';
+ public const MINUTES = 'minutes';
+ public const VOTES = 'votes';
+
+ /** @var Db */
+ private Db $zdb;
+ /** @var int */
+ private int $id;
+ /** @var string */
+ private string $type;
+ /** @var string */
+ private string $filename;
+ /** @var DateTime */
+ private DateTime $creation_date;
+ /** @var string */
+ protected string $store_path = GALETTE_DOCUMENTS_PATH;
+ /** @var ?string */
+ private ?string $comment = null;
+ /** @var array<string> */
+ private array $errors = [];
+
+ /**
+ * Main constructor
+ *
+ * @param Db $zdb Database instance
+ * @param int|ArrayObject<string,int|string>|null $args Arguments
+ */
+ public function __construct(Db $zdb, int|ArrayObject $args = null)
+ {
+ $this->zdb = $zdb;
+ $this->can_public = true;
+
+ $this->init($this->store_path);
+
+ if (is_int($args)) {
+ $this->load($args);
+ } elseif ($args instanceof ArrayObject) {
+ $this->loadFromRs($args);
+ }
+ }
+
+ /**
+ * Load a document from its identifier
+ *
+ * @param integer $id Identifier
+ *
+ * @return void
+ */
+ private function load(int $id): void
+ {
+ try {
+ $select = $this->zdb->select(self::TABLE);
+ $select->limit(1)->where([self::PK => $id]);
+
+ $results = $this->zdb->execute($select);
+ /** @var ArrayObject<string, int|string> $res */
+ $res = $results->current();
+ $this->loadFromRs($res);
+ } catch (Throwable $e) {
+ Analog::log(
+ 'An error occurred loading document #' . $id . "Message:\n" .
+ $e->getMessage(),
+ Analog::ERROR
+ );
+ }
+ }
+
+ /**
+ * Get documents
+ *
+ * @param string|null $type Type to retrieve
+ *
+ * @return array<int,Document>
+ *
+ * @throws Throwable
+ */
+ public static function getList(string $type = null): array
+ {
+ global $zdb, $login;
+
+ try {
+ $select = $zdb->select(self::TABLE);
+
+ if ($type !== null) {
+ $select->where(['type' => $type]);
+ }
+
+ $select->order(self::PK);
+
+ $results = $zdb->execute($select);
+ $documents = [];
+ $access_level = $login->getAccessLevel();
+
+ foreach ($results as $r) {
+ // skip entries according to access control
+ if (
+ $r->visible == FieldsConfig::NOBODY ||
+ ($r->visible == FieldsConfig::ADMIN &&
+ $access_level < Authentication::ACCESS_ADMIN) ||
+ ($r->visible == FieldsConfig::STAFF &&
+ $access_level < Authentication::ACCESS_STAFF) ||
+ ($r->visible == FieldsConfig::MANAGER &&
+ $access_level < Authentication::ACCESS_MANAGER) ||
+ (($r->visible == FieldsConfig::USER_READ || $r->visible == FieldsConfig::USER_WRITE) &&
+ $access_level < Authentication::ACCESS_USER)
+ ) {
+ continue;
+ }
+
+ $documents[$r->{self::PK}] = new Document($zdb, $r);
+ }
+ return $documents;
+ } catch (Throwable $e) {
+ Analog::log(
+ "An error occurred loading documents. Message:\n" .
+ $e->getMessage(),
+ Analog::ERROR
+ );
+ throw $e;
+ }
+ }
+
+ /**
+ * Get list by type
+ *
+ * @return array<string, array<int, Document>>
+ *
+ * @throws Throwable
+ */
+ public function getTypedList(): array
+ {
+ $list = $this->getList();
+ $sys_types = $this->getSystemTypes(false);
+
+ $typed_list = array_fill_keys($sys_types, []);
+ foreach ($list as $doc_id => $document) {
+ $typed_list[$document->getType()][] = $document;
+ }
+
+ //cleanup: some system types may have no entries
+ foreach ($sys_types as $type) {
+ if (count($typed_list[$type]) == 0) {
+ unset($typed_list[$type]);
+ }
+ }
+
+ return $typed_list;
+ }
+
+ /**
+ * Check if a document can be shown
+ *
+ * @param Login $login Login
+ *
+ * @return boolean
+ */
+ public function canShow(Login $login): bool
+ {
+ $access_level = $login->getAccessLevel();
+
+ switch ($this->getPermission()) {
+ case FieldsConfig::ALL:
+ return true;
+ case FieldsConfig::NOBODY:
+ return false;
+ case FieldsConfig::ADMIN:
+ return $access_level >= Authentication::ACCESS_ADMIN;
+ case FieldsConfig::STAFF:
+ return $access_level >= Authentication::ACCESS_STAFF;
+ case FieldsConfig::MANAGER:
+ return $access_level >= Authentication::ACCESS_MANAGER;
+ case FieldsConfig::USER_WRITE:
+ case FieldsConfig::USER_READ:
+ return $access_level >= Authentication::ACCESS_USER;
+ }
+
+ return false;
+ }
+
+ /**
+ * Load document from a db ResultSet
+ *
+ * @param ArrayObject<string, int|string> $rs ResultSet
+ *
+ * @return void
+ */
+ private function loadFromRs(ArrayObject $rs): void
+ {
+ $this->id = $rs->{self::PK};
+ $this->type = $rs->type;
+ $this->permission = $rs->visible;
+ $this->filename = $rs->filename;
+ $this->comment = $rs->comment;
+ $this->creation_date = new DateTime($rs->creation_date);
+ }
+
+ /**
+ * Store document in database
+ *
+ * @param array<string,mixed> $post POST data
+ * @param array<string,mixed> $files Files
+ *
+ * @return boolean
+ */
+ public function store(array $post, array $files): bool
+ {
+ $this->setType($post['document_type']);
+ $this->setComment($post['comment']);
+ $this->permission = $post['visible'];
+
+ $handled = $this->handleFiles($files);
+ if ($handled !== true) {
+ $this->errors = $handled;
+ return false;
+ }
+
+ try {
+ $values = [
+ 'type' => $this->type,
+ 'filename' => $this->filename,
+ 'visible' => $this->getPermission(),
+ 'comment' => $this->comment,
+ ];
+ if (isset($this->id) && $this->id > 0) {
+ $update = $this->zdb->update(self::TABLE);
+ $update->set($values)->where([self::PK => $this->id]);
+ $this->zdb->execute($update);
+ } else {
+ $values['creation_date'] = date('Y-m-d H:i:s');
+ $insert = $this->zdb->insert(self::TABLE);
+ $insert->values($values);
+ $add = $this->zdb->execute($insert);
+ if (!$add->count() > 0) {
+ Analog::log('Not stored!', Analog::ERROR);
+ return false;
+ }
+
+ $this->id = $this->zdb->getLastGeneratedValue($this);
+ if (!in_array($this->type, $this->getSystemTypes(false))) {
+ $this->addTranslation($this->type);
+ }
+ }
+ return true;
+ } catch (Throwable $e) {
+ $this->removeFile();
+ Analog::log(
+ 'An error occurred storing document: ' . $e->getMessage(),
+ Analog::ERROR
+ );
+ throw $e;
+ }
+ }
+
+ /**
+ * Remove document
+ *
+ * @param array<int>|null $ids IDs to remove, default to current id
+ *
+ * @return boolean
+ */
+ public function remove(array $ids = null): bool
+ {
+ if ($ids == null) {
+ $ids[] = $this->id;
+ }
+
+ try {
+ $this->zdb->connection->beginTransaction();
+ $delete = $this->zdb->delete(self::TABLE);
+ $delete->where([self::PK => $ids]);
+ $this->zdb->execute($delete);
+ if (!$this->removeFile()) {
+ throw new \RuntimeException('cannot remove file document from disk');
+ }
+ Analog::log(
+ 'Document #' . implode(', #', $ids) . ' deleted successfully.',
+ Analog::INFO
+ );
+
+ $this->zdb->connection->commit();
+ return true;
+ } catch (Throwable $e) {
+ $this->zdb->connection->rollBack();
+ Analog::log(
+ 'Unable to delete document #' . implode(', #', $ids) . ' | ' . $e->getMessage(),
+ Analog::ERROR
+ );
+ throw $e;
+ }
+ }
+
+ /**
+ * Remove document file
+ *
+ * @return bool
+ */
+ protected function removeFile(): bool
+ {
+ $file = $this->getDestDir() . $this->getDocumentFilename();
+ if (file_exists($file)) {
+ return unlink($file);
+ }
+
+ Analog::log('File ' . $file . ' does not exist', Analog::WARNING);
+ return false;
+ }
+
+ /**
+ * Get file URL
+ *
+ * @return string
+ */
+ public function getURL(): string
+ {
+ return $this->getDestDir() . $this->getDocumentFileName();
+ }
+
+ /**
+ * Get document ID
+ *
+ * @return ?int
+ */
+ public function getId(): ?int
+ {
+ return $this->id ?? null;
+ }
+
+ /**
+ * Get document file name
+ *
+ * @return string
+ */
+ public function getDocumentFilename(): string
+ {
+ return $this->filename ?? '';
+ }
+
+ /**
+ * Set comment
+ * @param ?string $comment Comment to set
+ *
+ * @return self
+ */
+ public function setComment(?string $comment): self
+ {
+ $this->comment = $comment;
+ return $this;
+ }
+
+ /**
+ * Get comment
+ *
+ * @return ?string
+ */
+ public function getComment(): ?string
+ {
+ return $this->comment;
+ }
+
+ /**
+ * Set type
+ *
+ * @param string $type Type
+ *
+ * @return self
+ */
+ public function setType(string $type): self
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ /**
+ * Get type
+ *
+ * @return string
+ */
+ public function getType(): string
+ {
+ return $this->type ?? '';
+ }
+
+ /**
+ * Get creation date
+ *
+ * @param boolean $formatted Return formatted date (default) or not
+ *
+ * @return string|DateTime
+ */
+ public function getCreationDate(bool $formatted = true): string|DateTime
+ {
+ if ($formatted) {
+ return $this->creation_date->format(_T('Y-m-d H:i:s'));
+ }
+ return $this->creation_date;
+ }
+
+ /**
+ * Get system social types
+ *
+ * @param boolean $translated Return translated types (default) or not
+ *
+ * @return array<string,string>
+ */
+ public function getSystemTypes(bool $translated = true): array
+ {
+ if ($translated) {
+ $systypes = [
+ self::STATUS => _T('Association status'),
+ self::RULES => _T('Rules of procedure'),
+ self::ADHESION => _T('Adhesion form'),
+ self::MINUTES => _T('Meeting minutes'),
+ self::VOTES => _T('Votes results')
+ ];
+ } else {
+ $systypes = [
+ self::STATUS => 'Association status',
+ self::RULES => 'Rules of procedure',
+ self::ADHESION => 'Adhesion form',
+ self::MINUTES => 'Meeting minutes',
+ self::VOTES => 'Votes results'
+ ];
+ }
+ return $systypes;
+ }
+
+ /**
+ * Get system documents types
+ *
+ * @param string $type Document type
+ * @param boolean $translated Return translated types (default) or not
+ *
+ * @return string
+ */
+ public function getSystemType(string $type, bool $translated = true): string
+ {
+ return $this->getSystemTypes($translated)[$type] ?? _T($type);
+ }
+
+ /**
+ * Handle files
+ *
+ * @param array<string,mixed> $files Files sent
+ *
+ * @return array<string>|true
+ */
+ public function handleFiles(array $files): array|bool
+ {
+ $this->errors = [];
+ // document upload
+ if (isset($files['document_file'])) {
+ if ($files['document_file']['error'] === UPLOAD_ERR_OK) {
+ if ($files['document_file']['tmp_name'] != '') {
+ if (is_uploaded_file($files['document_file']['tmp_name'])) {
+ $res = $this->trait_store($files['document_file']);
+ if ($res < 0) {
+ $this->errors[] = $this->getErrorMessage($res);
+ } else {
+ $this->filename = sprintf(
+ '%s.%s',
+ $this->name_wo_ext,
+ $this->extension
+ );
+ }
+ }
+ }
+ } elseif (!isset($this->id)) {
+ Analog::log(
+ $this->getPhpErrorMessage($files['document_file']['error']),
+ Analog::WARNING
+ );
+ $this->errors[] = $this->getPhpErrorMessage($files['document_file']['error']);
+ }
+ }
+
+ if (count($this->errors) > 0) {
+ Analog::log(
+ 'Some errors has been thew attempting to edit/store a document file' . "\n" .
+ print_r($this->errors, true),
+ Analog::ERROR
+ );
+ return $this->errors;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Get errors
+ *
+ * @return string[]
+ */
+ public function getErrors(): array
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Write file on disk
+ *
+ * @param string $tmpfile Temporary file
+ * @param bool $ajax If the file comes from an ajax call (dnd)
+ *
+ * @return bool|int
+ */
+ public function writeOnDisk(string $tmpfile, bool $ajax): bool|int
+ {
+ //remove existing file when updating
+ if (isset($this->id) && $this->id > 0) {
+ $this->removeFile();
+ }
+ return $this->trait_writeOnDisk($tmpfile, $ajax);
+ }
+}
/**
* Migrate old required fields configuration
- * Only needeed for 0.7.4 upgrade
+ * Only needed for 0.7.4 upgrade
* (should have been 0.7.3 - but I missed that.)
*
* @return boolean
trait Permissions
{
protected ?int $permission = null;
+ protected bool $can_public = false;
/* FIXME/ requires PHP 8.2
public const NOBODY = 0;
{
$list = [
FieldsConfig::NOBODY => _T("Inaccessible"),
+ ];
+
+ if ($can_public) {
+ $list += [FieldsConfig::ALL => _T("Public")];
+ }
+
+ $list += [
FieldsConfig::USER_READ => _T("User, read only"),
FieldsConfig::USER_WRITE => _T("User, read/write"),
FieldsConfig::MANAGER => _T("Group manager"),
FieldsConfig::ADMIN => _T("Administrator"),
];
- if ($can_public) {
- $all = [FieldsConfig::ALL => _T("Public")];
- $list = array_unshift($all);
- }
-
return $list;
}
*/
public function getPermissionName(): string
{
- $perms = self::getPermissionsList();
+ $perms = self::getPermissionsList($this->can_public);
return $perms[$this->getPermission()];
}
use Galette\Entity\Social;
/**
- * Replacements feature
+ * Socials feature
*
* @author Johan Cwiklinski <johan@x-tnd.be>
*/
--- /dev/null
+<?php
+
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Galette\Filters;
+
+use Analog\Analog;
+use Galette\Core\Pagination;
+use Galette\Entity\Group;
+use Galette\Repository\Members;
+
+/**
+ * Documents list filters and paginator
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ */
+
+class DocumentsList extends Pagination
+{
+ public const ORDERBY_DATE = 0;
+ public const ORDERBY_TYPE = 1;
+ public const ORDERBY_NAME = 2;
+ public const ORDERBY_ID = 3;
+
+ /**
+ * Returns the field we want to default set order to
+ *
+ * @return int|string
+ */
+ protected function getDefaultOrder(): int|string
+ {
+ return 'creation_date';
+ }
+
+ /**
+ * Return the default direction for ordering
+ *
+ * @return string ASC or DESC
+ */
+ protected function getDefaultDirection(): string
+ {
+ return self::ORDER_DESC;
+ }
+}
--- /dev/null
+{#
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+#}
+{% extends (mode == 'ajax') ? "ajax.html.twig" : "page.html.twig" %}
+
+{% block content %}
+ <form action="{% if document.getID() is null %}{{ url_for('doAddDocument') }}{% else %}{{ url_for('editDocument', {'id': document.getID()}) }}{% endif %}" method="post" class="ui form" enctype="multipart/form-data">
+ {% if mode != 'ajax' %}
+ <div class="ui segment" id="general">
+ {% endif %}
+ <div class="field ui items">
+ <label>{{ _T("File:") }}</label>
+ {% if document.getID() is not null %}
+ <a href="{{ url_for('getDocumentFile', {'id': document.getID()}) }}">
+ {{ document.getDocumentFilename() }}
+ <i class="external alternate icon" aria-hidden="true"></i>
+ </a>
+ {% endif %}
+ <div class="extra ui basic fitted segment">
+ <div class="ui file action input">
+ <input id="document_new" type="file" name="document_file"/>
+ <label for="document_new" class="ui button{% if callstatic('\\Galette\\Core\\Galette', 'isDemo') %} disabled{% endif %}">
+ <i class="blue upload icon" aria-hidden="true"></i>
+ {% if document.getId() != null %}{{ _T("Choose another file") }}{% else %}{{ _T("Choose a file") }}{% endif %}
+ </label>
+ </div>
+ </div>
+ </div>
+ <div class="required field">
+ <label>{{ _T("Document type") }}</label>
+ <div id="document_type" class="jsonly search-dropdown documents-dropdown ui input">
+ <input id="document_type_input" type="hidden" name="document_type" value="{{ document.getType() }}">
+ <i class="jsonly displaynone dropdown icon" aria-hidden="true"></i>
+ <div class="jsonly displaynone default text">{{ _T("Choose or enter your own...") }}</div>
+ <div class="jsonly displaynone menu">
+ {% for doc_type in document.getSystemTypes(false) %}
+ <div class="item" data-value="{{ doc_type }}">{{ document.getSystemType(doc_type) }}</div>
+ {% endfor %}
+ </div>
+ </div>
+ </div>
+ <div class="required field">
+ <label>{{ _T("Permissions") }}</label>
+ <select name="visible" id="visible" class="ui dropdown">
+ {% for key, value in perm_names %}
+ <option value="{{ key }}"{% if key == document.getPermission() %} selected="selected"{% endif %}>{{ value }}</option>
+ {% endfor %}
+ </select>
+ </div>
+ <div class="field">
+ <label for="field_information">{{ _T("Comment") }}</label>
+ <textarea name="comment" id="comment" cols="20" rows="6">{{ document.getComment() }}</textarea>
+ <span class="tip">{{ _T("Extra information displayed along with document.") }}</span>
+ </div>
+ </div>
+ {% if mode != 'ajax' %}
+ <div class="ui basic center aligned segment">
+ <button type="submit" class="ui labeled icon primary button action">
+ <i class="save icon" aria-hidden="true"></i> {{ _T("Save") }}
+ </button>
+ <input type="submit" name="cancel" value="{{ _T("Cancel") }}" class="ui button" />
+ {% endif %}
+ <input type="hidden" name="id" id="id" value="{{ document.id }}"/>
+ {% include 'components/forms/csrf.html.twig' %}
+ {% if mode != 'ajax' %}
+ </div>
+ {% endif %}
+ </form>
+{% endblock %}
+
+{% block javascripts %}
+ <script>
+ $(function() {
+ activateHtmlEditor($('#comment'), true);
+ $('.documents-dropdown').dropdown({
+ allowAdditions: true,
+ onNoResults: function(searchValue) {
+ $(this).dropdown('set value', searchValue);
+ }
+ });
+ });
+ </script>
+{% endblock %}
--- /dev/null
+{#
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+#}
+{% extends 'elements/list.html.twig' %}
+
+{% set form = {
+ 'order': {
+ 'name': "documentsList",
+ }
+} %}
+
+{% block infoline %}
+ {% set infoline = {
+ 'label': _Tn("%count document", "%count documents", nb)|replace({"%count": nb})
+ } %}
+ {{ parent() }}
+{% endblock %}
+
+{% block infoline_actions %}
+ {% if not is_public and (login.isAdmin() or login.isStaff()) %}
+ <a
+ class="ui tiny labeled icon button"
+ href="{{ url_for("addDocument") }}"
+ >
+ <i class="ui user check green icon" aria-hidden="true"></i>
+ {{ _T("Add a document") }}
+ </a>
+ {% endif %}
+{% endblock %}
+
+{% block header %}
+ {% set columns = [
+ {'label': '#'},
+ {'label': _T('Type')},
+ {'label': _T('Visibility')},
+ {'label': _T("Filename")},
+ {'label': _T("Creation date")}
+ ] %}
+
+ {{ parent() }}
+{% endblock %}
+
+{% block body %}
+ {% for document in documents %}
+ <tr class="" id="row_{{ document.id }}">
+ <td data-scope="row">
+ {% if preferences.pref_show_id %}
+ {{ document.id }}
+ {% else %}
+ {{ loop.index }}
+ {% endif %}
+ </td>
+ <td data-col-label="{{ _T("Type") }}">{{ document.getType() }}</td>
+ <td data-col-label="{{ _T("Visibility") }}">{{ document.getPermissionName() }}</td>
+ <td data-col-label="{{ _T("Filename") }}">
+ <a href="{{ url_for('getDocumentFile', {'id': document.getID()}) }}">
+ {{ document.getDocumentFilename() }}
+ </a>
+ </td>
+ <td data-col-label="{{ _T("Creation date") }}">{{ document.getCreationDate() }}</td>
+ {% if mode != 'ajax' %}
+ <td class="actions_row center">
+ {% if (login.isAdmin() or login.isStaff()) %}
+ <a
+ href="{{ url_for("editDocument", {"id": document.getID()}) }}"
+ class="action"
+ >
+ <i class="ui edit icon tooltip" aria-hidden="true"></i>
+ <span class="ui special popup">{{ _T("Edit document") }}</span>
+ </a>
+ <a
+ href="{{ url_for("removeDocument", {"id": document.getID()}) }}"
+ class="delete"
+ >
+ <i class="ui trash red icon tooltip" aria-hidden="true"></i>
+ <span class="ui special popup">{{ _T("Delete document") }}</span>
+ </a>
+ {% endif %}
+ </td>
+ {% endif %}
+ </tr>
+ {% else %}
+ <tr><td colspan="{% if login.isAdmin() or login.isStaff() %}6{% else %}5{% endif %}" class="emptylist">{{ _T("No document") }}</td></tr>
+ {% endfor %}
+{% endblock %}
--- /dev/null
+{#
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+#}
+{% extends 'public_page.html.twig' %}
+
+{% macro getSlug(value) -%}
+ {{ value|lower|spaceless|slug|e('html_attr') }}
+{%- endmacro %}
+
+{% block content %}
+ <div class="ui stackable grid">
+ <div class="four wide left attached column">
+ <div class="ui stackable vertical fluid tabular menu tabbed">
+ {% for type, documents in typed_documents %}
+ <a class="{% if loop.index == 1 %}active {% endif %}item" data-tab="{{ _self.getSlug(type) }}">
+ {{ _T(type) }}
+ <p class="ui small disabled text">({{ _Tn("%count document", "%count documents", documents|length)|replace({"%count": documents|length}) }})</p>
+ </a>
+ {% endfor %}
+ </div>
+ </div>
+ <div class="twelve wide stretched right attached column">
+ {% for type, documents in typed_documents %}
+ <div class="ui seamless right attached tab segment{% if loop.index == 1 %} active{% endif %}" data-tab="{{ _self.getSlug(type) }}">
+ {% for document in documents %}
+ <div class="ui relaxed divided list">
+ <div class="item">
+ <div class="ui tiny image">
+ <i class="large file middle aligned icon"></i>
+ </div>
+ <div class="content">
+ <a class="ui small header" href="{{ url_for('getDocumentFile', {'id': document.getID()}) }}">
+ {{ document.getDocumentFilename() }}
+ </a>
+ <div class="description">
+ {% if document.getComment %}{{ document.getComment()|raw }}{% endif %}
+ </div>
+ <div class="extra">
+ <div class="ui right floated meta">
+ <span class="date ui small text">
+ <i class="clock outline icon"></i>
+ {{ document.getCreationDate() }}
+ </span>
+ <div class="ui label">{{ document.getPermissionName() }}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+ {% endfor %}
+ </div>
+ </div>
+{% endblock %}
'galette_pdfmodels',
'galette_preferences',
'galette_searches',
- 'galette_tmplinks'
+ 'galette_tmplinks',
+ 'galette_documents'
);
$tables = $this->db->getTables();
$login->method('isSuperAdmin')->willReturn(true);
$dashboards = \Galette\Core\Galette::getDashboards();
- $this->assertCount(8, $dashboards);
+ $this->assertCount(9, $dashboards);
$login = $this->getMockBuilder(\Galette\Core\Login::class)
->setConstructorArgs(array($db, new \Galette\Core\I18n()))
--- /dev/null
+<?php
+
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Galette\Entity\test\units;
+
+use Galette\GaletteTestCase;
+
+/**
+ * Status tests
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ */
+class Document extends GaletteTestCase
+{
+ protected int $seed = 20240312213127;
+
+ /**
+ * Tear down tests
+ *
+ * @return void
+ */
+ public function tearDown(): void
+ {
+ parent::tearDown();
+
+ $this->deleteDocuments();
+
+ //drop dynamic translations
+ $delete = $this->zdb->delete(\Galette\Core\L10n::TABLE);
+ $this->zdb->execute($delete);
+ }
+
+ /**
+ * Delete documents
+ *
+ * @return void
+ */
+ private function deleteDocuments(): void
+ {
+ $delete = $this->zdb->delete(\Galette\Entity\Document::TABLE);
+ $this->zdb->execute($delete);
+ }
+
+ /**
+ * Test document object
+ *
+ * @return void
+ */
+ public function testObject(): void
+ {
+ $document = new \Galette\Entity\Document($this->zdb);
+
+ //getters only
+ $this->assertSame('', $document->getDocumentFilename());
+ $this->assertSame($document->getDestDir(), $document->getURL());
+ $this->assertNull($document->getID());
+
+ //setters and getters
+ $this->assertSame('', $document->getType());
+ $this->assertInstanceOf(\Galette\Entity\Document::class, $document->setType('mytype'));
+ $this->assertSame('mytype', $document->getType());
+
+ $this->assertNull($document->getComment());
+ $this->assertInstanceOf(\Galette\Entity\Document::class, $document->setComment('any comment'));
+ $this->assertSame('any comment', $document->getComment());
+ }
+
+ /**
+ * Test document "system" types
+ *
+ * @return void
+ */
+ public function testGetSystemTypes(): void
+ {
+ $document = new \Galette\Entity\Document($this->zdb);
+ $this->assertCount(5, $document->getSystemTypes());
+ }
+
+ //FIXME: not possible to test real document, since all relies on a file upload...
+
+ /**
+ * Get mocked document instance
+ *
+ * @return \Galette\Entity\Document
+ */
+ private function getDocumentInstance(): \Galette\Entity\Document
+ {
+ $document = $this->getMockBuilder(\Galette\Entity\Document::class)
+ ->setConstructorArgs(array($this->zdb))
+ ->onlyMethods(array('handleFiles'))
+ ->getMock();
+
+ $document->method('handleFiles')
+ ->willReturnCallback(
+ function (array $files) use ($document) {
+ $reflection = new \ReflectionClass(\Galette\Entity\Document::class);
+ $reflection_property = $reflection->getProperty('filename');
+ $reflection_property->setAccessible(true);
+ $reflection_property->setValue($document, $files['document_file']['name']);
+
+ return true;
+ }
+ );
+ return $document;
+ }
+
+ /**
+ * Test getList
+ *
+ * @return void
+ */
+ public function testGetList(): void
+ {
+ $document = $this->getDocumentInstance();
+
+ // no document yet, list is empty
+ $this->assertSame([], $document->getList());
+
+ $_FILES['document_file'] = [
+ 'error' => UPLOAD_ERR_OK,
+ 'name' => 'status.pdf',
+ 'tmp_name' => '/tmp/status.pdf',
+ 'size' => 2048
+ ];
+ $post = [
+ 'document_type' => \Galette\Entity\Document::STATUS,
+ 'comment' => 'Status of the association',
+ 'visible' => \Galette\Entity\FieldsConfig::ALL
+ ];
+
+ $this->assertTrue($document->store($post, $_FILES));
+
+ //test list
+ $list = $document->getList();
+ $this->assertCount(1, $list);
+
+ $entry = array_pop($list);
+ $this->assertSame('status.pdf', $entry->getDocumentFilename());
+ $this->assertSame(\Galette\Entity\Document::STATUS, $entry->getType());
+ $this->assertSame('Status of the association', $entry->getComment());
+ $this->assertSame(\Galette\Entity\FieldsConfig::ALL, $entry->getPermission());
+ $this->assertSame('Public', $entry->getPermissionName());
+
+ //test list by type (for public pages)
+ $tlist = $document->getTypedList();
+ $this->assertCount(1, $tlist);
+ $this->assertArrayHasKey(\Galette\Entity\Document::STATUS, $tlist);
+ $this->assertCount(1, $tlist[\Galette\Entity\Document::STATUS]);
+
+ //"upload" another document
+ $document = $this->getDocumentInstance();
+ $_FILES['document_file'] = [
+ 'error' => UPLOAD_ERR_OK,
+ 'name' => 'afile.pdf',
+ 'tmp_name' => '/tmp/afile.pdf',
+ 'size' => 4096
+ ];
+ $post = [
+ 'document_type' => 'An other document type',
+ 'comment' => '',
+ 'visible' => \Galette\Entity\FieldsConfig::ADMIN
+ ];
+
+ $this->assertTrue($document->store($post, $_FILES));
+
+ //test list - not authenticated
+ $list = $document->getList();
+ $this->assertCount(1, $list);
+
+ //test list - authenticated
+ $this->logSuperAdmin();
+ $list = $document->getList();
+ $this->assertCount(2, $list);
+
+ //test list by type (for public pages)
+ $tlist = $document->getTypedList();
+ $this->assertCount(2, $tlist);
+ $this->assertArrayHasKey(\Galette\Entity\Document::STATUS, $tlist);
+ $this->assertArrayHasKey('An other document type', $tlist);
+ $this->assertCount(1, $tlist[\Galette\Entity\Document::STATUS]);
+ $this->assertCount(1, $tlist['An other document type']);
+ }
+}
Vertical
---------------*/
- .ui.vertical.menu {
+ .ui.vertical.menu:not(.tabular) {
display: block;
flex-direction: column;
background: @verticalBackground;
box-shadow: @verticalBoxShadow;
}
- .ui.vertical.menu {
+ .ui.vertical.menu:not(.tabular) {
border: @border;
}
- .ui.vertical.menu div.item .ui.button {
+ .ui.vertical.menu:not(.tabular) div.item .ui.button {
width: 100%;
border: @border;
margin: 0 0 .4rem 0;
box-shadow: none;
}
- .ui.vertical.menu div.item .item.button::before {
+ .ui.vertical.menu:not(.tabular) div.item .item.button::before {
content: none;
}
.ui.vertical.accordion.menu {