aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-10-27 19:29:43 +0100
committerGitHub <noreply@github.com>2020-10-27 19:29:43 +0100
commite6215a2ad97182efcf88ef532ec6bd65ae35fd19 (patch)
tree33a1619091366c8a0cf0c772ba644a75ce61764f
parent034c1ce5269956ec43448f9fd2959e094c861004 (diff)
parent0cf76ccb4736473a958d9fd36ed914e2d25d594a (diff)
downloadShaarli-e6215a2ad97182efcf88ef532ec6bd65ae35fd19.tar.gz
Shaarli-e6215a2ad97182efcf88ef532ec6bd65ae35fd19.tar.zst
Shaarli-e6215a2ad97182efcf88ef532ec6bd65ae35fd19.zip
Merge pull request #1604 from ArthurHoaro/feature/server-admin-page
Feature: add a Server administration page
-rw-r--r--application/ApplicationUtils.php93
-rw-r--r--application/FileUtils.php56
-rw-r--r--application/front/controller/admin/ServerController.php87
-rw-r--r--application/front/controller/visitor/BookmarkListController.php28
-rw-r--r--application/front/controller/visitor/InstallController.php12
-rw-r--r--assets/default/scss/shaarli.scss56
-rw-r--r--inc/languages/fr/LC_MESSAGES/shaarli.po294
-rw-r--r--index.php2
-rw-r--r--tests/ApplicationUtilsTest.php62
-rw-r--r--tests/FileUtilsTest.php88
-rw-r--r--tests/front/controller/admin/ServerControllerTest.php184
-rw-r--r--tests/front/controller/visitor/InstallControllerTest.php9
-rw-r--r--tpl/default/install.html10
-rw-r--r--tpl/default/server.html129
-rw-r--r--tpl/default/server.requirements.html68
-rw-r--r--tpl/default/tools.html14
16 files changed, 1087 insertions, 105 deletions
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php
index 3aa21829..bd1c7cf3 100644
--- a/application/ApplicationUtils.php
+++ b/application/ApplicationUtils.php
@@ -14,8 +14,9 @@ class ApplicationUtils
14 */ 14 */
15 public static $VERSION_FILE = 'shaarli_version.php'; 15 public static $VERSION_FILE = 'shaarli_version.php';
16 16
17 private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli'; 17 public static $GITHUB_URL = 'https://github.com/shaarli/Shaarli';
18 private static $GIT_BRANCHES = array('latest', 'stable'); 18 public static $GIT_RAW_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
19 public static $GIT_BRANCHES = array('latest', 'stable');
19 private static $VERSION_START_TAG = '<?php /* '; 20 private static $VERSION_START_TAG = '<?php /* ';
20 private static $VERSION_END_TAG = ' */ ?>'; 21 private static $VERSION_END_TAG = ' */ ?>';
21 22
@@ -125,7 +126,7 @@ class ApplicationUtils
125 // Late Static Binding allows overriding within tests 126 // Late Static Binding allows overriding within tests
126 // See http://php.net/manual/en/language.oop5.late-static-bindings.php 127 // See http://php.net/manual/en/language.oop5.late-static-bindings.php
127 $latestVersion = static::getVersion( 128 $latestVersion = static::getVersion(
128 self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE 129 self::$GIT_RAW_URL . '/' . $branch . '/' . self::$VERSION_FILE
129 ); 130 );
130 131
131 if (!$latestVersion) { 132 if (!$latestVersion) {
@@ -171,35 +172,45 @@ class ApplicationUtils
171 /** 172 /**
172 * Checks Shaarli has the proper access permissions to its resources 173 * Checks Shaarli has the proper access permissions to its resources
173 * 174 *
174 * @param ConfigManager $conf Configuration Manager instance. 175 * @param ConfigManager $conf Configuration Manager instance.
176 * @param bool $minimalMode In minimal mode we only check permissions to be able to display a template.
177 * Currently we only need to be able to read the theme and write in raintpl cache.
175 * 178 *
176 * @return array A list of the detected configuration issues 179 * @return array A list of the detected configuration issues
177 */ 180 */
178 public static function checkResourcePermissions($conf) 181 public static function checkResourcePermissions(ConfigManager $conf, bool $minimalMode = false): array
179 { 182 {
180 $errors = array(); 183 $errors = [];
181 $rainTplDir = rtrim($conf->get('resource.raintpl_tpl'), '/'); 184 $rainTplDir = rtrim($conf->get('resource.raintpl_tpl'), '/');
182 185
183 // Check script and template directories are readable 186 // Check script and template directories are readable
184 foreach (array( 187 foreach ([
185 'application', 188 'application',
186 'inc', 189 'inc',
187 'plugins', 190 'plugins',
188 $rainTplDir, 191 $rainTplDir,
189 $rainTplDir . '/' . $conf->get('resource.theme'), 192 $rainTplDir . '/' . $conf->get('resource.theme'),
190 ) as $path) { 193 ] as $path) {
191 if (!is_readable(realpath($path))) { 194 if (!is_readable(realpath($path))) {
192 $errors[] = '"' . $path . '" ' . t('directory is not readable'); 195 $errors[] = '"' . $path . '" ' . t('directory is not readable');
193 } 196 }
194 } 197 }
195 198
196 // Check cache and data directories are readable and writable 199 // Check cache and data directories are readable and writable
197 foreach (array( 200 if ($minimalMode) {
198 $conf->get('resource.thumbnails_cache'), 201 $folders = [
199 $conf->get('resource.data_dir'), 202 $conf->get('resource.raintpl_tmp'),
200 $conf->get('resource.page_cache'), 203 ];
201 $conf->get('resource.raintpl_tmp'), 204 } else {
202 ) as $path) { 205 $folders = [
206 $conf->get('resource.thumbnails_cache'),
207 $conf->get('resource.data_dir'),
208 $conf->get('resource.page_cache'),
209 $conf->get('resource.raintpl_tmp'),
210 ];
211 }
212
213 foreach ($folders as $path) {
203 if (!is_readable(realpath($path))) { 214 if (!is_readable(realpath($path))) {
204 $errors[] = '"' . $path . '" ' . t('directory is not readable'); 215 $errors[] = '"' . $path . '" ' . t('directory is not readable');
205 } 216 }
@@ -208,6 +219,10 @@ class ApplicationUtils
208 } 219 }
209 } 220 }
210 221
222 if ($minimalMode) {
223 return $errors;
224 }
225
211 // Check configuration files are readable and writable 226 // Check configuration files are readable and writable
212 foreach (array( 227 foreach (array(
213 $conf->getConfigFileExt(), 228 $conf->getConfigFileExt(),
@@ -246,4 +261,54 @@ class ApplicationUtils
246 { 261 {
247 return hash_hmac('sha256', $currentVersion, $salt); 262 return hash_hmac('sha256', $currentVersion, $salt);
248 } 263 }
264
265 /**
266 * Get a list of PHP extensions used by Shaarli.
267 *
268 * @return array[] List of extension with following keys:
269 * - name: extension name
270 * - required: whether the extension is required to use Shaarli
271 * - desc: short description of extension usage in Shaarli
272 * - loaded: whether the extension is properly loaded or not
273 */
274 public static function getPhpExtensionsRequirement(): array
275 {
276 $extensions = [
277 ['name' => 'json', 'required' => true, 'desc' => t('Configuration parsing')],
278 ['name' => 'simplexml', 'required' => true, 'desc' => t('Slim Framework (routing, etc.)')],
279 ['name' => 'mbstring', 'required' => true, 'desc' => t('Multibyte (Unicode) string support')],
280 ['name' => 'gd', 'required' => false, 'desc' => t('Required to use thumbnails')],
281 ['name' => 'intl', 'required' => false, 'desc' => t('Localized text sorting (e.g. e->รจ->f)')],
282 ['name' => 'curl', 'required' => false, 'desc' => t('Better retrieval of bookmark metadata and thumbnail')],
283 ['name' => 'gettext', 'required' => false, 'desc' => t('Use the translation system in gettext mode')],
284 ['name' => 'ldap', 'required' => false, 'desc' => t('Login using LDAP server')],
285 ];
286
287 foreach ($extensions as &$extension) {
288 $extension['loaded'] = extension_loaded($extension['name']);
289 }
290
291 return $extensions;
292 }
293
294 /**
295 * Return the EOL date of given PHP version. If the version is unknown,
296 * we return today + 2 years.
297 *
298 * @param string $fullVersion PHP version, e.g. 7.4.7
299 *
300 * @return string Date format: YYYY-MM-DD
301 */
302 public static function getPhpEol(string $fullVersion): string
303 {
304 preg_match('/(\d+\.\d+)\.\d+/', $fullVersion, $matches);
305
306 return [
307 '7.1' => '2019-12-01',
308 '7.2' => '2020-11-30',
309 '7.3' => '2021-12-06',
310 '7.4' => '2022-11-28',
311 '8.0' => '2023-12-01',
312 ][$matches[1]] ?? (new \DateTime('+2 year'))->format('Y-m-d');
313 }
249} 314}
diff --git a/application/FileUtils.php b/application/FileUtils.php
index 30560bfc..3f940751 100644
--- a/application/FileUtils.php
+++ b/application/FileUtils.php
@@ -81,4 +81,60 @@ class FileUtils
81 ) 81 )
82 ); 82 );
83 } 83 }
84
85 /**
86 * Recursively deletes a folder content, and deletes itself optionally.
87 * If an excluded file is found, folders won't be deleted.
88 *
89 * Additional security: raise an exception if it tries to delete a folder outside of Shaarli directory.
90 *
91 * @param string $path
92 * @param bool $selfDelete Delete the provided folder if true, only its content if false.
93 * @param array $exclude
94 */
95 public static function clearFolder(string $path, bool $selfDelete, array $exclude = []): bool
96 {
97 $skipped = false;
98
99 if (!is_dir($path)) {
100 throw new IOException(t('Provided path is not a directory.'));
101 }
102
103 if (!static::isPathInShaarliFolder($path)) {
104 throw new IOException(t('Trying to delete a folder outside of Shaarli path.'));
105 }
106
107 foreach (new \DirectoryIterator($path) as $file) {
108 if($file->isDot()) {
109 continue;
110 }
111
112 if (in_array($file->getBasename(), $exclude, true)) {
113 $skipped = true;
114 continue;
115 }
116
117 if ($file->isFile()) {
118 unlink($file->getPathname());
119 } elseif($file->isDir()) {
120 $skipped = static::clearFolder($file->getRealPath(), true, $exclude) || $skipped;
121 }
122 }
123
124 if ($selfDelete && !$skipped) {
125 rmdir($path);
126 }
127
128 return $skipped;
129 }
130
131 /**
132 * Checks that the given path is inside Shaarli directory.
133 */
134 public static function isPathInShaarliFolder(string $path): bool
135 {
136 $rootDirectory = dirname(dirname(__FILE__));
137
138 return strpos(realpath($path), $rootDirectory) !== false;
139 }
84} 140}
diff --git a/application/front/controller/admin/ServerController.php b/application/front/controller/admin/ServerController.php
new file mode 100644
index 00000000..85654a43
--- /dev/null
+++ b/application/front/controller/admin/ServerController.php
@@ -0,0 +1,87 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\ApplicationUtils;
8use Shaarli\FileUtils;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12/**
13 * Slim controller used to handle Server administration page, and actions.
14 */
15class ServerController extends ShaarliAdminController
16{
17 /** @var string Cache type - main - by default pagecache/ and tmp/ */
18 protected const CACHE_MAIN = 'main';
19
20 /** @var string Cache type - thumbnails - by default cache/ */
21 protected const CACHE_THUMB = 'thumbnails';
22
23 /**
24 * GET /admin/server - Display page Server administration
25 */
26 public function index(Request $request, Response $response): Response
27 {
28 $latestVersion = 'v' . ApplicationUtils::getVersion(
29 ApplicationUtils::$GIT_RAW_URL . '/latest/' . ApplicationUtils::$VERSION_FILE
30 );
31 $currentVersion = ApplicationUtils::getVersion('./shaarli_version.php');
32 $currentVersion = $currentVersion === 'dev' ? $currentVersion : 'v' . $currentVersion;
33 $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION));
34
35 $this->assignView('php_version', PHP_VERSION);
36 $this->assignView('php_eol', format_date($phpEol, false));
37 $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable());
38 $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement());
39 $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf));
40 $this->assignView('release_url', ApplicationUtils::$GITHUB_URL . '/releases/tag/' . $latestVersion);
41 $this->assignView('latest_version', $latestVersion);
42 $this->assignView('current_version', $currentVersion);
43 $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode'));
44 $this->assignView('index_url', index_url($this->container->environment));
45 $this->assignView('client_ip', client_ip_id($this->container->environment));
46 $this->assignView('trusted_proxies', $this->container->conf->get('security.trusted_proxies', []));
47
48 $this->assignView(
49 'pagetitle',
50 t('Server administration') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')
51 );
52
53 return $response->write($this->render('server'));
54 }
55
56 /**
57 * GET /admin/clear-cache?type={$type} - Action to trigger cache folder clearing (either main or thumbnails).
58 */
59 public function clearCache(Request $request, Response $response): Response
60 {
61 $exclude = ['.htaccess'];
62
63 if ($request->getQueryParam('type') === static::CACHE_THUMB) {
64 $folders = [$this->container->conf->get('resource.thumbnails_cache')];
65
66 $this->saveWarningMessage(
67 t('Thumbnails cache has been cleared.') . ' ' .
68 '<a href="'. $this->container->basePath .'/admin/thumbnails">' . t('Please synchronize them.') .'</a>'
69 );
70 } else {
71 $folders = [
72 $this->container->conf->get('resource.page_cache'),
73 $this->container->conf->get('resource.raintpl_tmp'),
74 ];
75
76 $this->saveSuccessMessage(t('Shaarli\'s cache folder has been cleared!'));
77 }
78
79 // Make sure that we don't delete root cache folder
80 $folders = array_map('realpath', array_values(array_filter(array_map('trim', $folders))));
81 foreach ($folders as $folder) {
82 FileUtils::clearFolder($folder, false, $exclude);
83 }
84
85 return $this->redirect($response, '/admin/server');
86 }
87}
diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php
index a8019ead..5267c8f5 100644
--- a/application/front/controller/visitor/BookmarkListController.php
+++ b/application/front/controller/visitor/BookmarkListController.php
@@ -169,16 +169,24 @@ class BookmarkListController extends ShaarliVisitorController
169 */ 169 */
170 protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool 170 protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool
171 { 171 {
172 // Logged in, not async retrieval, thumbnails enabled, and thumbnail should be updated 172 if (false === $this->container->loginManager->isLoggedIn()) {
173 if ($this->container->loginManager->isLoggedIn() 173 return false;
174 && true !== $this->container->conf->get('general.enable_async_metadata', true) 174 }
175 && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE 175
176 && $bookmark->shouldUpdateThumbnail() 176 // If thumbnail should be updated, we reset it to null
177 ) { 177 if ($bookmark->shouldUpdateThumbnail()) {
178 $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); 178 $bookmark->setThumbnail(null);
179 $this->container->bookmarkService->set($bookmark, $writeDatastore); 179
180 180 // Requires an update, not async retrieval, thumbnails enabled
181 return true; 181 if ($bookmark->shouldUpdateThumbnail()
182 && true !== $this->container->conf->get('general.enable_async_metadata', true)
183 && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
184 ) {
185 $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl()));
186 $this->container->bookmarkService->set($bookmark, $writeDatastore);
187
188 return true;
189 }
182 } 190 }
183 191
184 return false; 192 return false;
diff --git a/application/front/controller/visitor/InstallController.php b/application/front/controller/visitor/InstallController.php
index 7cb32777..564a5777 100644
--- a/application/front/controller/visitor/InstallController.php
+++ b/application/front/controller/visitor/InstallController.php
@@ -53,6 +53,16 @@ class InstallController extends ShaarliVisitorController
53 $this->assignView('cities', $cities); 53 $this->assignView('cities', $cities);
54 $this->assignView('languages', Languages::getAvailableLanguages()); 54 $this->assignView('languages', Languages::getAvailableLanguages());
55 55
56 $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION));
57
58 $this->assignView('php_version', PHP_VERSION);
59 $this->assignView('php_eol', format_date($phpEol, false));
60 $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable());
61 $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement());
62 $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf));
63
64 $this->assignView('pagetitle', t('Install Shaarli'));
65
56 return $response->write($this->render('install')); 66 return $response->write($this->render('install'));
57 } 67 }
58 68
@@ -150,7 +160,7 @@ class InstallController extends ShaarliVisitorController
150 protected function checkPermissions(): bool 160 protected function checkPermissions(): bool
151 { 161 {
152 // Ensure Shaarli has proper access to its resources 162 // Ensure Shaarli has proper access to its resources
153 $errors = ApplicationUtils::checkResourcePermissions($this->container->conf); 163 $errors = ApplicationUtils::checkResourcePermissions($this->container->conf, true);
154 if (empty($errors)) { 164 if (empty($errors)) {
155 return true; 165 return true;
156 } 166 }
diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss
index 286ac83b..7dc61903 100644
--- a/assets/default/scss/shaarli.scss
+++ b/assets/default/scss/shaarli.scss
@@ -1047,7 +1047,7 @@ body,
1047 } 1047 }
1048 1048
1049 table { 1049 table {
1050 margin: auto; 1050 margin: 10px auto 25px auto;
1051 width: 90%; 1051 width: 90%;
1052 1052
1053 .order { 1053 .order {
@@ -1696,6 +1696,60 @@ form {
1696 } 1696 }
1697} 1697}
1698 1698
1699// SERVER PAGE
1700
1701.server-tables-page,
1702.server-tables {
1703 .window-subtitle {
1704 &::before {
1705 display: block;
1706 margin: 8px auto;
1707 background: linear-gradient(to right, var(--background-color), $dark-grey, var(--background-color));
1708 width: 50%;
1709 height: 1px;
1710 content: '';
1711 }
1712 }
1713
1714 .server-row {
1715 p {
1716 height: 25px;
1717 padding: 0 10px;
1718 }
1719 }
1720
1721 .server-label {
1722 text-align: right;
1723 font-weight: bold;
1724 }
1725
1726 i {
1727 &.fa-color-green {
1728 color: $main-green;
1729 }
1730
1731 &.fa-color-orange {
1732 color: $orange;
1733 }
1734
1735 &.fa-color-red {
1736 color: $red;
1737 }
1738 }
1739
1740 @media screen and (max-width: 64em) {
1741 .server-label {
1742 text-align: center;
1743 }
1744
1745 .server-row {
1746 p {
1747 text-align: center;
1748 }
1749 }
1750 }
1751}
1752
1699// Print rules 1753// Print rules
1700@media print { 1754@media print {
1701 .shaarli-menu { 1755 .shaarli-menu {
diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po
index f7baedfb..db6bfa3e 100644
--- a/inc/languages/fr/LC_MESSAGES/shaarli.po
+++ b/inc/languages/fr/LC_MESSAGES/shaarli.po
@@ -1,8 +1,8 @@
1msgid "" 1msgid ""
2msgstr "" 2msgstr ""
3"Project-Id-Version: Shaarli\n" 3"Project-Id-Version: Shaarli\n"
4"POT-Creation-Date: 2020-10-16 20:01+0200\n" 4"POT-Creation-Date: 2020-10-21 15:00+0200\n"
5"PO-Revision-Date: 2020-10-16 20:02+0200\n" 5"PO-Revision-Date: 2020-10-21 15:06+0200\n"
6"Last-Translator: \n" 6"Last-Translator: \n"
7"Language-Team: Shaarli\n" 7"Language-Team: Shaarli\n"
8"Language: fr_FR\n" 8"Language: fr_FR\n"
@@ -20,7 +20,7 @@ msgstr ""
20"X-Poedit-SearchPath-3: init.php\n" 20"X-Poedit-SearchPath-3: init.php\n"
21"X-Poedit-SearchPath-4: plugins\n" 21"X-Poedit-SearchPath-4: plugins\n"
22 22
23#: application/ApplicationUtils.php:161 23#: application/ApplicationUtils.php:162
24#, php-format 24#, php-format
25msgid "" 25msgid ""
26"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus " 26"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus "
@@ -31,22 +31,62 @@ msgstr ""
31"peut donc pas fonctionner. Votre version de PHP a des failles de sรฉcuritรฉs " 31"peut donc pas fonctionner. Votre version de PHP a des failles de sรฉcuritรฉs "
32"connues et devrait รชtre mise ร  jour au plus tรดt." 32"connues et devrait รชtre mise ร  jour au plus tรดt."
33 33
34#: application/ApplicationUtils.php:192 application/ApplicationUtils.php:204 34#: application/ApplicationUtils.php:195 application/ApplicationUtils.php:215
35msgid "directory is not readable" 35msgid "directory is not readable"
36msgstr "le rรฉpertoire n'est pas accessible en lecture" 36msgstr "le rรฉpertoire n'est pas accessible en lecture"
37 37
38#: application/ApplicationUtils.php:207 38#: application/ApplicationUtils.php:218
39msgid "directory is not writable" 39msgid "directory is not writable"
40msgstr "le rรฉpertoire n'est pas accessible en รฉcriture" 40msgstr "le rรฉpertoire n'est pas accessible en รฉcriture"
41 41
42#: application/ApplicationUtils.php:225 42#: application/ApplicationUtils.php:240
43msgid "file is not readable" 43msgid "file is not readable"
44msgstr "le fichier n'est pas accessible en lecture" 44msgstr "le fichier n'est pas accessible en lecture"
45 45
46#: application/ApplicationUtils.php:228 46#: application/ApplicationUtils.php:243
47msgid "file is not writable" 47msgid "file is not writable"
48msgstr "le fichier n'est pas accessible en รฉcriture" 48msgstr "le fichier n'est pas accessible en รฉcriture"
49 49
50#: application/ApplicationUtils.php:277
51msgid "Configuration parsing"
52msgstr "Chargement de la configuration"
53
54#: application/ApplicationUtils.php:278
55msgid "Slim Framework (routing, etc.)"
56msgstr "Slim Framwork (routage, etc.)"
57
58#: application/ApplicationUtils.php:279
59msgid "Multibyte (Unicode) string support"
60msgstr "Support des chaรฎnes de caractรจre multibytes (Unicode)"
61
62#: application/ApplicationUtils.php:280
63msgid "Required to use thumbnails"
64msgstr "Obligatoire pour utiliser les miniatures"
65
66#: application/ApplicationUtils.php:281
67msgid "Localized text sorting (e.g. e->รจ->f)"
68msgstr "Tri des textes traduits (ex : e->รจ->f)"
69
70#: application/ApplicationUtils.php:282
71msgid "Better retrieval of bookmark metadata and thumbnail"
72msgstr "Meilleure rรฉcupรฉration des meta-donnรฉes des marque-pages et minatures"
73
74#: application/ApplicationUtils.php:283
75msgid "Use the translation system in gettext mode"
76msgstr "Utiliser le systรจme de traduction en mode gettext"
77
78#: application/ApplicationUtils.php:284
79msgid "Login using LDAP server"
80msgstr "Authentification via un serveur LDAP"
81
82#: application/FileUtils.php:100
83msgid "Provided path is not a directory."
84msgstr "Le chemin fourni n'est pas un dossier."
85
86#: application/FileUtils.php:104
87msgid "Trying to delete a folder outside of Shaarli path."
88msgstr "Tentative de supprimer un dossier en dehors du chemin de Shaarli."
89
50#: application/History.php:179 90#: application/History.php:179
51msgid "History file isn't readable or writable" 91msgid "History file isn't readable or writable"
52msgstr "Le fichier d'historique n'est pas accessible en lecture ou en รฉcriture" 92msgstr "Le fichier d'historique n'est pas accessible en lecture ou en รฉcriture"
@@ -330,12 +370,13 @@ msgid "You have enabled or changed thumbnails mode."
330msgstr "Vous avez activรฉ ou changรฉ le mode de miniatures." 370msgstr "Vous avez activรฉ ou changรฉ le mode de miniatures."
331 371
332#: application/front/controller/admin/ConfigureController.php:103 372#: application/front/controller/admin/ConfigureController.php:103
373#: application/front/controller/admin/ServerController.php:68
333#: application/legacy/LegacyUpdater.php:538 374#: application/legacy/LegacyUpdater.php:538
334msgid "Please synchronize them." 375msgid "Please synchronize them."
335msgstr "Merci de les synchroniser." 376msgstr "Merci de les synchroniser."
336 377
337#: application/front/controller/admin/ConfigureController.php:113 378#: application/front/controller/admin/ConfigureController.php:113
338#: application/front/controller/visitor/InstallController.php:136 379#: application/front/controller/visitor/InstallController.php:146
339msgid "Error while writing config file after configuration update." 380msgid "Error while writing config file after configuration update."
340msgstr "" 381msgstr ""
341"Une erreur s'est produite lors de la sauvegarde du fichier de configuration." 382"Une erreur s'est produite lors de la sauvegarde du fichier de configuration."
@@ -377,33 +418,33 @@ msgstr ""
377msgid "Shaare a new link" 418msgid "Shaare a new link"
378msgstr "Partager un nouveau lien" 419msgstr "Partager un nouveau lien"
379 420
380#: application/front/controller/admin/ManageShaareController.php:78 421#: application/front/controller/admin/ManageShaareController.php:64
381msgid "Note: " 422msgid "Note: "
382msgstr "Note : " 423msgstr "Note : "
383 424
384#: application/front/controller/admin/ManageShaareController.php:109 425#: application/front/controller/admin/ManageShaareController.php:95
385#: application/front/controller/admin/ManageShaareController.php:206 426#: application/front/controller/admin/ManageShaareController.php:193
386#: application/front/controller/admin/ManageShaareController.php:275 427#: application/front/controller/admin/ManageShaareController.php:262
387#: application/front/controller/admin/ManageShaareController.php:315 428#: application/front/controller/admin/ManageShaareController.php:302
388#, php-format 429#, php-format
389msgid "Bookmark with identifier %s could not be found." 430msgid "Bookmark with identifier %s could not be found."
390msgstr "Le lien avec l'identifiant %s n'a pas pu รชtre trouvรฉ." 431msgstr "Le lien avec l'identifiant %s n'a pas pu รชtre trouvรฉ."
391 432
392#: application/front/controller/admin/ManageShaareController.php:194 433#: application/front/controller/admin/ManageShaareController.php:181
393#: application/front/controller/admin/ManageShaareController.php:252 434#: application/front/controller/admin/ManageShaareController.php:239
394msgid "Invalid bookmark ID provided." 435msgid "Invalid bookmark ID provided."
395msgstr "ID du lien non valide." 436msgstr "ID du lien non valide."
396 437
397#: application/front/controller/admin/ManageShaareController.php:260 438#: application/front/controller/admin/ManageShaareController.php:247
398msgid "Invalid visibility provided." 439msgid "Invalid visibility provided."
399msgstr "Visibilitรฉ du lien non valide." 440msgstr "Visibilitรฉ du lien non valide."
400 441
401#: application/front/controller/admin/ManageShaareController.php:363 442#: application/front/controller/admin/ManageShaareController.php:352
402#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:171 443#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:171
403msgid "Edit" 444msgid "Edit"
404msgstr "Modifier" 445msgstr "Modifier"
405 446
406#: application/front/controller/admin/ManageShaareController.php:366 447#: application/front/controller/admin/ManageShaareController.php:355
407#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 448#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
408#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:28 449#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:28
409msgid "Shaare" 450msgid "Shaare"
@@ -411,7 +452,7 @@ msgstr "Shaare"
411 452
412#: application/front/controller/admin/ManageTagController.php:29 453#: application/front/controller/admin/ManageTagController.php:29
413#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 454#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
414#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 455#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
415msgid "Manage tags" 456msgid "Manage tags"
416msgstr "Gรฉrer les tags" 457msgstr "Gรฉrer les tags"
417 458
@@ -435,7 +476,7 @@ msgstr[1] "Le tag a รฉtรฉ renommรฉ dans %d liens."
435 476
436#: application/front/controller/admin/PasswordController.php:28 477#: application/front/controller/admin/PasswordController.php:28
437#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 478#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
438#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29 479#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
439msgid "Change password" 480msgid "Change password"
440msgstr "Modifier le mot de passe" 481msgstr "Modifier le mot de passe"
441 482
@@ -467,6 +508,20 @@ msgstr ""
467"Une erreur s'est produite lors de la sauvegarde de la configuration des " 508"Une erreur s'est produite lors de la sauvegarde de la configuration des "
468"plugins : " 509"plugins : "
469 510
511#: application/front/controller/admin/ServerController.php:50
512#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
513#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
514msgid "Server administration"
515msgstr "Administration serveur"
516
517#: application/front/controller/admin/ServerController.php:67
518msgid "Thumbnails cache has been cleared."
519msgstr "Le cache des miniatures a รฉtรฉ vidรฉ."
520
521#: application/front/controller/admin/ServerController.php:76
522msgid "Shaarli's cache folder has been cleared!"
523msgstr "Le dossier de cache de Shaarli a รฉtรฉ vidรฉ !"
524
470#: application/front/controller/admin/ThumbnailsController.php:37 525#: application/front/controller/admin/ThumbnailsController.php:37
471#: tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 526#: tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
472msgid "Thumbnails update" 527msgid "Thumbnails update"
@@ -502,9 +557,14 @@ msgstr "Une erreur inattendue s'est produite."
502 557
503#: application/front/controller/visitor/ErrorNotFoundController.php:25 558#: application/front/controller/visitor/ErrorNotFoundController.php:25
504msgid "Requested page could not be found." 559msgid "Requested page could not be found."
505msgstr "" 560msgstr "La page demandรฉe n'a pas pu รชtre trouvรฉe."
506 561
507#: application/front/controller/visitor/InstallController.php:73 562#: application/front/controller/visitor/InstallController.php:64
563#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
564msgid "Install Shaarli"
565msgstr "Installation de Shaarli"
566
567#: application/front/controller/visitor/InstallController.php:83
508#, php-format 568#, php-format
509msgid "" 569msgid ""
510"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the " 570"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
@@ -523,14 +583,14 @@ msgstr ""
523"des cookies. Nous vous recommandons d'accรฉder ร  votre serveur depuis son " 583"des cookies. Nous vous recommandons d'accรฉder ร  votre serveur depuis son "
524"adresse IP ou un <em>Fully Qualified Domain Name</em>.<br>" 584"adresse IP ou un <em>Fully Qualified Domain Name</em>.<br>"
525 585
526#: application/front/controller/visitor/InstallController.php:144 586#: application/front/controller/visitor/InstallController.php:154
527msgid "" 587msgid ""
528"Shaarli is now configured. Please login and start shaaring your bookmarks!" 588"Shaarli is now configured. Please login and start shaaring your bookmarks!"
529msgstr "" 589msgstr ""
530"Shaarli est maintenant configurรฉ. Vous pouvez vous connecter et commencez ร  " 590"Shaarli est maintenant configurรฉ. Vous pouvez vous connecter et commencez ร  "
531"shaare vos liens !" 591"shaare vos liens !"
532 592
533#: application/front/controller/visitor/InstallController.php:158 593#: application/front/controller/visitor/InstallController.php:168
534msgid "Insufficient permissions:" 594msgid "Insufficient permissions:"
535msgstr "Permissions insuffisantes :" 595msgstr "Permissions insuffisantes :"
536 596
@@ -1016,25 +1076,28 @@ msgstr ""
1016"miniatures." 1076"miniatures."
1017 1077
1018#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:328 1078#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:328
1019#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:56 1079#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:122
1020msgid "Synchronize thumbnails" 1080msgid "Synchronize thumbnails"
1021msgstr "Synchroniser les miniatures" 1081msgstr "Synchroniser les miniatures"
1022 1082
1023#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:339 1083#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:339
1024#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30 1084#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
1085#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
1025msgid "All" 1086msgid "All"
1026msgstr "Tous" 1087msgstr "Tous"
1027 1088
1028#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:343 1089#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:343
1090#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106
1029msgid "Only common media hosts" 1091msgid "Only common media hosts"
1030msgstr "Seulement les hรฉbergeurs de mรฉdia connus" 1092msgstr "Seulement les hรฉbergeurs de mรฉdia connus"
1031 1093
1032#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:347 1094#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:347
1095#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110
1033msgid "None" 1096msgid "None"
1034msgstr "Aucune" 1097msgstr "Aucune"
1035 1098
1036#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:355 1099#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:355
1037#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88 1100#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
1038#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139 1101#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
1039#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199 1102#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199
1040msgid "Save" 1103msgid "Save"
@@ -1060,27 +1123,27 @@ msgstr "Tous les liens d'un jour sur une page."
1060msgid "Next day" 1123msgid "Next day"
1061msgstr "Jour suivant" 1124msgstr "Jour suivant"
1062 1125
1063#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18 1126#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
1064msgid "Edit Shaare" 1127msgid "Edit Shaare"
1065msgstr "Modifier le Shaare" 1128msgstr "Modifier le Shaare"
1066 1129
1067#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18 1130#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
1068msgid "New Shaare" 1131msgid "New Shaare"
1069msgstr "Nouveau Shaare" 1132msgstr "Nouveau Shaare"
1070 1133
1071#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26 1134#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
1072msgid "Created:" 1135msgid "Created:"
1073msgstr "Crรฉation :" 1136msgstr "Crรฉation :"
1074 1137
1075#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29 1138#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:32
1076msgid "URL" 1139msgid "URL"
1077msgstr "URL" 1140msgstr "URL"
1078 1141
1079#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35 1142#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:38
1080msgid "Title" 1143msgid "Title"
1081msgstr "Titre" 1144msgstr "Titre"
1082 1145
1083#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41 1146#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:49
1084#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 1147#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
1085#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75 1148#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75
1086#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99 1149#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
@@ -1088,33 +1151,33 @@ msgstr "Titre"
1088msgid "Description" 1151msgid "Description"
1089msgstr "Description" 1152msgstr "Description"
1090 1153
1091#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47 1154#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
1092msgid "Tags" 1155msgid "Tags"
1093msgstr "Tags" 1156msgstr "Tags"
1094 1157
1095#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:60 1158#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
1096#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35 1159#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
1097#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169 1160#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
1098msgid "Private" 1161msgid "Private"
1099msgstr "Privรฉ" 1162msgstr "Privรฉ"
1100 1163
1101#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66 1164#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:80
1102msgid "Description will be rendered with" 1165msgid "Description will be rendered with"
1103msgstr "La description sera gรฉnรฉrรฉe avec" 1166msgstr "La description sera gรฉnรฉrรฉe avec"
1104 1167
1105#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:68 1168#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82
1106msgid "Markdown syntax documentation" 1169msgid "Markdown syntax documentation"
1107msgstr "Documentation sur la syntaxe Markdown" 1170msgstr "Documentation sur la syntaxe Markdown"
1108 1171
1109#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69 1172#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
1110msgid "Markdown syntax" 1173msgid "Markdown syntax"
1111msgstr "la syntaxe Markdown" 1174msgstr "la syntaxe Markdown"
1112 1175
1113#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88 1176#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
1114msgid "Apply Changes" 1177msgid "Apply Changes"
1115msgstr "Appliquer les changements" 1178msgstr "Appliquer les changements"
1116 1179
1117#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:93 1180#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:107
1118#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:173 1181#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:173
1119#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147 1182#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147
1120#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:147 1183#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:147
@@ -1179,10 +1242,6 @@ msgstr "Les doublons s'appuient sur les URL"
1179msgid "Add default tags" 1242msgid "Add default tags"
1180msgstr "Ajouter des tags par dรฉfaut" 1243msgstr "Ajouter des tags par dรฉfaut"
1181 1244
1182#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
1183msgid "Install Shaarli"
1184msgstr "Installation de Shaarli"
1185
1186#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25 1245#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
1187msgid "It looks like it's the first time you run Shaarli. Please configure it." 1246msgid "It looks like it's the first time you run Shaarli. Please configure it."
1188msgstr "" 1247msgstr ""
@@ -1215,6 +1274,10 @@ msgstr "Mes liens"
1215msgid "Install" 1274msgid "Install"
1216msgstr "Installer" 1275msgstr "Installer"
1217 1276
1277#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:190
1278msgid "Server requirements"
1279msgstr "Prรฉ-requis serveur"
1280
1218#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 1281#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
1219#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79 1282#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79
1220msgid "shaare" 1283msgid "shaare"
@@ -1511,6 +1574,100 @@ msgstr "Configuration des extensions"
1511msgid "No parameter available." 1574msgid "No parameter available."
1512msgstr "Aucun paramรจtre disponible." 1575msgstr "Aucun paramรจtre disponible."
1513 1576
1577#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
1578msgid "General"
1579msgstr "Gรฉnรฉral"
1580
1581#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:20
1582msgid "Index URL"
1583msgstr "URL de l'index"
1584
1585#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
1586msgid "Base path"
1587msgstr "Chemin de base"
1588
1589#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
1590msgid "Client IP"
1591msgstr "IP du client"
1592
1593#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
1594msgid "Trusted reverse proxies"
1595msgstr "Reverse proxies de confiance"
1596
1597#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
1598msgid "N/A"
1599msgstr "N/A"
1600
1601#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:84
1602msgid "Visit releases page on Github"
1603msgstr "Visiter la page des releases sur Github"
1604
1605#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:121
1606msgid "Synchronize all link thumbnails"
1607msgstr "Synchroniser toutes les miniatures"
1608
1609#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:2
1610#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:2
1611msgid "Permissions"
1612msgstr "Permissions"
1613
1614#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:8
1615#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:8
1616msgid "There are permissions that need to be fixed."
1617msgstr "Il y a des permissions qui doivent รชtre corrigรฉes."
1618
1619#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
1620#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:23
1621msgid "All read/write permissions are properly set."
1622msgstr "Toutes les permissions de lecture/รฉcriture sont dรฉfinies correctement."
1623
1624#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:32
1625#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:32
1626msgid "Running PHP"
1627msgstr "Fonctionnant avec PHP"
1628
1629#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
1630#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:36
1631msgid "End of life: "
1632msgstr "Fin de vie : "
1633
1634#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
1635#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:48
1636msgid "Extension"
1637msgstr "Extension"
1638
1639#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:49
1640#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:49
1641msgid "Usage"
1642msgstr "Utilisation"
1643
1644#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:50
1645#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:50
1646msgid "Status"
1647msgstr "Statut"
1648
1649#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51
1650#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66
1651#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:51
1652#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:66
1653msgid "Loaded"
1654msgstr "Chargรฉ"
1655
1656#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:60
1657#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:60
1658msgid "Required"
1659msgstr "Obligatoire"
1660
1661#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:60
1662#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:60
1663msgid "Optional"
1664msgstr "Optionnel"
1665
1666#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:70
1667#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:70
1668msgid "Not loaded"
1669msgstr "Non chargรฉ"
1670
1514#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19 1671#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
1515#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19 1672#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
1516msgid "tags" 1673msgid "tags"
@@ -1561,15 +1718,19 @@ msgstr "Configurer Shaarli"
1561msgid "Enable, disable and configure plugins" 1718msgid "Enable, disable and configure plugins"
1562msgstr "Activer, dรฉsactiver et configurer les extensions" 1719msgstr "Activer, dรฉsactiver et configurer les extensions"
1563 1720
1564#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 1721#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:27
1722msgid "Check instance's server configuration"
1723msgstr "Vรฉrifier la configuration serveur de l'instance"
1724
1725#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
1565msgid "Change your password" 1726msgid "Change your password"
1566msgstr "Modifier le mot de passe" 1727msgstr "Modifier le mot de passe"
1567 1728
1568#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35 1729#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
1569msgid "Rename or delete a tag in all links" 1730msgid "Rename or delete a tag in all links"
1570msgstr "Renommer ou supprimer un tag dans tous les liens" 1731msgstr "Renommer ou supprimer un tag dans tous les liens"
1571 1732
1572#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41 1733#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
1573msgid "" 1734msgid ""
1574"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, " 1735"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
1575"delicious...)" 1736"delicious...)"
@@ -1577,11 +1738,11 @@ msgstr ""
1577"Importer des marques pages au format Netscape HTML (comme exportรฉs depuis " 1738"Importer des marques pages au format Netscape HTML (comme exportรฉs depuis "
1578"Firefox, Chrome, Opera, delicious...)" 1739"Firefox, Chrome, Opera, delicious...)"
1579 1740
1580#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 1741#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
1581msgid "Import links" 1742msgid "Import links"
1582msgstr "Importer des liens" 1743msgstr "Importer des liens"
1583 1744
1584#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47 1745#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:53
1585msgid "" 1746msgid ""
1586"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, " 1747"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
1587"Opera, delicious...)" 1748"Opera, delicious...)"
@@ -1589,15 +1750,11 @@ msgstr ""
1589"Exporter les marques pages au format Netscape HTML (comme exportรฉs depuis " 1750"Exporter les marques pages au format Netscape HTML (comme exportรฉs depuis "
1590"Firefox, Chrome, Opera, delicious...)" 1751"Firefox, Chrome, Opera, delicious...)"
1591 1752
1592#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48 1753#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:54
1593msgid "Export database" 1754msgid "Export database"
1594msgstr "Exporter les donnรฉes" 1755msgstr "Exporter les donnรฉes"
1595 1756
1596#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:55 1757#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
1597msgid "Synchronize all link thumbnails"
1598msgstr "Synchroniser toutes les miniatures"
1599
1600#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:81
1601msgid "" 1758msgid ""
1602"Drag one of these button to your bookmarks toolbar or right-click it and " 1759"Drag one of these button to your bookmarks toolbar or right-click it and "
1603"\"Bookmark This Link\"" 1760"\"Bookmark This Link\""
@@ -1605,13 +1762,13 @@ msgstr ""
1605"Glisser un de ces boutons dans votre barre de favoris ou cliquer droit " 1762"Glisser un de ces boutons dans votre barre de favoris ou cliquer droit "
1606"dessus et ยซ Ajouter aux favoris ยป" 1763"dessus et ยซ Ajouter aux favoris ยป"
1607 1764
1608#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82 1765#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:78
1609msgid "then click on the bookmarklet in any page you want to share." 1766msgid "then click on the bookmarklet in any page you want to share."
1610msgstr "" 1767msgstr ""
1611"puis cliquer sur le marque-page depuis un site que vous souhaitez partager." 1768"puis cliquer sur le marque-page depuis un site que vous souhaitez partager."
1612 1769
1613#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86 1770#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82
1614#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110 1771#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106
1615msgid "" 1772msgid ""
1616"Drag this link to your bookmarks toolbar or right-click it and Bookmark This " 1773"Drag this link to your bookmarks toolbar or right-click it and Bookmark This "
1617"Link" 1774"Link"
@@ -1619,40 +1776,40 @@ msgstr ""
1619"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et ยซ " 1776"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et ยซ "
1620"Ajouter aux favoris ยป" 1777"Ajouter aux favoris ยป"
1621 1778
1622#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87 1779#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
1623msgid "then click โœšShaare link button in any page you want to share" 1780msgid "then click โœšShaare link button in any page you want to share"
1624msgstr "puis cliquer sur โœšShaare depuis un site que vous souhaitez partager" 1781msgstr "puis cliquer sur โœšShaare depuis un site que vous souhaitez partager"
1625 1782
1626#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96 1783#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:92
1627#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118 1784#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:114
1628msgid "The selected text is too long, it will be truncated." 1785msgid "The selected text is too long, it will be truncated."
1629msgstr "Le texte sรฉlectionnรฉ est trop long, il sera tronquรฉ." 1786msgstr "Le texte sรฉlectionnรฉ est trop long, il sera tronquรฉ."
1630 1787
1631#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106 1788#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
1632msgid "Shaare link" 1789msgid "Shaare link"
1633msgstr "Shaare" 1790msgstr "Shaare"
1634 1791
1635#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111 1792#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:107
1636msgid "" 1793msgid ""
1637"Then click โœšAdd Note button anytime to start composing a private Note (text " 1794"Then click โœšAdd Note button anytime to start composing a private Note (text "
1638"post) to your Shaarli" 1795"post) to your Shaarli"
1639msgstr "" 1796msgstr ""
1640"Puis cliquer sur โœšAdd Note pour commencer ร  rรฉdiger une Note sur Shaarli" 1797"Puis cliquer sur โœšAdd Note pour commencer ร  rรฉdiger une Note sur Shaarli"
1641 1798
1642#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127 1799#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123
1643msgid "Add Note" 1800msgid "Add Note"
1644msgstr "Ajouter une Note" 1801msgstr "Ajouter une Note"
1645 1802
1646#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136 1803#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:132
1647msgid "3rd party" 1804msgid "3rd party"
1648msgstr "Applications tierces" 1805msgstr "Applications tierces"
1649 1806
1650#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139 1807#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
1651#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:144 1808#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:140
1652msgid "plugin" 1809msgid "plugin"
1653msgstr "extension" 1810msgstr "extension"
1654 1811
1655#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169 1812#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:165
1656msgid "" 1813msgid ""
1657"Drag this link to your bookmarks toolbar, or right-click it and choose " 1814"Drag this link to your bookmarks toolbar, or right-click it and choose "
1658"Bookmark This Link" 1815"Bookmark This Link"
@@ -1660,9 +1817,6 @@ msgstr ""
1660"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et ยซ " 1817"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et ยซ "
1661"Ajouter aux favoris ยป" 1818"Ajouter aux favoris ยป"
1662 1819
1663#~ msgid "Provided data is invalid"
1664#~ msgstr "Les informations fournies ne sont pas valides"
1665
1666#~ msgid "Rename" 1820#~ msgid "Rename"
1667#~ msgstr "Renommer" 1821#~ msgstr "Renommer"
1668 1822
diff --git a/index.php b/index.php
index 1b10ee41..a46e32c9 100644
--- a/index.php
+++ b/index.php
@@ -143,6 +143,8 @@ $app->group('/admin', function () {
143 $this->get('/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:index'); 143 $this->get('/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:index');
144 $this->post('/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:save'); 144 $this->post('/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:save');
145 $this->get('/token', '\Shaarli\Front\Controller\Admin\TokenController:getToken'); 145 $this->get('/token', '\Shaarli\Front\Controller\Admin\TokenController:getToken');
146 $this->get('/server', '\Shaarli\Front\Controller\Admin\ServerController:index');
147 $this->get('/clear-cache', '\Shaarli\Front\Controller\Admin\ServerController:clearCache');
146 $this->get('/thumbnails', '\Shaarli\Front\Controller\Admin\ThumbnailsController:index'); 148 $this->get('/thumbnails', '\Shaarli\Front\Controller\Admin\ThumbnailsController:index');
147 $this->get('/metadata', '\Shaarli\Front\Controller\Admin\MetadataController:ajaxRetrieveTitle'); 149 $this->get('/metadata', '\Shaarli\Front\Controller\Admin\MetadataController:ajaxRetrieveTitle');
148 $this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility'); 150 $this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility');
diff --git a/tests/ApplicationUtilsTest.php b/tests/ApplicationUtilsTest.php
index a232b351..ac46cbf1 100644
--- a/tests/ApplicationUtilsTest.php
+++ b/tests/ApplicationUtilsTest.php
@@ -340,6 +340,35 @@ class ApplicationUtilsTest extends \Shaarli\TestCase
340 } 340 }
341 341
342 /** 342 /**
343 * Checks resource permissions in minimal mode.
344 */
345 public function testCheckCurrentResourcePermissionsErrorsMinimalMode(): void
346 {
347 $conf = new ConfigManager('');
348 $conf->set('resource.thumbnails_cache', 'null/cache');
349 $conf->set('resource.config', 'null/data/config.php');
350 $conf->set('resource.data_dir', 'null/data');
351 $conf->set('resource.datastore', 'null/data/store.php');
352 $conf->set('resource.ban_file', 'null/data/ipbans.php');
353 $conf->set('resource.log', 'null/data/log.txt');
354 $conf->set('resource.page_cache', 'null/pagecache');
355 $conf->set('resource.raintpl_tmp', 'null/tmp');
356 $conf->set('resource.raintpl_tpl', 'null/tpl');
357 $conf->set('resource.raintpl_theme', 'null/tpl/default');
358 $conf->set('resource.update_check', 'null/data/lastupdatecheck.txt');
359
360 static::assertSame(
361 [
362 '"null/tpl" directory is not readable',
363 '"null/tpl/default" directory is not readable',
364 '"null/tmp" directory is not readable',
365 '"null/tmp" directory is not writable'
366 ],
367 ApplicationUtils::checkResourcePermissions($conf, true)
368 );
369 }
370
371 /**
343 * Check update with 'dev' as curent version (master branch). 372 * Check update with 'dev' as curent version (master branch).
344 * It should always return false. 373 * It should always return false.
345 */ 374 */
@@ -349,4 +378,37 @@ class ApplicationUtilsTest extends \Shaarli\TestCase
349 ApplicationUtils::checkUpdate('dev', self::$testUpdateFile, 100, true, true) 378 ApplicationUtils::checkUpdate('dev', self::$testUpdateFile, 100, true, true)
350 ); 379 );
351 } 380 }
381
382 /**
383 * Basic test of getPhpExtensionsRequirement()
384 */
385 public function testGetPhpExtensionsRequirementSimple(): void
386 {
387 static::assertCount(8, ApplicationUtils::getPhpExtensionsRequirement());
388 static::assertSame([
389 'name' => 'json',
390 'required' => true,
391 'desc' => 'Configuration parsing',
392 'loaded' => true,
393 ], ApplicationUtils::getPhpExtensionsRequirement()[0]);
394 }
395
396 /**
397 * Test getPhpEol with a known version: 7.4 -> 2022
398 */
399 public function testGetKnownPhpEol(): void
400 {
401 static::assertSame('2022-11-28', ApplicationUtils::getPhpEol('7.4.7'));
402 }
403
404 /**
405 * Test getPhpEol with an unknown version: 7.4 -> 2022
406 */
407 public function testGetUnknownPhpEol(): void
408 {
409 static::assertSame(
410 (((int) (new \DateTime())->format('Y')) + 2) . (new \DateTime())->format('-m-d'),
411 ApplicationUtils::getPhpEol('7.51.34')
412 );
413 }
352} 414}
diff --git a/tests/FileUtilsTest.php b/tests/FileUtilsTest.php
index 9163bdf1..3384504a 100644
--- a/tests/FileUtilsTest.php
+++ b/tests/FileUtilsTest.php
@@ -3,25 +3,48 @@
3namespace Shaarli; 3namespace Shaarli;
4 4
5use Exception; 5use Exception;
6use Shaarli\Exceptions\IOException;
6 7
7/** 8/**
8 * Class FileUtilsTest 9 * Class FileUtilsTest
9 * 10 *
10 * Test file utility class. 11 * Test file utility class.
11 */ 12 */
12class FileUtilsTest extends \Shaarli\TestCase 13class FileUtilsTest extends TestCase
13{ 14{
14 /** 15 /**
15 * @var string Test file path. 16 * @var string Test file path.
16 */ 17 */
17 protected static $file = 'sandbox/flat.db'; 18 protected static $file = 'sandbox/flat.db';
18 19
20 protected function setUp(): void
21 {
22 @mkdir('sandbox');
23 mkdir('sandbox/folder2');
24 touch('sandbox/file1');
25 touch('sandbox/file2');
26 mkdir('sandbox/folder1');
27 touch('sandbox/folder1/file1');
28 touch('sandbox/folder1/file2');
29 mkdir('sandbox/folder3');
30 mkdir('/tmp/shaarli-to-delete');
31 }
32
19 /** 33 /**
20 * Delete test file after every test. 34 * Delete test file after every test.
21 */ 35 */
22 protected function tearDown(): void 36 protected function tearDown(): void
23 { 37 {
24 @unlink(self::$file); 38 @unlink(self::$file);
39
40 @unlink('sandbox/folder1/file1');
41 @unlink('sandbox/folder1/file2');
42 @rmdir('sandbox/folder1');
43 @unlink('sandbox/file1');
44 @unlink('sandbox/file2');
45 @rmdir('sandbox/folder2');
46 @rmdir('sandbox/folder3');
47 @rmdir('/tmp/shaarli-to-delete');
25 } 48 }
26 49
27 /** 50 /**
@@ -107,4 +130,67 @@ class FileUtilsTest extends \Shaarli\TestCase
107 $this->assertEquals(null, FileUtils::readFlatDB(self::$file)); 130 $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
108 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test'])); 131 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
109 } 132 }
133
134 /**
135 * Test clearFolder with self delete and excluded files
136 */
137 public function testClearFolderSelfDeleteWithExclusion(): void
138 {
139 FileUtils::clearFolder('sandbox', true, ['file2']);
140
141 static::assertFileExists('sandbox/folder1/file2');
142 static::assertFileExists('sandbox/folder1');
143 static::assertFileExists('sandbox/file2');
144 static::assertFileExists('sandbox');
145
146 static::assertFileNotExists('sandbox/folder1/file1');
147 static::assertFileNotExists('sandbox/file1');
148 static::assertFileNotExists('sandbox/folder3');
149 }
150
151 /**
152 * Test clearFolder with self delete and excluded files
153 */
154 public function testClearFolderSelfDeleteWithoutExclusion(): void
155 {
156 FileUtils::clearFolder('sandbox', true);
157
158 static::assertFileNotExists('sandbox');
159 }
160
161 /**
162 * Test clearFolder with self delete and excluded files
163 */
164 public function testClearFolderNoSelfDeleteWithoutExclusion(): void
165 {
166 FileUtils::clearFolder('sandbox', false);
167
168 static::assertFileExists('sandbox');
169
170 // 2 because '.' and '..'
171 static::assertCount(2, new \DirectoryIterator('sandbox'));
172 }
173
174 /**
175 * Test clearFolder on a file instead of a folder
176 */
177 public function testClearFolderOnANonDirectory(): void
178 {
179 $this->expectException(IOException::class);
180 $this->expectExceptionMessage('Provided path is not a directory.');
181
182 FileUtils::clearFolder('sandbox/file1', false);
183 }
184
185 /**
186 * Test clearFolder on a file instead of a folder
187 */
188 public function testClearFolderOutsideOfShaarliDirectory(): void
189 {
190 $this->expectException(IOException::class);
191 $this->expectExceptionMessage('Trying to delete a folder outside of Shaarli path.');
192
193
194 FileUtils::clearFolder('/tmp/shaarli-to-delete', true);
195 }
110} 196}
diff --git a/tests/front/controller/admin/ServerControllerTest.php b/tests/front/controller/admin/ServerControllerTest.php
new file mode 100644
index 00000000..355cce7d
--- /dev/null
+++ b/tests/front/controller/admin/ServerControllerTest.php
@@ -0,0 +1,184 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Security\SessionManager;
9use Shaarli\TestCase;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13/**
14 * Test Server administration controller.
15 */
16class ServerControllerTest extends TestCase
17{
18 use FrontAdminControllerMockHelper;
19
20 /** @var ServerController */
21 protected $controller;
22
23 public function setUp(): void
24 {
25 $this->createContainer();
26
27 $this->controller = new ServerController($this->container);
28
29 // initialize dummy cache
30 @mkdir('sandbox/');
31 foreach (['pagecache', 'tmp', 'cache'] as $folder) {
32 @mkdir('sandbox/' . $folder);
33 @touch('sandbox/' . $folder . '/.htaccess');
34 @touch('sandbox/' . $folder . '/1');
35 @touch('sandbox/' . $folder . '/2');
36 }
37 }
38
39 public function tearDown(): void
40 {
41 foreach (['pagecache', 'tmp', 'cache'] as $folder) {
42 @unlink('sandbox/' . $folder . '/.htaccess');
43 @unlink('sandbox/' . $folder . '/1');
44 @unlink('sandbox/' . $folder . '/2');
45 @rmdir('sandbox/' . $folder);
46 }
47 }
48
49 /**
50 * Test default display of server administration page.
51 */
52 public function testIndex(): void
53 {
54 $request = $this->createMock(Request::class);
55 $response = new Response();
56
57 // Save RainTPL assigned variables
58 $assignedVariables = [];
59 $this->assignTemplateVars($assignedVariables);
60
61 $result = $this->controller->index($request, $response);
62
63 static::assertSame(200, $result->getStatusCode());
64 static::assertSame('server', (string) $result->getBody());
65
66 static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
67 static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
68 static::assertArrayHasKey('php_eol', $assignedVariables);
69 static::assertArrayHasKey('php_extensions', $assignedVariables);
70 static::assertArrayHasKey('permissions', $assignedVariables);
71 static::assertEmpty($assignedVariables['permissions']);
72
73 static::assertRegExp(
74 '#https://github\.com/shaarli/Shaarli/releases/tag/v\d+\.\d+\.\d+#',
75 $assignedVariables['release_url']
76 );
77 static::assertRegExp('#v\d+\.\d+\.\d+#', $assignedVariables['latest_version']);
78 static::assertRegExp('#(v\d+\.\d+\.\d+|dev)#', $assignedVariables['current_version']);
79 static::assertArrayHasKey('index_url', $assignedVariables);
80 static::assertArrayHasKey('client_ip', $assignedVariables);
81 static::assertArrayHasKey('trusted_proxies', $assignedVariables);
82
83 static::assertSame('Server administration - Shaarli', $assignedVariables['pagetitle']);
84 }
85
86 /**
87 * Test clearing the main cache
88 */
89 public function testClearMainCache(): void
90 {
91 $this->container->conf = $this->createMock(ConfigManager::class);
92 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
93 if ($key === 'resource.page_cache') {
94 return 'sandbox/pagecache';
95 } elseif ($key === 'resource.raintpl_tmp') {
96 return 'sandbox/tmp';
97 } elseif ($key === 'resource.thumbnails_cache') {
98 return 'sandbox/cache';
99 } else {
100 return $default;
101 }
102 });
103
104 $this->container->sessionManager
105 ->expects(static::once())
106 ->method('setSessionParameter')
107 ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['Shaarli\'s cache folder has been cleared!'])
108 ;
109
110 $request = $this->createMock(Request::class);
111 $request->method('getQueryParam')->with('type')->willReturn('main');
112 $response = new Response();
113
114 $result = $this->controller->clearCache($request, $response);
115
116 static::assertSame(302, $result->getStatusCode());
117 static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
118
119 static::assertFileNotExists('sandbox/pagecache/1');
120 static::assertFileNotExists('sandbox/pagecache/2');
121 static::assertFileNotExists('sandbox/tmp/1');
122 static::assertFileNotExists('sandbox/tmp/2');
123
124 static::assertFileExists('sandbox/pagecache/.htaccess');
125 static::assertFileExists('sandbox/tmp/.htaccess');
126 static::assertFileExists('sandbox/cache');
127 static::assertFileExists('sandbox/cache/.htaccess');
128 static::assertFileExists('sandbox/cache/1');
129 static::assertFileExists('sandbox/cache/2');
130 }
131
132 /**
133 * Test clearing thumbnails cache
134 */
135 public function testClearThumbnailsCache(): void
136 {
137 $this->container->conf = $this->createMock(ConfigManager::class);
138 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
139 if ($key === 'resource.page_cache') {
140 return 'sandbox/pagecache';
141 } elseif ($key === 'resource.raintpl_tmp') {
142 return 'sandbox/tmp';
143 } elseif ($key === 'resource.thumbnails_cache') {
144 return 'sandbox/cache';
145 } else {
146 return $default;
147 }
148 });
149
150 $this->container->sessionManager
151 ->expects(static::once())
152 ->method('setSessionParameter')
153 ->willReturnCallback(function (string $key, array $value): SessionManager {
154 static::assertSame(SessionManager::KEY_WARNING_MESSAGES, $key);
155 static::assertCount(1, $value);
156 static::assertStringStartsWith('Thumbnails cache has been cleared.', $value[0]);
157
158 return $this->container->sessionManager;
159 });
160 ;
161
162 $request = $this->createMock(Request::class);
163 $request->method('getQueryParam')->with('type')->willReturn('thumbnails');
164 $response = new Response();
165
166 $result = $this->controller->clearCache($request, $response);
167
168 static::assertSame(302, $result->getStatusCode());
169 static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
170
171 static::assertFileNotExists('sandbox/cache/1');
172 static::assertFileNotExists('sandbox/cache/2');
173
174 static::assertFileExists('sandbox/cache/.htaccess');
175 static::assertFileExists('sandbox/pagecache');
176 static::assertFileExists('sandbox/pagecache/.htaccess');
177 static::assertFileExists('sandbox/pagecache/1');
178 static::assertFileExists('sandbox/pagecache/2');
179 static::assertFileExists('sandbox/tmp');
180 static::assertFileExists('sandbox/tmp/.htaccess');
181 static::assertFileExists('sandbox/tmp/1');
182 static::assertFileExists('sandbox/tmp/2');
183 }
184}
diff --git a/tests/front/controller/visitor/InstallControllerTest.php b/tests/front/controller/visitor/InstallControllerTest.php
index 345ad544..2105ed77 100644
--- a/tests/front/controller/visitor/InstallControllerTest.php
+++ b/tests/front/controller/visitor/InstallControllerTest.php
@@ -79,6 +79,15 @@ class InstallControllerTest extends TestCase
79 static::assertIsArray($assignedVariables['languages']); 79 static::assertIsArray($assignedVariables['languages']);
80 static::assertSame('Automatic', $assignedVariables['languages']['auto']); 80 static::assertSame('Automatic', $assignedVariables['languages']['auto']);
81 static::assertSame('French', $assignedVariables['languages']['fr']); 81 static::assertSame('French', $assignedVariables['languages']['fr']);
82
83 static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
84 static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
85 static::assertArrayHasKey('php_eol', $assignedVariables);
86 static::assertArrayHasKey('php_extensions', $assignedVariables);
87 static::assertArrayHasKey('permissions', $assignedVariables);
88 static::assertEmpty($assignedVariables['permissions']);
89
90 static::assertSame('Install Shaarli', $assignedVariables['pagetitle']);
82 } 91 }
83 92
84 /** 93 /**
diff --git a/tpl/default/install.html b/tpl/default/install.html
index a506a2eb..4f98d49d 100644
--- a/tpl/default/install.html
+++ b/tpl/default/install.html
@@ -163,6 +163,16 @@
163 </div> 163 </div>
164</div> 164</div>
165</form> 165</form>
166
167<div class="pure-g">
168 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
169 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-form-complete">
170 <h2 class="window-title">{'Server requirements'|t}</h2>
171
172 {include="server.requirements"}
173 </div>
174</div>
175
166{include="page.footer"} 176{include="page.footer"}
167</body> 177</body>
168</html> 178</html>
diff --git a/tpl/default/server.html b/tpl/default/server.html
new file mode 100644
index 00000000..de1c8b53
--- /dev/null
+++ b/tpl/default/server.html
@@ -0,0 +1,129 @@
1<!DOCTYPE html>
2<html{if="$language !== 'auto'"} lang="{$language}"{/if}>
3<head>
4 {include="includes"}
5</head>
6<body>
7{include="page.header"}
8
9<div class="pure-g">
10 <div class="pure-u-lg-1-4 pure-u-1-24"></div>
11 <div class="pure-u-lg-1-2 pure-u-22-24 page-form server-tables-page">
12 <h2 class="window-title">{'Server administration'|t}</h2>
13
14 <h3 class="window-subtitle">{'General'|t}</h3>
15
16 <div class="pure-g server-row">
17 <div class="pure-u-lg-1-2 pure-u-1 server-label">
18 <p>{'Index URL'|t}</p>
19 </div>
20 <div class="pure-u-lg-1-2 pure-u-1">
21 <p><a href="{$index_url}" title="{$pagetitle}">{$index_url}</a></p>
22 </div>
23 </div>
24 <div class="pure-g server-row">
25 <div class="pure-u-lg-1-2 pure-u-1 server-label">
26 <p>{'Base path'|t}</p>
27 </div>
28 <div class="pure-u-lg-1-2 pure-u-1">
29 <p>{$base_path}</p>
30 </div>
31 </div>
32 <div class="pure-g server-row">
33 <div class="pure-u-lg-1-2 pure-u-1 server-label">
34 <p>{'Client IP'|t}</p>
35 </div>
36 <div class="pure-u-lg-1-2 pure-u-1">
37 <p>{$client_ip}</p>
38 </div>
39 </div>
40 <div class="pure-g server-row">
41 <div class="pure-u-lg-1-2 pure-u-1 server-label">
42 <p>{'Trusted reverse proxies'|t}</p>
43 </div>
44 <div class="pure-u-lg-1-2 pure-u-1">
45 {if="count($trusted_proxies) > 0"}
46 <p>
47 {loop="$trusted_proxies"}
48 {$value}<br>
49 {/loop}
50 </p>
51 {else}
52 <p>{'N/A'|t}</p>
53 {/if}
54 </div>
55 </div>
56
57 {include="server.requirements"}
58
59 <h3 class="window-subtitle">Version</h3>
60
61 <div class="pure-g server-row">
62 <div class="pure-u-lg-1-2 pure-u-1 server-label">
63 <p>Current version</p>
64 </div>
65 <div class="pure-u-lg-1-2 pure-u-1">
66 <p>{$current_version}</p>
67 </div>
68 </div>
69
70 <div class="pure-g server-row">
71 <div class="pure-u-lg-1-2 pure-u-1 server-label">
72 <p>Latest release</p>
73 </div>
74 <div class="pure-u-lg-1-2 pure-u-1">
75 <p>
76 <a href="{$release_url}" title="{'Visit releases page on Github'|t}">
77 {$latest_version}
78 </a>
79 </p>
80 </div>
81 </div>
82
83 <h3 class="window-subtitle">Thumbnails</h3>
84
85 <div class="pure-g server-row">
86 <div class="pure-u-lg-1-2 pure-u-1 server-label">
87 <p>Thumbnails status</p>
88 </div>
89 <div class="pure-u-lg-1-2 pure-u-1">
90 <p>
91 {if="$thumbnails_mode==='all'"}
92 {'All'|t}
93 {elseif="$thumbnails_mode==='common'"}
94 {'Only common media hosts'|t}
95 {else}
96 {'None'|t}
97 {/if}
98 </p>
99 </div>
100 </div>
101
102 {if="$thumbnails_mode!=='none'"}
103 <div class="center tools-item">
104 <a href="{$base_path}/admin/thumbnails" title="{'Synchronize all link thumbnails'|t}">
105 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span>
106 </a>
107 </div>
108 {/if}
109
110 <h3 class="window-subtitle">Cache</h3>
111
112 <div class="center tools-item">
113 <a href="{$base_path}/admin/clear-cache?type=main">
114 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Clear main cache</span>
115 </a>
116 </div>
117
118 <div class="center tools-item">
119 <a href="{$base_path}/admin/clear-cache?type=thumbnails">
120 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Clear thumbnails cache</span>
121 </a>
122 </div>
123 </div>
124</div>
125
126{include="page.footer"}
127
128</body>
129</html>
diff --git a/tpl/default/server.requirements.html b/tpl/default/server.requirements.html
new file mode 100644
index 00000000..85def9b7
--- /dev/null
+++ b/tpl/default/server.requirements.html
@@ -0,0 +1,68 @@
1<div class="server-tables">
2 <h3 class="window-subtitle">{'Permissions'|t}</h3>
3
4 {if="count($permissions) > 0"}
5 <p class="center">
6 <i class="fa fa-close fa-color-red" aria-hidden="true"></i>
7 {'There are permissions that need to be fixed.'|t}
8 </p>
9
10 <p>
11 {loop="$permissions"}
12 <div class="center">{$value}</div>
13 {/loop}
14 </p>
15 {else}
16 <p class="center">
17 <i class="fa fa-check fa-color-green" aria-hidden="true"></i>
18 {'All read/write permissions are properly set.'|t}
19 </p>
20 {/if}
21
22 <h3 class="window-subtitle">PHP</h3>
23
24 <p class="center">
25 <strong>{'Running PHP'|t} {$php_version}</strong>
26 {if="$php_has_reached_eol"}
27 <i class="fa fa-circle fa-color-orange" aria-label="hidden"></i><br>
28 {'End of life: '|t} {$php_eol}
29 {else}
30 <i class="fa fa-circle fa-color-green" aria-label="hidden"></i><br>
31 {/if}
32 </p>
33
34 <table class="center">
35 <thead>
36 <tr>
37 <th>{'Extension'|t}</th>
38 <th>{'Usage'|t}</th>
39 <th>{'Status'|t}</th>
40 <th>{'Loaded'|t}</th>
41 </tr>
42 </thead>
43 <tbody>
44 {loop="$php_extensions"}
45 <tr>
46 <td>{$value.name}</td>
47 <td>{$value.desc}</td>
48 <td>{$value.required ? t('Required') : t('Optional')}</td>
49 <td>
50 {if="$value.loaded"}
51 {$classLoaded="fa-color-green"}
52 {$strLoaded=t('Loaded')}
53 {else}
54 {$strLoaded=t('Not loaded')}
55 {if="$value.required"}
56 {$classLoaded="fa-color-red"}
57 {else}
58 {$classLoaded="fa-color-orange"}
59 {/if}
60 {/if}
61
62 <i class="fa fa-circle {$classLoaded}" aria-label="{$strLoaded}" title="{$strLoaded}"></i>
63 </td>
64 </tr>
65 {/loop}
66 </tbody>
67 </table>
68</div>
diff --git a/tpl/default/tools.html b/tpl/default/tools.html
index 2cb08e38..2df73598 100644
--- a/tpl/default/tools.html
+++ b/tpl/default/tools.html
@@ -20,6 +20,12 @@
20 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Plugin administration'|t}</span> 20 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Plugin administration'|t}</span>
21 </a> 21 </a>
22 </div> 22 </div>
23 <div class="tools-item">
24 <a href="{$base_path}/admin/server"
25 title="{'Check instance\'s server configuration'|t}">
26 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Server administration'|t}</span>
27 </a>
28 </div>
23 {if="!$openshaarli"} 29 {if="!$openshaarli"}
24 <div class="tools-item"> 30 <div class="tools-item">
25 <a href="{$base_path}/admin/password" title="{'Change your password'|t}"> 31 <a href="{$base_path}/admin/password" title="{'Change your password'|t}">
@@ -45,14 +51,6 @@
45 </a> 51 </a>
46 </div> 52 </div>
47 53
48 {if="$thumbnails_enabled"}
49 <div class="tools-item">
50 <a href="{$base_path}/admin/thumbnails" title="{'Synchronize all link thumbnails'|t}">
51 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span>
52 </a>
53 </div>
54 {/if}
55
56 {loop="$tools_plugin"} 54 {loop="$tools_plugin"}
57 <div class="tools-item"> 55 <div class="tools-item">
58 {$value} 56 {$value}