*/
public static $VERSION_FILE = 'shaarli_version.php';
- private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
- private static $GIT_BRANCHES = array('latest', 'stable');
+ public static $GITHUB_URL = 'https://github.com/shaarli/Shaarli';
+ public static $GIT_RAW_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
+ public static $GIT_BRANCHES = array('latest', 'stable');
private static $VERSION_START_TAG = '<?php /* ';
private static $VERSION_END_TAG = ' */ ?>';
// Late Static Binding allows overriding within tests
// See http://php.net/manual/en/language.oop5.late-static-bindings.php
$latestVersion = static::getVersion(
- self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE
+ self::$GIT_RAW_URL . '/' . $branch . '/' . self::$VERSION_FILE
);
if (!$latestVersion) {
/**
* Checks Shaarli has the proper access permissions to its resources
*
- * @param ConfigManager $conf Configuration Manager instance.
+ * @param ConfigManager $conf Configuration Manager instance.
+ * @param bool $minimalMode In minimal mode we only check permissions to be able to display a template.
+ * Currently we only need to be able to read the theme and write in raintpl cache.
*
* @return array A list of the detected configuration issues
*/
- public static function checkResourcePermissions($conf)
+ public static function checkResourcePermissions(ConfigManager $conf, bool $minimalMode = false): array
{
- $errors = array();
+ $errors = [];
$rainTplDir = rtrim($conf->get('resource.raintpl_tpl'), '/');
// Check script and template directories are readable
- foreach (array(
+ foreach ([
'application',
'inc',
'plugins',
$rainTplDir,
$rainTplDir . '/' . $conf->get('resource.theme'),
- ) as $path) {
+ ] as $path) {
if (!is_readable(realpath($path))) {
$errors[] = '"' . $path . '" ' . t('directory is not readable');
}
}
// Check cache and data directories are readable and writable
- foreach (array(
- $conf->get('resource.thumbnails_cache'),
- $conf->get('resource.data_dir'),
- $conf->get('resource.page_cache'),
- $conf->get('resource.raintpl_tmp'),
- ) as $path) {
+ if ($minimalMode) {
+ $folders = [
+ $conf->get('resource.raintpl_tmp'),
+ ];
+ } else {
+ $folders = [
+ $conf->get('resource.thumbnails_cache'),
+ $conf->get('resource.data_dir'),
+ $conf->get('resource.page_cache'),
+ $conf->get('resource.raintpl_tmp'),
+ ];
+ }
+
+ foreach ($folders as $path) {
if (!is_readable(realpath($path))) {
$errors[] = '"' . $path . '" ' . t('directory is not readable');
}
}
}
+ if ($minimalMode) {
+ return $errors;
+ }
+
// Check configuration files are readable and writable
foreach (array(
$conf->getConfigFileExt(),
{
return hash_hmac('sha256', $currentVersion, $salt);
}
+
+ /**
+ * Get a list of PHP extensions used by Shaarli.
+ *
+ * @return array[] List of extension with following keys:
+ * - name: extension name
+ * - required: whether the extension is required to use Shaarli
+ * - desc: short description of extension usage in Shaarli
+ * - loaded: whether the extension is properly loaded or not
+ */
+ public static function getPhpExtensionsRequirement(): array
+ {
+ $extensions = [
+ ['name' => 'json', 'required' => true, 'desc' => t('Configuration parsing')],
+ ['name' => 'simplexml', 'required' => true, 'desc' => t('Slim Framework (routing, etc.)')],
+ ['name' => 'mbstring', 'required' => true, 'desc' => t('Multibyte (Unicode) string support')],
+ ['name' => 'gd', 'required' => false, 'desc' => t('Required to use thumbnails')],
+ ['name' => 'intl', 'required' => false, 'desc' => t('Localized text sorting (e.g. e->รจ->f)')],
+ ['name' => 'curl', 'required' => false, 'desc' => t('Better retrieval of bookmark metadata and thumbnail')],
+ ['name' => 'gettext', 'required' => false, 'desc' => t('Use the translation system in gettext mode')],
+ ['name' => 'ldap', 'required' => false, 'desc' => t('Login using LDAP server')],
+ ];
+
+ foreach ($extensions as &$extension) {
+ $extension['loaded'] = extension_loaded($extension['name']);
+ }
+
+ return $extensions;
+ }
+
+ /**
+ * Return the EOL date of given PHP version. If the version is unknown,
+ * we return today + 2 years.
+ *
+ * @param string $fullVersion PHP version, e.g. 7.4.7
+ *
+ * @return string Date format: YYYY-MM-DD
+ */
+ public static function getPhpEol(string $fullVersion): string
+ {
+ preg_match('/(\d+\.\d+)\.\d+/', $fullVersion, $matches);
+
+ return [
+ '7.1' => '2019-12-01',
+ '7.2' => '2020-11-30',
+ '7.3' => '2021-12-06',
+ '7.4' => '2022-11-28',
+ '8.0' => '2023-12-01',
+ ][$matches[1]] ?? (new \DateTime('+2 year'))->format('Y-m-d');
+ }
}
)
);
}
+
+ /**
+ * Recursively deletes a folder content, and deletes itself optionally.
+ * If an excluded file is found, folders won't be deleted.
+ *
+ * Additional security: raise an exception if it tries to delete a folder outside of Shaarli directory.
+ *
+ * @param string $path
+ * @param bool $selfDelete Delete the provided folder if true, only its content if false.
+ * @param array $exclude
+ */
+ public static function clearFolder(string $path, bool $selfDelete, array $exclude = []): bool
+ {
+ $skipped = false;
+
+ if (!is_dir($path)) {
+ throw new IOException(t('Provided path is not a directory.'));
+ }
+
+ if (!static::isPathInShaarliFolder($path)) {
+ throw new IOException(t('Trying to delete a folder outside of Shaarli path.'));
+ }
+
+ foreach (new \DirectoryIterator($path) as $file) {
+ if($file->isDot()) {
+ continue;
+ }
+
+ if (in_array($file->getBasename(), $exclude, true)) {
+ $skipped = true;
+ continue;
+ }
+
+ if ($file->isFile()) {
+ unlink($file->getPathname());
+ } elseif($file->isDir()) {
+ $skipped = static::clearFolder($file->getRealPath(), true, $exclude) || $skipped;
+ }
+ }
+
+ if ($selfDelete && !$skipped) {
+ rmdir($path);
+ }
+
+ return $skipped;
+ }
+
+ /**
+ * Checks that the given path is inside Shaarli directory.
+ */
+ public static function isPathInShaarliFolder(string $path): bool
+ {
+ $rootDirectory = dirname(dirname(__FILE__));
+
+ return strpos(realpath($path), $rootDirectory) !== false;
+ }
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Admin;
+
+use Shaarli\ApplicationUtils;
+use Shaarli\FileUtils;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+/**
+ * Slim controller used to handle Server administration page, and actions.
+ */
+class ServerController extends ShaarliAdminController
+{
+ /** @var string Cache type - main - by default pagecache/ and tmp/ */
+ protected const CACHE_MAIN = 'main';
+
+ /** @var string Cache type - thumbnails - by default cache/ */
+ protected const CACHE_THUMB = 'thumbnails';
+
+ /**
+ * GET /admin/server - Display page Server administration
+ */
+ public function index(Request $request, Response $response): Response
+ {
+ $latestVersion = 'v' . ApplicationUtils::getVersion(
+ ApplicationUtils::$GIT_RAW_URL . '/latest/' . ApplicationUtils::$VERSION_FILE
+ );
+ $currentVersion = ApplicationUtils::getVersion('./shaarli_version.php');
+ $currentVersion = $currentVersion === 'dev' ? $currentVersion : 'v' . $currentVersion;
+ $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION));
+
+ $this->assignView('php_version', PHP_VERSION);
+ $this->assignView('php_eol', format_date($phpEol, false));
+ $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable());
+ $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement());
+ $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf));
+ $this->assignView('release_url', ApplicationUtils::$GITHUB_URL . '/releases/tag/' . $latestVersion);
+ $this->assignView('latest_version', $latestVersion);
+ $this->assignView('current_version', $currentVersion);
+ $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode'));
+ $this->assignView('index_url', index_url($this->container->environment));
+ $this->assignView('client_ip', client_ip_id($this->container->environment));
+ $this->assignView('trusted_proxies', $this->container->conf->get('security.trusted_proxies', []));
+
+ $this->assignView(
+ 'pagetitle',
+ t('Server administration') . ' - ' . $this->container->conf->get('general.title', 'Shaarli')
+ );
+
+ return $response->write($this->render('server'));
+ }
+
+ /**
+ * GET /admin/clear-cache?type={$type} - Action to trigger cache folder clearing (either main or thumbnails).
+ */
+ public function clearCache(Request $request, Response $response): Response
+ {
+ $exclude = ['.htaccess'];
+
+ if ($request->getQueryParam('type') === static::CACHE_THUMB) {
+ $folders = [$this->container->conf->get('resource.thumbnails_cache')];
+
+ $this->saveWarningMessage(
+ t('Thumbnails cache has been cleared.') . ' ' .
+ '<a href="'. $this->container->basePath .'/admin/thumbnails">' . t('Please synchronize them.') .'</a>'
+ );
+ } else {
+ $folders = [
+ $this->container->conf->get('resource.page_cache'),
+ $this->container->conf->get('resource.raintpl_tmp'),
+ ];
+
+ $this->saveSuccessMessage(t('Shaarli\'s cache folder has been cleared!'));
+ }
+
+ // Make sure that we don't delete root cache folder
+ $folders = array_map('realpath', array_values(array_filter(array_map('trim', $folders))));
+ foreach ($folders as $folder) {
+ FileUtils::clearFolder($folder, false, $exclude);
+ }
+
+ return $this->redirect($response, '/admin/server');
+ }
+}
*/
protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool
{
- // Logged in, not async retrieval, thumbnails enabled, and thumbnail should be updated
- if ($this->container->loginManager->isLoggedIn()
- && true !== $this->container->conf->get('general.enable_async_metadata', true)
- && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
- && $bookmark->shouldUpdateThumbnail()
- ) {
- $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl()));
- $this->container->bookmarkService->set($bookmark, $writeDatastore);
-
- return true;
+ if (false === $this->container->loginManager->isLoggedIn()) {
+ return false;
+ }
+
+ // If thumbnail should be updated, we reset it to null
+ if ($bookmark->shouldUpdateThumbnail()) {
+ $bookmark->setThumbnail(null);
+
+ // Requires an update, not async retrieval, thumbnails enabled
+ if ($bookmark->shouldUpdateThumbnail()
+ && true !== $this->container->conf->get('general.enable_async_metadata', true)
+ && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
+ ) {
+ $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl()));
+ $this->container->bookmarkService->set($bookmark, $writeDatastore);
+
+ return true;
+ }
}
return false;
$this->assignView('cities', $cities);
$this->assignView('languages', Languages::getAvailableLanguages());
+ $phpEol = new \DateTimeImmutable(ApplicationUtils::getPhpEol(PHP_VERSION));
+
+ $this->assignView('php_version', PHP_VERSION);
+ $this->assignView('php_eol', format_date($phpEol, false));
+ $this->assignView('php_has_reached_eol', $phpEol < new \DateTimeImmutable());
+ $this->assignView('php_extensions', ApplicationUtils::getPhpExtensionsRequirement());
+ $this->assignView('permissions', ApplicationUtils::checkResourcePermissions($this->container->conf));
+
+ $this->assignView('pagetitle', t('Install Shaarli'));
+
return $response->write($this->render('install'));
}
protected function checkPermissions(): bool
{
// Ensure Shaarli has proper access to its resources
- $errors = ApplicationUtils::checkResourcePermissions($this->container->conf);
+ $errors = ApplicationUtils::checkResourcePermissions($this->container->conf, true);
if (empty($errors)) {
return true;
}
}
table {
- margin: auto;
+ margin: 10px auto 25px auto;
width: 90%;
.order {
}
}
+// SERVER PAGE
+
+.server-tables-page,
+.server-tables {
+ .window-subtitle {
+ &::before {
+ display: block;
+ margin: 8px auto;
+ background: linear-gradient(to right, var(--background-color), $dark-grey, var(--background-color));
+ width: 50%;
+ height: 1px;
+ content: '';
+ }
+ }
+
+ .server-row {
+ p {
+ height: 25px;
+ padding: 0 10px;
+ }
+ }
+
+ .server-label {
+ text-align: right;
+ font-weight: bold;
+ }
+
+ i {
+ &.fa-color-green {
+ color: $main-green;
+ }
+
+ &.fa-color-orange {
+ color: $orange;
+ }
+
+ &.fa-color-red {
+ color: $red;
+ }
+ }
+
+ @media screen and (max-width: 64em) {
+ .server-label {
+ text-align: center;
+ }
+
+ .server-row {
+ p {
+ text-align: center;
+ }
+ }
+ }
+}
+
// Print rules
@media print {
.shaarli-menu {
msgid ""
msgstr ""
"Project-Id-Version: Shaarli\n"
-"POT-Creation-Date: 2020-10-16 20:01+0200\n"
-"PO-Revision-Date: 2020-10-16 20:02+0200\n"
+"POT-Creation-Date: 2020-10-21 15:00+0200\n"
+"PO-Revision-Date: 2020-10-21 15:06+0200\n"
"Last-Translator: \n"
"Language-Team: Shaarli\n"
"Language: fr_FR\n"
"X-Poedit-SearchPath-3: init.php\n"
"X-Poedit-SearchPath-4: plugins\n"
-#: application/ApplicationUtils.php:161
+#: application/ApplicationUtils.php:162
#, php-format
msgid ""
"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus "
"peut donc pas fonctionner. Votre version de PHP a des failles de sรฉcuritรฉs "
"connues et devrait รชtre mise ร jour au plus tรดt."
-#: application/ApplicationUtils.php:192 application/ApplicationUtils.php:204
+#: application/ApplicationUtils.php:195 application/ApplicationUtils.php:215
msgid "directory is not readable"
msgstr "le rรฉpertoire n'est pas accessible en lecture"
-#: application/ApplicationUtils.php:207
+#: application/ApplicationUtils.php:218
msgid "directory is not writable"
msgstr "le rรฉpertoire n'est pas accessible en รฉcriture"
-#: application/ApplicationUtils.php:225
+#: application/ApplicationUtils.php:240
msgid "file is not readable"
msgstr "le fichier n'est pas accessible en lecture"
-#: application/ApplicationUtils.php:228
+#: application/ApplicationUtils.php:243
msgid "file is not writable"
msgstr "le fichier n'est pas accessible en รฉcriture"
+#: application/ApplicationUtils.php:277
+msgid "Configuration parsing"
+msgstr "Chargement de la configuration"
+
+#: application/ApplicationUtils.php:278
+msgid "Slim Framework (routing, etc.)"
+msgstr "Slim Framwork (routage, etc.)"
+
+#: application/ApplicationUtils.php:279
+msgid "Multibyte (Unicode) string support"
+msgstr "Support des chaรฎnes de caractรจre multibytes (Unicode)"
+
+#: application/ApplicationUtils.php:280
+msgid "Required to use thumbnails"
+msgstr "Obligatoire pour utiliser les miniatures"
+
+#: application/ApplicationUtils.php:281
+msgid "Localized text sorting (e.g. e->รจ->f)"
+msgstr "Tri des textes traduits (ex : e->รจ->f)"
+
+#: application/ApplicationUtils.php:282
+msgid "Better retrieval of bookmark metadata and thumbnail"
+msgstr "Meilleure rรฉcupรฉration des meta-donnรฉes des marque-pages et minatures"
+
+#: application/ApplicationUtils.php:283
+msgid "Use the translation system in gettext mode"
+msgstr "Utiliser le systรจme de traduction en mode gettext"
+
+#: application/ApplicationUtils.php:284
+msgid "Login using LDAP server"
+msgstr "Authentification via un serveur LDAP"
+
+#: application/FileUtils.php:100
+msgid "Provided path is not a directory."
+msgstr "Le chemin fourni n'est pas un dossier."
+
+#: application/FileUtils.php:104
+msgid "Trying to delete a folder outside of Shaarli path."
+msgstr "Tentative de supprimer un dossier en dehors du chemin de Shaarli."
+
#: application/History.php:179
msgid "History file isn't readable or writable"
msgstr "Le fichier d'historique n'est pas accessible en lecture ou en รฉcriture"
msgstr "Vous avez activรฉ ou changรฉ le mode de miniatures."
#: application/front/controller/admin/ConfigureController.php:103
+#: application/front/controller/admin/ServerController.php:68
#: application/legacy/LegacyUpdater.php:538
msgid "Please synchronize them."
msgstr "Merci de les synchroniser."
#: application/front/controller/admin/ConfigureController.php:113
-#: application/front/controller/visitor/InstallController.php:136
+#: application/front/controller/visitor/InstallController.php:146
msgid "Error while writing config file after configuration update."
msgstr ""
"Une erreur s'est produite lors de la sauvegarde du fichier de configuration."
msgid "Shaare a new link"
msgstr "Partager un nouveau lien"
-#: application/front/controller/admin/ManageShaareController.php:78
+#: application/front/controller/admin/ManageShaareController.php:64
msgid "Note: "
msgstr "Note : "
-#: application/front/controller/admin/ManageShaareController.php:109
-#: application/front/controller/admin/ManageShaareController.php:206
-#: application/front/controller/admin/ManageShaareController.php:275
-#: application/front/controller/admin/ManageShaareController.php:315
+#: application/front/controller/admin/ManageShaareController.php:95
+#: application/front/controller/admin/ManageShaareController.php:193
+#: application/front/controller/admin/ManageShaareController.php:262
+#: application/front/controller/admin/ManageShaareController.php:302
#, php-format
msgid "Bookmark with identifier %s could not be found."
msgstr "Le lien avec l'identifiant %s n'a pas pu รชtre trouvรฉ."
-#: application/front/controller/admin/ManageShaareController.php:194
-#: application/front/controller/admin/ManageShaareController.php:252
+#: application/front/controller/admin/ManageShaareController.php:181
+#: application/front/controller/admin/ManageShaareController.php:239
msgid "Invalid bookmark ID provided."
msgstr "ID du lien non valide."
-#: application/front/controller/admin/ManageShaareController.php:260
+#: application/front/controller/admin/ManageShaareController.php:247
msgid "Invalid visibility provided."
msgstr "Visibilitรฉ du lien non valide."
-#: application/front/controller/admin/ManageShaareController.php:363
+#: application/front/controller/admin/ManageShaareController.php:352
#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:171
msgid "Edit"
msgstr "Modifier"
-#: application/front/controller/admin/ManageShaareController.php:366
+#: application/front/controller/admin/ManageShaareController.php:355
#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:28
msgid "Shaare"
#: application/front/controller/admin/ManageTagController.php:29
#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
msgid "Manage tags"
msgstr "Gรฉrer les tags"
#: application/front/controller/admin/PasswordController.php:28
#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
msgid "Change password"
msgstr "Modifier le mot de passe"
"Une erreur s'est produite lors de la sauvegarde de la configuration des "
"plugins : "
+#: application/front/controller/admin/ServerController.php:50
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
+msgid "Server administration"
+msgstr "Administration serveur"
+
+#: application/front/controller/admin/ServerController.php:67
+msgid "Thumbnails cache has been cleared."
+msgstr "Le cache des miniatures a รฉtรฉ vidรฉ."
+
+#: application/front/controller/admin/ServerController.php:76
+msgid "Shaarli's cache folder has been cleared!"
+msgstr "Le dossier de cache de Shaarli a รฉtรฉ vidรฉ !"
+
#: application/front/controller/admin/ThumbnailsController.php:37
#: tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
msgid "Thumbnails update"
#: application/front/controller/visitor/ErrorNotFoundController.php:25
msgid "Requested page could not be found."
-msgstr ""
+msgstr "La page demandรฉe n'a pas pu รชtre trouvรฉe."
-#: application/front/controller/visitor/InstallController.php:73
+#: application/front/controller/visitor/InstallController.php:64
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
+msgid "Install Shaarli"
+msgstr "Installation de Shaarli"
+
+#: application/front/controller/visitor/InstallController.php:83
#, php-format
msgid ""
"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
"des cookies. Nous vous recommandons d'accรฉder ร votre serveur depuis son "
"adresse IP ou un <em>Fully Qualified Domain Name</em>.<br>"
-#: application/front/controller/visitor/InstallController.php:144
+#: application/front/controller/visitor/InstallController.php:154
msgid ""
"Shaarli is now configured. Please login and start shaaring your bookmarks!"
msgstr ""
"Shaarli est maintenant configurรฉ. Vous pouvez vous connecter et commencez ร "
"shaare vos liens !"
-#: application/front/controller/visitor/InstallController.php:158
+#: application/front/controller/visitor/InstallController.php:168
msgid "Insufficient permissions:"
msgstr "Permissions insuffisantes :"
"miniatures."
#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:328
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:56
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:122
msgid "Synchronize thumbnails"
msgstr "Synchroniser les miniatures"
#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:339
#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
msgid "All"
msgstr "Tous"
#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:343
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106
msgid "Only common media hosts"
msgstr "Seulement les hรฉbergeurs de mรฉdia connus"
#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:347
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110
msgid "None"
msgstr "Aucune"
#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:355
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199
msgid "Save"
msgid "Next day"
msgstr "Jour suivant"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
msgid "Edit Shaare"
msgstr "Modifier le Shaare"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
msgid "New Shaare"
msgstr "Nouveau Shaare"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
msgid "Created:"
msgstr "Crรฉation :"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:32
msgid "URL"
msgstr "URL"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:38
msgid "Title"
msgstr "Titre"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:49
#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75
#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
msgid "Description"
msgstr "Description"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
msgid "Tags"
msgstr "Tags"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:60
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
msgid "Private"
msgstr "Privรฉ"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:80
msgid "Description will be rendered with"
msgstr "La description sera gรฉnรฉrรฉe avec"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:68
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82
msgid "Markdown syntax documentation"
msgstr "Documentation sur la syntaxe Markdown"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
msgid "Markdown syntax"
msgstr "la syntaxe Markdown"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
msgid "Apply Changes"
msgstr "Appliquer les changements"
-#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:93
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:107
#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:173
#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147
#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:147
msgid "Add default tags"
msgstr "Ajouter des tags par dรฉfaut"
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
-msgid "Install Shaarli"
-msgstr "Installation de Shaarli"
-
#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
msgid "It looks like it's the first time you run Shaarli. Please configure it."
msgstr ""
msgid "Install"
msgstr "Installer"
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:190
+msgid "Server requirements"
+msgstr "Prรฉ-requis serveur"
+
#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79
msgid "shaare"
msgid "No parameter available."
msgstr "Aucun paramรจtre disponible."
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "General"
+msgstr "Gรฉnรฉral"
+
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:20
+msgid "Index URL"
+msgstr "URL de l'index"
+
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
+msgid "Base path"
+msgstr "Chemin de base"
+
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
+msgid "Client IP"
+msgstr "IP du client"
+
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
+msgid "Trusted reverse proxies"
+msgstr "Reverse proxies de confiance"
+
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
+msgid "N/A"
+msgstr "N/A"
+
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:84
+msgid "Visit releases page on Github"
+msgstr "Visiter la page des releases sur Github"
+
+#: tmp/server.b91ef64efc3688266305ea9b42e5017e.rtpl.php:121
+msgid "Synchronize all link thumbnails"
+msgstr "Synchroniser toutes les miniatures"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:2
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:2
+msgid "Permissions"
+msgstr "Permissions"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:8
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:8
+msgid "There are permissions that need to be fixed."
+msgstr "Il y a des permissions qui doivent รชtre corrigรฉes."
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:23
+msgid "All read/write permissions are properly set."
+msgstr "Toutes les permissions de lecture/รฉcriture sont dรฉfinies correctement."
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:32
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:32
+msgid "Running PHP"
+msgstr "Fonctionnant avec PHP"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:36
+msgid "End of life: "
+msgstr "Fin de vie : "
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:48
+msgid "Extension"
+msgstr "Extension"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:49
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:49
+msgid "Usage"
+msgstr "Utilisation"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:50
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:50
+msgid "Status"
+msgstr "Statut"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:51
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:66
+msgid "Loaded"
+msgstr "Chargรฉ"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:60
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:60
+msgid "Required"
+msgstr "Obligatoire"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:60
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:60
+msgid "Optional"
+msgstr "Optionnel"
+
+#: tmp/server.requirements.b91ef64efc3688266305ea9b42e5017e.rtpl.php:70
+#: tmp/server.requirements.cedf684561d925457130839629000a81.rtpl.php:70
+msgid "Not loaded"
+msgstr "Non chargรฉ"
+
#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
msgid "tags"
msgid "Enable, disable and configure plugins"
msgstr "Activer, dรฉsactiver et configurer les extensions"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:27
+msgid "Check instance's server configuration"
+msgstr "Vรฉrifier la configuration serveur de l'instance"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
msgid "Change your password"
msgstr "Modifier le mot de passe"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
msgid "Rename or delete a tag in all links"
msgstr "Renommer ou supprimer un tag dans tous les liens"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
msgid ""
"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
"delicious...)"
"Importer des marques pages au format Netscape HTML (comme exportรฉs depuis "
"Firefox, Chrome, Opera, delicious...)"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
msgid "Import links"
msgstr "Importer des liens"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:53
msgid ""
"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
"Opera, delicious...)"
"Exporter les marques pages au format Netscape HTML (comme exportรฉs depuis "
"Firefox, Chrome, Opera, delicious...)"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:54
msgid "Export database"
msgstr "Exporter les donnรฉes"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:55
-msgid "Synchronize all link thumbnails"
-msgstr "Synchroniser toutes les miniatures"
-
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:81
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
msgid ""
"Drag one of these button to your bookmarks toolbar or right-click it and "
"\"Bookmark This Link\""
"Glisser un de ces boutons dans votre barre de favoris ou cliquer droit "
"dessus et ยซ Ajouter aux favoris ยป"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:78
msgid "then click on the bookmarklet in any page you want to share."
msgstr ""
"puis cliquer sur le marque-page depuis un site que vous souhaitez partager."
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106
msgid ""
"Drag this link to your bookmarks toolbar or right-click it and Bookmark This "
"Link"
"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et ยซ "
"Ajouter aux favoris ยป"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
msgid "then click โShaare link button in any page you want to share"
msgstr "puis cliquer sur โShaare depuis un site que vous souhaitez partager"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:92
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:114
msgid "The selected text is too long, it will be truncated."
msgstr "Le texte sรฉlectionnรฉ est trop long, il sera tronquรฉ."
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
msgid "Shaare link"
msgstr "Shaare"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:107
msgid ""
"Then click โAdd Note button anytime to start composing a private Note (text "
"post) to your Shaarli"
msgstr ""
"Puis cliquer sur โAdd Note pour commencer ร rรฉdiger une Note sur Shaarli"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123
msgid "Add Note"
msgstr "Ajouter une Note"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:132
msgid "3rd party"
msgstr "Applications tierces"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:144
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:140
msgid "plugin"
msgstr "extension"
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:165
msgid ""
"Drag this link to your bookmarks toolbar, or right-click it and choose "
"Bookmark This Link"
"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et ยซ "
"Ajouter aux favoris ยป"
-#~ msgid "Provided data is invalid"
-#~ msgstr "Les informations fournies ne sont pas valides"
-
#~ msgid "Rename"
#~ msgstr "Renommer"
$this->get('/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:index');
$this->post('/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:save');
$this->get('/token', '\Shaarli\Front\Controller\Admin\TokenController:getToken');
+ $this->get('/server', '\Shaarli\Front\Controller\Admin\ServerController:index');
+ $this->get('/clear-cache', '\Shaarli\Front\Controller\Admin\ServerController:clearCache');
$this->get('/thumbnails', '\Shaarli\Front\Controller\Admin\ThumbnailsController:index');
$this->get('/metadata', '\Shaarli\Front\Controller\Admin\MetadataController:ajaxRetrieveTitle');
$this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility');
);
}
+ /**
+ * Checks resource permissions in minimal mode.
+ */
+ public function testCheckCurrentResourcePermissionsErrorsMinimalMode(): void
+ {
+ $conf = new ConfigManager('');
+ $conf->set('resource.thumbnails_cache', 'null/cache');
+ $conf->set('resource.config', 'null/data/config.php');
+ $conf->set('resource.data_dir', 'null/data');
+ $conf->set('resource.datastore', 'null/data/store.php');
+ $conf->set('resource.ban_file', 'null/data/ipbans.php');
+ $conf->set('resource.log', 'null/data/log.txt');
+ $conf->set('resource.page_cache', 'null/pagecache');
+ $conf->set('resource.raintpl_tmp', 'null/tmp');
+ $conf->set('resource.raintpl_tpl', 'null/tpl');
+ $conf->set('resource.raintpl_theme', 'null/tpl/default');
+ $conf->set('resource.update_check', 'null/data/lastupdatecheck.txt');
+
+ static::assertSame(
+ [
+ '"null/tpl" directory is not readable',
+ '"null/tpl/default" directory is not readable',
+ '"null/tmp" directory is not readable',
+ '"null/tmp" directory is not writable'
+ ],
+ ApplicationUtils::checkResourcePermissions($conf, true)
+ );
+ }
+
/**
* Check update with 'dev' as curent version (master branch).
* It should always return false.
ApplicationUtils::checkUpdate('dev', self::$testUpdateFile, 100, true, true)
);
}
+
+ /**
+ * Basic test of getPhpExtensionsRequirement()
+ */
+ public function testGetPhpExtensionsRequirementSimple(): void
+ {
+ static::assertCount(8, ApplicationUtils::getPhpExtensionsRequirement());
+ static::assertSame([
+ 'name' => 'json',
+ 'required' => true,
+ 'desc' => 'Configuration parsing',
+ 'loaded' => true,
+ ], ApplicationUtils::getPhpExtensionsRequirement()[0]);
+ }
+
+ /**
+ * Test getPhpEol with a known version: 7.4 -> 2022
+ */
+ public function testGetKnownPhpEol(): void
+ {
+ static::assertSame('2022-11-28', ApplicationUtils::getPhpEol('7.4.7'));
+ }
+
+ /**
+ * Test getPhpEol with an unknown version: 7.4 -> 2022
+ */
+ public function testGetUnknownPhpEol(): void
+ {
+ static::assertSame(
+ (((int) (new \DateTime())->format('Y')) + 2) . (new \DateTime())->format('-m-d'),
+ ApplicationUtils::getPhpEol('7.51.34')
+ );
+ }
}
namespace Shaarli;
use Exception;
+use Shaarli\Exceptions\IOException;
/**
* Class FileUtilsTest
*
* Test file utility class.
*/
-class FileUtilsTest extends \Shaarli\TestCase
+class FileUtilsTest extends TestCase
{
/**
* @var string Test file path.
*/
protected static $file = 'sandbox/flat.db';
+ protected function setUp(): void
+ {
+ @mkdir('sandbox');
+ mkdir('sandbox/folder2');
+ touch('sandbox/file1');
+ touch('sandbox/file2');
+ mkdir('sandbox/folder1');
+ touch('sandbox/folder1/file1');
+ touch('sandbox/folder1/file2');
+ mkdir('sandbox/folder3');
+ mkdir('/tmp/shaarli-to-delete');
+ }
+
/**
* Delete test file after every test.
*/
protected function tearDown(): void
{
@unlink(self::$file);
+
+ @unlink('sandbox/folder1/file1');
+ @unlink('sandbox/folder1/file2');
+ @rmdir('sandbox/folder1');
+ @unlink('sandbox/file1');
+ @unlink('sandbox/file2');
+ @rmdir('sandbox/folder2');
+ @rmdir('sandbox/folder3');
+ @rmdir('/tmp/shaarli-to-delete');
}
/**
$this->assertEquals(null, FileUtils::readFlatDB(self::$file));
$this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
}
+
+ /**
+ * Test clearFolder with self delete and excluded files
+ */
+ public function testClearFolderSelfDeleteWithExclusion(): void
+ {
+ FileUtils::clearFolder('sandbox', true, ['file2']);
+
+ static::assertFileExists('sandbox/folder1/file2');
+ static::assertFileExists('sandbox/folder1');
+ static::assertFileExists('sandbox/file2');
+ static::assertFileExists('sandbox');
+
+ static::assertFileNotExists('sandbox/folder1/file1');
+ static::assertFileNotExists('sandbox/file1');
+ static::assertFileNotExists('sandbox/folder3');
+ }
+
+ /**
+ * Test clearFolder with self delete and excluded files
+ */
+ public function testClearFolderSelfDeleteWithoutExclusion(): void
+ {
+ FileUtils::clearFolder('sandbox', true);
+
+ static::assertFileNotExists('sandbox');
+ }
+
+ /**
+ * Test clearFolder with self delete and excluded files
+ */
+ public function testClearFolderNoSelfDeleteWithoutExclusion(): void
+ {
+ FileUtils::clearFolder('sandbox', false);
+
+ static::assertFileExists('sandbox');
+
+ // 2 because '.' and '..'
+ static::assertCount(2, new \DirectoryIterator('sandbox'));
+ }
+
+ /**
+ * Test clearFolder on a file instead of a folder
+ */
+ public function testClearFolderOnANonDirectory(): void
+ {
+ $this->expectException(IOException::class);
+ $this->expectExceptionMessage('Provided path is not a directory.');
+
+ FileUtils::clearFolder('sandbox/file1', false);
+ }
+
+ /**
+ * Test clearFolder on a file instead of a folder
+ */
+ public function testClearFolderOutsideOfShaarliDirectory(): void
+ {
+ $this->expectException(IOException::class);
+ $this->expectExceptionMessage('Trying to delete a folder outside of Shaarli path.');
+
+
+ FileUtils::clearFolder('/tmp/shaarli-to-delete', true);
+ }
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Admin;
+
+use Shaarli\Config\ConfigManager;
+use Shaarli\Security\SessionManager;
+use Shaarli\TestCase;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+/**
+ * Test Server administration controller.
+ */
+class ServerControllerTest extends TestCase
+{
+ use FrontAdminControllerMockHelper;
+
+ /** @var ServerController */
+ protected $controller;
+
+ public function setUp(): void
+ {
+ $this->createContainer();
+
+ $this->controller = new ServerController($this->container);
+
+ // initialize dummy cache
+ @mkdir('sandbox/');
+ foreach (['pagecache', 'tmp', 'cache'] as $folder) {
+ @mkdir('sandbox/' . $folder);
+ @touch('sandbox/' . $folder . '/.htaccess');
+ @touch('sandbox/' . $folder . '/1');
+ @touch('sandbox/' . $folder . '/2');
+ }
+ }
+
+ public function tearDown(): void
+ {
+ foreach (['pagecache', 'tmp', 'cache'] as $folder) {
+ @unlink('sandbox/' . $folder . '/.htaccess');
+ @unlink('sandbox/' . $folder . '/1');
+ @unlink('sandbox/' . $folder . '/2');
+ @rmdir('sandbox/' . $folder);
+ }
+ }
+
+ /**
+ * Test default display of server administration page.
+ */
+ public function testIndex(): void
+ {
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ // Save RainTPL assigned variables
+ $assignedVariables = [];
+ $this->assignTemplateVars($assignedVariables);
+
+ $result = $this->controller->index($request, $response);
+
+ static::assertSame(200, $result->getStatusCode());
+ static::assertSame('server', (string) $result->getBody());
+
+ static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
+ static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
+ static::assertArrayHasKey('php_eol', $assignedVariables);
+ static::assertArrayHasKey('php_extensions', $assignedVariables);
+ static::assertArrayHasKey('permissions', $assignedVariables);
+ static::assertEmpty($assignedVariables['permissions']);
+
+ static::assertRegExp(
+ '#https://github\.com/shaarli/Shaarli/releases/tag/v\d+\.\d+\.\d+#',
+ $assignedVariables['release_url']
+ );
+ static::assertRegExp('#v\d+\.\d+\.\d+#', $assignedVariables['latest_version']);
+ static::assertRegExp('#(v\d+\.\d+\.\d+|dev)#', $assignedVariables['current_version']);
+ static::assertArrayHasKey('index_url', $assignedVariables);
+ static::assertArrayHasKey('client_ip', $assignedVariables);
+ static::assertArrayHasKey('trusted_proxies', $assignedVariables);
+
+ static::assertSame('Server administration - Shaarli', $assignedVariables['pagetitle']);
+ }
+
+ /**
+ * Test clearing the main cache
+ */
+ public function testClearMainCache(): void
+ {
+ $this->container->conf = $this->createMock(ConfigManager::class);
+ $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
+ if ($key === 'resource.page_cache') {
+ return 'sandbox/pagecache';
+ } elseif ($key === 'resource.raintpl_tmp') {
+ return 'sandbox/tmp';
+ } elseif ($key === 'resource.thumbnails_cache') {
+ return 'sandbox/cache';
+ } else {
+ return $default;
+ }
+ });
+
+ $this->container->sessionManager
+ ->expects(static::once())
+ ->method('setSessionParameter')
+ ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['Shaarli\'s cache folder has been cleared!'])
+ ;
+
+ $request = $this->createMock(Request::class);
+ $request->method('getQueryParam')->with('type')->willReturn('main');
+ $response = new Response();
+
+ $result = $this->controller->clearCache($request, $response);
+
+ static::assertSame(302, $result->getStatusCode());
+ static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
+
+ static::assertFileNotExists('sandbox/pagecache/1');
+ static::assertFileNotExists('sandbox/pagecache/2');
+ static::assertFileNotExists('sandbox/tmp/1');
+ static::assertFileNotExists('sandbox/tmp/2');
+
+ static::assertFileExists('sandbox/pagecache/.htaccess');
+ static::assertFileExists('sandbox/tmp/.htaccess');
+ static::assertFileExists('sandbox/cache');
+ static::assertFileExists('sandbox/cache/.htaccess');
+ static::assertFileExists('sandbox/cache/1');
+ static::assertFileExists('sandbox/cache/2');
+ }
+
+ /**
+ * Test clearing thumbnails cache
+ */
+ public function testClearThumbnailsCache(): void
+ {
+ $this->container->conf = $this->createMock(ConfigManager::class);
+ $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
+ if ($key === 'resource.page_cache') {
+ return 'sandbox/pagecache';
+ } elseif ($key === 'resource.raintpl_tmp') {
+ return 'sandbox/tmp';
+ } elseif ($key === 'resource.thumbnails_cache') {
+ return 'sandbox/cache';
+ } else {
+ return $default;
+ }
+ });
+
+ $this->container->sessionManager
+ ->expects(static::once())
+ ->method('setSessionParameter')
+ ->willReturnCallback(function (string $key, array $value): SessionManager {
+ static::assertSame(SessionManager::KEY_WARNING_MESSAGES, $key);
+ static::assertCount(1, $value);
+ static::assertStringStartsWith('Thumbnails cache has been cleared.', $value[0]);
+
+ return $this->container->sessionManager;
+ });
+ ;
+
+ $request = $this->createMock(Request::class);
+ $request->method('getQueryParam')->with('type')->willReturn('thumbnails');
+ $response = new Response();
+
+ $result = $this->controller->clearCache($request, $response);
+
+ static::assertSame(302, $result->getStatusCode());
+ static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
+
+ static::assertFileNotExists('sandbox/cache/1');
+ static::assertFileNotExists('sandbox/cache/2');
+
+ static::assertFileExists('sandbox/cache/.htaccess');
+ static::assertFileExists('sandbox/pagecache');
+ static::assertFileExists('sandbox/pagecache/.htaccess');
+ static::assertFileExists('sandbox/pagecache/1');
+ static::assertFileExists('sandbox/pagecache/2');
+ static::assertFileExists('sandbox/tmp');
+ static::assertFileExists('sandbox/tmp/.htaccess');
+ static::assertFileExists('sandbox/tmp/1');
+ static::assertFileExists('sandbox/tmp/2');
+ }
+}
static::assertIsArray($assignedVariables['languages']);
static::assertSame('Automatic', $assignedVariables['languages']['auto']);
static::assertSame('French', $assignedVariables['languages']['fr']);
+
+ static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
+ static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
+ static::assertArrayHasKey('php_eol', $assignedVariables);
+ static::assertArrayHasKey('php_extensions', $assignedVariables);
+ static::assertArrayHasKey('permissions', $assignedVariables);
+ static::assertEmpty($assignedVariables['permissions']);
+
+ static::assertSame('Install Shaarli', $assignedVariables['pagetitle']);
}
/**
</div>
</div>
</form>
+
+<div class="pure-g">
+ <div class="pure-u-lg-1-6 pure-u-1-24"></div>
+ <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-form-complete">
+ <h2 class="window-title">{'Server requirements'|t}</h2>
+
+ {include="server.requirements"}
+ </div>
+</div>
+
{include="page.footer"}
</body>
</html>
--- /dev/null
+<!DOCTYPE html>
+<html{if="$language !== 'auto'"} lang="{$language}"{/if}>
+<head>
+ {include="includes"}
+</head>
+<body>
+{include="page.header"}
+
+<div class="pure-g">
+ <div class="pure-u-lg-1-4 pure-u-1-24"></div>
+ <div class="pure-u-lg-1-2 pure-u-22-24 page-form server-tables-page">
+ <h2 class="window-title">{'Server administration'|t}</h2>
+
+ <h3 class="window-subtitle">{'General'|t}</h3>
+
+ <div class="pure-g server-row">
+ <div class="pure-u-lg-1-2 pure-u-1 server-label">
+ <p>{'Index URL'|t}</p>
+ </div>
+ <div class="pure-u-lg-1-2 pure-u-1">
+ <p><a href="{$index_url}" title="{$pagetitle}">{$index_url}</a></p>
+ </div>
+ </div>
+ <div class="pure-g server-row">
+ <div class="pure-u-lg-1-2 pure-u-1 server-label">
+ <p>{'Base path'|t}</p>
+ </div>
+ <div class="pure-u-lg-1-2 pure-u-1">
+ <p>{$base_path}</p>
+ </div>
+ </div>
+ <div class="pure-g server-row">
+ <div class="pure-u-lg-1-2 pure-u-1 server-label">
+ <p>{'Client IP'|t}</p>
+ </div>
+ <div class="pure-u-lg-1-2 pure-u-1">
+ <p>{$client_ip}</p>
+ </div>
+ </div>
+ <div class="pure-g server-row">
+ <div class="pure-u-lg-1-2 pure-u-1 server-label">
+ <p>{'Trusted reverse proxies'|t}</p>
+ </div>
+ <div class="pure-u-lg-1-2 pure-u-1">
+ {if="count($trusted_proxies) > 0"}
+ <p>
+ {loop="$trusted_proxies"}
+ {$value}<br>
+ {/loop}
+ </p>
+ {else}
+ <p>{'N/A'|t}</p>
+ {/if}
+ </div>
+ </div>
+
+ {include="server.requirements"}
+
+ <h3 class="window-subtitle">Version</h3>
+
+ <div class="pure-g server-row">
+ <div class="pure-u-lg-1-2 pure-u-1 server-label">
+ <p>Current version</p>
+ </div>
+ <div class="pure-u-lg-1-2 pure-u-1">
+ <p>{$current_version}</p>
+ </div>
+ </div>
+
+ <div class="pure-g server-row">
+ <div class="pure-u-lg-1-2 pure-u-1 server-label">
+ <p>Latest release</p>
+ </div>
+ <div class="pure-u-lg-1-2 pure-u-1">
+ <p>
+ <a href="{$release_url}" title="{'Visit releases page on Github'|t}">
+ {$latest_version}
+ </a>
+ </p>
+ </div>
+ </div>
+
+ <h3 class="window-subtitle">Thumbnails</h3>
+
+ <div class="pure-g server-row">
+ <div class="pure-u-lg-1-2 pure-u-1 server-label">
+ <p>Thumbnails status</p>
+ </div>
+ <div class="pure-u-lg-1-2 pure-u-1">
+ <p>
+ {if="$thumbnails_mode==='all'"}
+ {'All'|t}
+ {elseif="$thumbnails_mode==='common'"}
+ {'Only common media hosts'|t}
+ {else}
+ {'None'|t}
+ {/if}
+ </p>
+ </div>
+ </div>
+
+ {if="$thumbnails_mode!=='none'"}
+ <div class="center tools-item">
+ <a href="{$base_path}/admin/thumbnails" title="{'Synchronize all link thumbnails'|t}">
+ <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span>
+ </a>
+ </div>
+ {/if}
+
+ <h3 class="window-subtitle">Cache</h3>
+
+ <div class="center tools-item">
+ <a href="{$base_path}/admin/clear-cache?type=main">
+ <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Clear main cache</span>
+ </a>
+ </div>
+
+ <div class="center tools-item">
+ <a href="{$base_path}/admin/clear-cache?type=thumbnails">
+ <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Clear thumbnails cache</span>
+ </a>
+ </div>
+ </div>
+</div>
+
+{include="page.footer"}
+
+</body>
+</html>
--- /dev/null
+<div class="server-tables">
+ <h3 class="window-subtitle">{'Permissions'|t}</h3>
+
+ {if="count($permissions) > 0"}
+ <p class="center">
+ <i class="fa fa-close fa-color-red" aria-hidden="true"></i>
+ {'There are permissions that need to be fixed.'|t}
+ </p>
+
+ <p>
+ {loop="$permissions"}
+ <div class="center">{$value}</div>
+ {/loop}
+ </p>
+ {else}
+ <p class="center">
+ <i class="fa fa-check fa-color-green" aria-hidden="true"></i>
+ {'All read/write permissions are properly set.'|t}
+ </p>
+ {/if}
+
+ <h3 class="window-subtitle">PHP</h3>
+
+ <p class="center">
+ <strong>{'Running PHP'|t} {$php_version}</strong>
+ {if="$php_has_reached_eol"}
+ <i class="fa fa-circle fa-color-orange" aria-label="hidden"></i><br>
+ {'End of life: '|t} {$php_eol}
+ {else}
+ <i class="fa fa-circle fa-color-green" aria-label="hidden"></i><br>
+ {/if}
+ </p>
+
+ <table class="center">
+ <thead>
+ <tr>
+ <th>{'Extension'|t}</th>
+ <th>{'Usage'|t}</th>
+ <th>{'Status'|t}</th>
+ <th>{'Loaded'|t}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {loop="$php_extensions"}
+ <tr>
+ <td>{$value.name}</td>
+ <td>{$value.desc}</td>
+ <td>{$value.required ? t('Required') : t('Optional')}</td>
+ <td>
+ {if="$value.loaded"}
+ {$classLoaded="fa-color-green"}
+ {$strLoaded=t('Loaded')}
+ {else}
+ {$strLoaded=t('Not loaded')}
+ {if="$value.required"}
+ {$classLoaded="fa-color-red"}
+ {else}
+ {$classLoaded="fa-color-orange"}
+ {/if}
+ {/if}
+
+ <i class="fa fa-circle {$classLoaded}" aria-label="{$strLoaded}" title="{$strLoaded}"></i>
+ </td>
+ </tr>
+ {/loop}
+ </tbody>
+ </table>
+</div>
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Plugin administration'|t}</span>
</a>
</div>
+ <div class="tools-item">
+ <a href="{$base_path}/admin/server"
+ title="{'Check instance\'s server configuration'|t}">
+ <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Server administration'|t}</span>
+ </a>
+ </div>
{if="!$openshaarli"}
<div class="tools-item">
<a href="{$base_path}/admin/password" title="{'Change your password'|t}">
</a>
</div>
- {if="$thumbnails_enabled"}
- <div class="tools-item">
- <a href="{$base_path}/admin/thumbnails" title="{'Synchronize all link thumbnails'|t}">
- <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span>
- </a>
- </div>
- {/if}
-
{loop="$tools_plugin"}
<div class="tools-item">
{$value}